diff --git a/.github/workflows/check-lua.yml b/.github/workflows/check-lua.yml index 3e6e87a05c6..dcbef82f105 100644 --- a/.github/workflows/check-lua.yml +++ b/.github/workflows/check-lua.yml @@ -5,6 +5,7 @@ on: paths: - rts/Lua/** - rts/Rml/SolLua/**/*.cpp + - rts/Sim/Units/Scripts/**/*.cpp - doc/site/mise.toml push: branches: @@ -12,6 +13,7 @@ on: paths: - rts/Lua/** - rts/Rml/SolLua/**/*.cpp + - rts/Sim/Units/Scripts/**/*.cpp - doc/site/mise.toml env: RECOIL_LUA_LIBRARY_DIR: "rts/Lua/library" @@ -29,6 +31,7 @@ jobs: sparse-checkout: | rts/Lua rts/Rml/SolLua + rts/Sim/Units/Scripts doc/site/mise.toml - uses: jdx/mise-action@v2 diff --git a/.github/workflows/publish-site.yml b/.github/workflows/publish-site.yml index efac668ec89..94a959d9893 100644 --- a/.github/workflows/publish-site.yml +++ b/.github/workflows/publish-site.yml @@ -15,7 +15,7 @@ env: LUA_DOC_EXTRACTOR_VERSION: "3" EMMYLUA_DOC_CLI_VERSION: "0.8.2" JQ_VERSION: "1.8.0" - 7Z_VERSION: "24.09" + SEVENZIP_VERSION: "24.09" RUBY_VERSION: "3.3" MISE_ENV: "ci" jobs: diff --git a/.github/workflows/synctest.yml b/.github/workflows/synctest.yml new file mode 100644 index 00000000000..cee0fe4e3b3 --- /dev/null +++ b/.github/workflows/synctest.yml @@ -0,0 +1,273 @@ +# Runs the Beyond All Reason "synctest" (/luarules synctest) against an engine +# commit on all supported platforms (amd64-linux, arm64-linux, amd64-windows). +# +# This workflow looks up the most recent successful "Build Engine v2" run for +# the target commit and downloads platform-specific artifacts. If no such run +# exists, the workflow fails fast. +# TODO: trigger after every engine build +# +# The cross-platform-check job compares hashes across architectures and is +# intended as a merge-blocking required check. +# +# See test/synctest/README.md for background and how to run locally. +name: Sync test + +on: + workflow_dispatch: + inputs: + commit: + description: 'Engine commit SHA to test. Leave blank to test the head of the ref selected in the picker above.' + type: string + default: '' + +permissions: + # actions:read is required to list workflow runs and download artifacts + # produced by a different workflow run (the Build Engine v2 run for the + # target commit). + actions: read + contents: read + +defaults: + run: + shell: bash -euo pipefail {0} + +env: + MAP: "Jade Empress 1.41" + GAME: "Beyond All Reason test-30770-4e0b241" + HASH_FILE: "synctest_synchash.json" + +jobs: + setup: + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + sha: ${{ steps.resolve.outputs.sha }} + run_id: ${{ steps.engine-run.outputs.run_id }} + platforms: ${{ steps.platforms.outputs.json }} + env: + GH_TOKEN: ${{ github.token }} + steps: + - name: Define target platforms + id: platforms + # Single source of truth for the platform matrix: needed so we can check + # if we have all platforms even if a later setup step fails. + run: | + # jq -c -n validates the JSON and emits it compact, as GITHUB_OUTPUT requires. + json=$(jq -c -n '[ + {"platform": "amd64-linux", "runs-on": "ubuntu-latest", "bin-suffix": ""}, + {"platform": "arm64-linux", "runs-on": "ubuntu-24.04-arm", "bin-suffix": ""}, + {"platform": "amd64-windows", "runs-on": "windows-latest", "bin-suffix": ".exe"} + ]') + echo "json=$json" >> "$GITHUB_OUTPUT" + + - name: Resolve target commit + id: resolve + env: + INPUT_COMMIT: ${{ inputs.commit }} + REPO: ${{ github.repository }} + DEFAULT_SHA: ${{ github.sha }} + run: | + target="${INPUT_COMMIT:-$DEFAULT_SHA}" + + # Normalize whatever the user pasted (full sha, short sha, branch, tag) + # into a full 40-char SHA so the run lookup below is unambiguous. + full_sha=$(gh api "repos/$REPO/commits/$target" --jq '.sha') + echo "Resolved target commit: $full_sha" + echo "sha=$full_sha" >> "$GITHUB_OUTPUT" + + - name: Find Build Engine v2 run for this commit + id: engine-run + env: + REPO: ${{ github.repository }} + TARGET_SHA: ${{ steps.resolve.outputs.sha }} + run: | + run_id=$(gh api \ + "repos/$REPO/actions/workflows/engine-build.yml/runs?head_sha=$TARGET_SHA&status=success" \ + --jq '.workflow_runs[0].id') + if [ -z "$run_id" ] || [ "$run_id" = "null" ]; then + echo "::error::No successful 'Build Engine v2' run found for commit $TARGET_SHA." + echo "::error::Trigger Build Engine v2 on that commit first (push it, or dispatch engine-build.yml manually) and retry." + exit 1 + fi + echo "Using engine-build run $run_id" + echo "run_id=$run_id" >> "$GITHUB_OUTPUT" + + synctest: + needs: setup + runs-on: ${{ matrix.runs-on }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: ${{ fromJSON(needs.setup.outputs.platforms) }} + steps: + - name: Checkout workflow definition and test inputs + # Uses the ref picked in the dispatch UI (github.sha), NOT the target + # commit — so the startscript and any other test assets come from the + # workflow-definition ref. The target commit only controls which engine + # artifact gets downloaded below. + uses: actions/checkout@v4 + + - name: Download engine artifact (${{ matrix.platform }}) + uses: actions/download-artifact@v4 + with: + pattern: engine-artifacts-${{ matrix.platform }}* + run-id: ${{ needs.setup.outputs.run_id }} + github-token: ${{ github.token }} + path: engine-artifact + merge-multiple: true + + - name: Extract install tree + run: | + mkdir -p install + + # Engine is packaged with INSTALL_PORTABLE=ON (CMake default, see + # CMakeLists.txt:22), so binaries live at the install-tree root: + # install/spring-headless, install/pr-downloader, install/spring, ... + # The archive name comes from docker-build-v2/scripts/package.sh; that + # script also emits a -dbgsym.tar.zst, which this glob excludes. + # + # Windows has 7z.exe, and ubuntu currently has 7z/7zz/7za but may later + # drop to just 7zz/7za. + sevenzip=$(command -v 7z || command -v 7zz || command -v 7za || true) + if [ -z "$sevenzip" ]; then + echo "::error::no 7-Zip binary (7z/7zz/7za) found on runner"; exit 1 + fi + "$sevenzip" x engine-artifact/recoil_*_${{ matrix.platform }}*.7z -oinstall + + - name: Restore BAR content cache + uses: actions/cache@v4 + with: + # Content-addressed and immutable, so all three platform legs share one + # entry. packages/ (the .sdp manifests) is required alongside pool/ + # for the engine to mount the game. Deliberately NOT the whole write-dir: + # everything else the engine writes here is build- and platform-specific. + # rapid/ is excluded too — mutable repo metadata, a few MB, refetched so + # the pin is genuinely re-resolved each run. + path: | + bar-data/pool + bar-data/packages + bar-data/maps + # IMPORTANT: + # Bump this key in lockstep with any change to the pinned + # gametype/mapname. See test/synctest/README.md. + key: bar-data-${{ env.GAME }}-${{ env.MAP }} + + - name: Fetch pinned BAR assets (no-op on warm cache) + run: | + ./install/pr-downloader${{ matrix.bin-suffix }} \ + --filesystem-writepath "$PWD/bar-data" \ + --download-game "$GAME" \ + --download-map "$MAP" + env: + PRD_RAPID_USE_STREAMER: "false" + PRD_RAPID_REPO_MASTER: "https://repos-cdn.beyondallreason.dev/repos.gz" + PRD_HTTP_SEARCH_URL: "https://files-cdn.beyondallreason.dev/find" + + - name: Render startscript + run: | + # The checked-in startscript is a template: @VERSION@ is the version + # suffix of the pinned GAME (text after the last space, e.g. + # "test-29932-21b3bb0") and @MAPNAME@ is MAP. + sed -e "s/@VERSION@/${GAME##* }/g" -e "s/@MAPNAME@/$MAP/g" \ + test/synctest/synctest-startscript.txt > startscript.txt + + # Fail loudly here rather than letting the engine fail later with an + # opaque "can't find game/map" error. + if grep -qE '@VERSION@|@MAPNAME@' startscript.txt; then + echo "::error::startscript template was not fully rendered — leftover placeholders:" + grep -nE '@VERSION@|@MAPNAME@' startscript.txt + exit 1 + fi + grep -nE 'gametype=|mapname=' startscript.txt + + - name: Run synctest + run: | + ./install/spring-headless${{ matrix.bin-suffix }} --isolation \ + --write-dir "$PWD/bar-data" startscript.txt 2>&1 | tee infolog.txt + + - name: Assert sync-hash file produced + run: | + digest=$(jq -r '.digest // empty' "bar-data/$HASH_FILE" 2>/dev/null || true) + if [ -z "$digest" ]; then + echo "::error::bar-data/$HASH_FILE is missing, empty, or has no .digest — the synctest produced no checksum output" + exit 1 + fi + echo "Sync hash digest: $digest" + + - name: Upload run artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: synctest-${{ matrix.platform }}-${{ needs.setup.outputs.sha }} + path: | + infolog.txt + bar-data/*.log + bar-data/${{ env.HASH_FILE }} + retention-days: 30 + if-no-files-found: ignore + + # Collects sync hashes from all synctest runs across platforms and fails if + # hashes diverge between architectures (cross-platform desync detection). + # This job is intended to be a required/merge-blocking status check. + cross-platform-check: + if: ${{ !cancelled() }} + needs: [setup, synctest] + runs-on: ubuntu-latest + timeout-minutes: 5 + env: + TESTED_SHA: ${{ needs.setup.outputs.sha }} + steps: + - name: Assert setup produced a platform list + env: + PLATFORMS_JSON: ${{ needs.setup.outputs.platforms }} + run: | + if [ -z "$PLATFORMS_JSON" ]; then + echo "::error::setup produced no platform list — it likely failed before it ran; check the setup job." + exit 1 + fi + + - name: Download current run artifacts + # If every platform leg died before uploading, the pattern matches + # nothing and this step errors. That is not the failure we want to + # report, so swallow it and let the MISSING check below speak instead. + continue-on-error: true + uses: actions/download-artifact@v4 + with: + pattern: synctest-*-${{ env.TESTED_SHA }} + path: artifacts + + - name: Compare sync hashes across platforms + env: + PLATFORMS_JSON: ${{ needs.setup.outputs.platforms }} + run: | + # Check from the EXPECTED platform list, so if artifact uploading fails + # we will be able to detect that its missing. + for platform in $(jq -r '.[].platform' <<<"$PLATFORMS_JSON"); do + digest=$(jq -r '.digest // empty' \ + "artifacts/synctest-$platform-$TESTED_SHA/bar-data/$HASH_FILE" 2>/dev/null || true) + echo "$platform ${digest:-MISSING}" + done > synchashes.txt + + echo "Cross-platform determinism check:" + cat synchashes.txt + + if grep -qw MISSING synchashes.txt; then + echo "::error::sync hash MISSING on at least one platform — see the per-platform jobs" + exit 1 + fi + digests=$(awk '{print $2}' synchashes.txt | sort -u) + if [ "$(wc -l <<<"$digests")" -gt 1 ]; then + echo "::error::CROSS-PLATFORM DESYNC — hashes differ across architectures; the engine is not deterministic across platforms" + exit 1 + fi + echo "OK across all platforms ($digests)" + + # A platform can write the hash and then fail, but + # `!cancelled()` means we still run the comparison. + # So let's do a final check to make sure they all passed. + - name: Assert all synctest jobs succeeded + if: ${{ needs.synctest.result != 'success' }} + run: | + echo "::error::synctest matrix result is '${{ needs.synctest.result }}' — a platform leg did not succeed; see the per-platform jobs" + exit 1 diff --git a/.gitignore b/.gitignore index 0a5ec2bb14b..833220ecc57 100644 --- a/.gitignore +++ b/.gitignore @@ -174,3 +174,5 @@ build-* # IDE /.idea .kilo + +/.i-understand-git-submodules.txt diff --git a/AGENTS.md b/AGENTS.md index 0f87ef9915c..4d346e24500 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,18 +8,49 @@ RecoilEngine is an open-source real-time strategy (RTS) game engine written in C ## Build Commands +### Submodules + +The repo uses git submodules for vendored libraries (`rts/lib/*`, `tools/pr-downloader`, AI skirmish bots, etc.). If you cloned without `--recurse-submodules`, initialize them before building: +```bash +git submodule update --init --recursive +``` + ### Building the Engine **Using Docker (Recommended):** ```bash -# Build for Linux +# Full build (default: RELWITHDEBINFO, -O3 -g -DNDEBUG, Ninja) +# Output lands in build--/ (e.g. build-amd64-linux/) and the +# ready-to-use install in build-amd64-linux/install/ docker-build-v2/build.sh linux -# Build for Windows +# Parallelism +docker-build-v2/build.sh -j 8 linux + +# Windows cross-build docker-build-v2/build.sh windows -# Build with custom CMake options +# Change optimization level — trailing -D… is forwarded to configure.sh and +# overrides the baked-in RELWITHDEBINFO default. docker-build-v2/build.sh linux -DCMAKE_BUILD_TYPE=DEBUG +docker-build-v2/build.sh linux -DCMAKE_BUILD_TYPE=RELEASE +docker-build-v2/build.sh linux -DCMAKE_BUILD_TYPE=PROFILE + +# Combine cmake options (configure phase) +docker-build-v2/build.sh linux -DBUILD_spring-headless=OFF -DTRACY_ENABLE=ON + +# List all available cmake options and their current values +docker-build-v2/build.sh --configure linux -LH + +# Build a specific target — use --compile so args flow to `cmake --build`, +# not to configure. Without --compile, `-t …` would be rejected by configure. +docker-build-v2/build.sh --compile linux -t engine-headless +docker-build-v2/build.sh --compile linux -t engine-legacy +docker-build-v2/build.sh --compile linux -t tests --verbose + +# Split the phases +docker-build-v2/build.sh --configure linux # configure only +docker-build-v2/build.sh --compile linux # compile only (reuses existing config) ``` **Without Docker:** @@ -27,16 +58,44 @@ docker-build-v2/build.sh linux -DCMAKE_BUILD_TYPE=DEBUG # Create build directory mkdir -p build && cd build -# Configure +# Configure — project requires C++23 (clang ≥ 17 or gcc ≥ 13 on PATH). +# CMAKE_BUILD_TYPE defaults to RELWITHDEBINFO when omitted. cmake .. -# Build specific target -cmake --build . --target engine-headless -j$(nproc) - -# Build all -cmake --build . -j$(nproc) +# Optional: Default generator is Unix Makefiles; add `-G Ninja` for faster builds if ninja is installed. +cmake -G Ninja .. + +# Optional: pin to gcc-13 + gold linker via the in-repo toolchain file +# (tracked under docker-build-v2/; same compiler the docker build uses). +cmake \ + -DCMAKE_TOOLCHAIN_FILE=../docker-build-v2/images/all-linux/toolchain.cmake .. + +# Optional: speed up incremental builds with ccache +cmake \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache .. + +# Change optimization level by re-running cmake (no wipe required): +cmake -DCMAKE_BUILD_TYPE=DEBUG .. # no optimization, full symbols +cmake -DCMAKE_BUILD_TYPE=RELEASE .. # optimized, no debug info +cmake -DCMAKE_BUILD_TYPE=RELWITHDEBINFO .. # optimized + debug info (default) +cmake -DCMAKE_BUILD_TYPE=PROFILE .. # optimized + profiling hooks + +# Build (generator-agnostic — works under Ninja or Make) +cmake --build . + +# Build a specific target +cmake --build . --target engine-headless +cmake --build . --target engine-legacy +cmake --build . --target engine-dedicated +cmake --build . --target tests ``` +> The docker flow writes to **`build--/`** (e.g. `build-amd64-linux/` +> or `build-amd64-windows/`), which is a different directory than the `build/` +> used by this flow. When running tests, point commands at whichever build +> directory you populated. + ### Build Types - `DEBUG` - Debug build with full symbols and no optimization - `RELEASE` - Optimized release build @@ -44,55 +103,64 @@ cmake --build . -j$(nproc) - `PROFILE` - Profiling build ### Build Targets -- `engine-legacy` - Main engine build -- `engine-headless` - Headless server build -- `engine-dedicated` - Dedicated server build -- `tests` - Build all test executables -- `check` - Build and run all tests -- `spring-content` - Build game content packages +- `engine-legacy` — main interactive engine build +- `engine-headless` — headless engine (no graphics) +- `engine-dedicated` — dedicated server +- `unitsync` — unitsync shared library +- `pr-downloader` — content downloader tool +- `tests` — phony; builds every `test_*` executable under `build/test/` +- `check` — phony; depends on `engine-headless` + all `test_*` executables, then runs ctest with `--output-on-failure -V` +- `install` — install into `CMAKE_INSTALL_PREFIX` ## Testing -### Test Framework -The project uses **Catch2** for unit testing. Test files are located in the `test/` directory. +### Writing Tests +See `test/AGENTS.md` for details on writing tests, available compile flags, patterns, and test helpers. ### Running Tests **Build and run all tests:** ```bash -# From build directory -make tests # Build all test executables -make check # Build and run all tests via CTest -make test # Alternative: run via CTest +# From build/ — ctest / check recipes below assume a non-docker build. +# For a docker build, run `docker-build-v2/build.sh --compile linux -t check` +# (runs ctest inside the container) or invoke the binaries in +# build-amd64-linux/test/ directly. +cmake --build . --target tests # build all test executables (no run) +ctest # run all tests (does not rebuild) +# OR +cmake --build . --target check # rebuild engine-headless + all tests, then run ctest -V ``` -**Run a single test:** +`check` is the safe default when iterating; bare `ctest` is faster when nothing relevant has changed since the last build. + +**Run a single test (from repo root):** ```bash -# Tests are built as executable binaries in the build directory -# Pattern: test_ +# Tests are built as executable binaries under /test/ +# Pattern: /test/test_ +# where depends on if you built in docker or not (see above). -# Run specific test executable -./test_Float3 -./test_Matrix44f -./test_SyncedPrimitive -./test_UDPListener +./build/test/test_Float3 +./build/test/test_Matrix44f +./build/test/test_SyncedPrimitive +./build/test/test_UDPListener -# Run with verbose output -./test_Float3 -s +# Catch2: show passing assertions too +./build/test/test_Float3 -s -# Run specific test case -./test_Float3 "TestSection" +# Run a specific test case by name (positional arg matches TEST_CASE name, supports wildcards) +./build/test/test_Float3 "Float3" +./build/test/test_Float3 "Float34_*" ``` -**Run via CTest:** +**Run via CTest (from inside build/):** ```bash -# Run specific test by name -ctest -R Float3 -V +# Filter by regex, show output only on failure +ctest -R Float3 --output-on-failure -# Run with regex pattern -ctest -R Matrix -V +# Same, but verbose (full stdout regardless of result) +ctest -R Float3 -V -# List all available tests +# List all registered tests without running ctest -N ``` @@ -325,20 +393,6 @@ Use preprocessor directives for platform-specific code: - Use tabs for indentation in CMake files - Keep lines reasonably short -### Adding Tests -In `test/CMakeLists.txt`: -```cmake -set(test_name TestName) -set(test_src - "${CMAKE_CURRENT_SOURCE_DIR}/path/to/TestFile.cpp" - ${test_Common_sources} -) -set(test_libs - library_name -) -add_spring_test(${test_name} "${test_src}" "${test_libs}" "${test_flags}") -``` - ## Project Structure - `rts/` - Main engine source code @@ -388,6 +442,10 @@ The engine uses custom thread pools. See `THREADPOOL` define and related code. 4. Follow the workflow in `contributing.md` 5. Disclose any AI assistance used +### Additional docs +Please see @coding-agents/ for additional documentation: +- coding-agents/ENGINE_PERFORMANCE.md — notes on scale targets and engine performance internals. Useful for performance related changes. +- coding-agents/BACKWARDS_COMPATIBILITY.md - notes on when we should strive to be backwards compatible. Reference it for any major reworks or api changes. ## Additional Resources - Official website: https://recoilengine.org diff --git a/AI/Interfaces/Java/CMakeLists.txt b/AI/Interfaces/Java/CMakeLists.txt deleted file mode 100644 index 9220868084e..00000000000 --- a/AI/Interfaces/Java/CMakeLists.txt +++ /dev/null @@ -1,661 +0,0 @@ -### Java AI Interface -# -# Global variables set in this file: -# * BUILD_Java_AIINTERFACE -# * Java_AIINTERFACE_VERS -# * Java_AIINTERFACE_TARGET -# -# Functions and macros defined in this file: -# * configure_java_skirmish_ai -# - -#enable_language(Java) - - -################################################################################ -### BEGIN: MACROS_AND_FUNCTIONS -# Define macros and functions to be used in this file and by Java Skirmish AIs - -# includes rts/build/cmake/UtilJava.cmake -include(UtilJava) - -# Java Skirmish AI configuration macro. -# This will be called from Java AIs at AI/Skirmish/*/CMakeLists.txt. -macro (configure_java_skirmish_ai wrapperNames) - # Assemble meta data - set(myDir "${CMAKE_CURRENT_SOURCE_DIR}") - get_last_path_part(dirName ${myDir}) - set(myName "${dirName}") - set(myJarFile "SkirmishAI") - set(myBinJarFile "${myJarFile}.jar") - set(mySrcJarFile "${myJarFile}-src.jar") - set(myJLibDir "${myDir}/data/jlib") - get_version_plus_dep_file(myVersion myVersionDepFile) - set(myTarget "${myName}") - set(myInstLibsDir "${SKIRMISH_AI_LIBS}/${myName}/${myVersion}") - set(myInstDataDir "${SKIRMISH_AI_DATA}/${myName}/${myVersion}") - # CMAKE_CURRENT_BINARY_DIR: .../spring-build-dir/AI/Skirmish/${myName} - set(myBuildDir "${CMAKE_CURRENT_BINARY_DIR}") - - # Check if the user wants to compile the AI - set(BUILD_THIS_SKIRMISHAI FALSE) - if (BUILD_Java_AIINTERFACE AND NOT myName MATCHES "${AI_EXCLUDE_REGEX}") - set(BUILD_THIS_SKIRMISHAI TRUE) - endif (BUILD_Java_AIINTERFACE AND NOT myName MATCHES "${AI_EXCLUDE_REGEX}") - if (NOT BUILD_THIS_SKIRMISHAI) - message("warning: ${myName} Skirmish AI will not be built!") - endif (NOT BUILD_THIS_SKIRMISHAI) - - # Compile and install - if (BUILD_THIS_SKIRMISHAI) - file(MAKE_DIRECTORY "${myBuildDir}/jlib") - - set(configString "default") - skirmish_ai_message(STATUS "Found Skirmish AI: ${myName} ${myVersion} (config: ${configString})") - - # Assemble project generated targets (and their libraries) we depend on - set(myDependTargets "${Java_AIINTERFACE_TARGET}") - set(myDependLibFiles "${Java_AIINTERFACE_JAR_BIN}") - foreach (wrapperName ${wrapperNames}) - set(myDependTargets ${myDependTargets} "${${wrapperName}_AIWRAPPER_TARGET}") - set(myDependLibFiles ${myDependLibFiles} "${${wrapperName}_AIWRAPPER_JAR_BIN}") - endforeach (wrapperName) - set_source_files_properties(${myDependLibFiles} PROPERTIES GENERATED TRUE) - - # find java source root - if (EXISTS "${myDir}/src/main/java") - # default Maven source dir - set(mySourceDir "${myDir}/src/main/java") - elseif (EXISTS "${myDir}/src") - # simple java source dir path - set(mySourceDir "${myDir}/src") - else (EXISTS "${myDir}/src/main/java") - message(SEND_ERROR "No sources dir found for Skirmish AI: ${myName}") - endif (EXISTS "${myDir}/src/main/java") - - # Create a list of all the AIs source files (for compiling) - file(GLOB_RECURSE mySources RELATIVE "${mySourceDir}" FOLLOW_SYMLINKS "${mySourceDir}/*.java") - # Create a list of all the AIs source files (for dependency tracking) - file(GLOB_RECURSE mySourcesDep FOLLOW_SYMLINKS "${mySourceDir}/*.java") - - set_source_files_properties("${myBuildDir}/${myBinJarFile}" PROPERTIES GENERATED TRUE) - set_source_files_properties("${myBuildDir}/${mySrcJarFile}" PROPERTIES GENERATED TRUE) - - - # If main Java package is "my.ai.pkg", this has to be set to "my". - get_first_sub_dir_name(firstSrcSubDir ${mySourceDir}) - set(myJavaPkgFirstPart "${firstSrcSubDir}") - - # Assemble additional meta data - set(myBinaryTarget "${myTarget}-BIN") - set(mySourceTarget "${myTarget}-SRC") - set(myJavaBuildDir "${myBuildDir}/classes") - - add_custom_target(${myBinaryTarget} - DEPENDS "${myBuildDir}/${myBinJarFile}") - add_dependencies(${myBinaryTarget} ${myDependTargets}) - - add_custom_target(${mySourceTarget} - DEPENDS "${myBuildDir}/${mySrcJarFile}") - - add_custom_target(${myTarget} ALL DEPENDS ${myVersionDepFile}) - add_dependencies(${myTarget} ${myBinaryTarget} ${mySourceTarget}) - - # Create our full Java class-path - create_classpath(myJavaLibs ${myJLibDir}) - concat_classpaths(myClassPath "${CLASSPATH_Java_AIINTERFACE}" "${myJavaLibs}") - foreach (wrapperName ${wrapperNames}) - concat_classpaths(myClassPath "${myClassPath}" "${${wrapperName}_AIWRAPPER_JAR_CLASSPATH}") - endforeach (wrapperName) - - # Locate the manifest file - find_manifest_file("${myDir}" myManifestFile) - if (myManifestFile) - set(myBinJarArgs "cmf" "${myManifestFile}") - else (myManifestFile) - set(myBinJarArgs "cf") - endif (myManifestFile) - - # Write list of source files to an arg-file - set(mySrcArgFile "${myBuildDir}/sourceFiles.txt") - if (EXISTS "${mySrcArgFile}") - file(REMOVE "${mySrcArgFile}") - endif (EXISTS "${mySrcArgFile}") - set(mySrcArgFile "${myBuildDir}/sourceFiles.txt") - foreach (srcFile ${mySources}) - file(APPEND "${mySrcArgFile}" "\"${srcFile}\"\n") - endforeach (srcFile) - - # Compile and pack the library - add_custom_command( - OUTPUT - "${myBuildDir}/${myBinJarFile}" - DEPENDS - ${myDependLibFiles} - ${mySourcesDep} - COMMAND "${CMAKE_COMMAND}" - "-E" "remove_directory" "${myJavaBuildDir}" - COMMAND "${CMAKE_COMMAND}" - "-E" "make_directory" "${myJavaBuildDir}" - COMMAND "${Java_JAVAC_EXECUTABLE}" - "${JAVA_COMPILE_FLAG_CONDITIONAL}" - "-Xlint:deprecation" - "-cp" "${myClassPath}" - "-d" "${myJavaBuildDir}" - "@${mySrcArgFile}" - COMMAND "${Java_JAR_EXECUTABLE}" - ${myBinJarArgs} "${myBuildDir}/${myBinJarFile}" - "-C" "${myJavaBuildDir}" "${myJavaPkgFirstPart}" - WORKING_DIRECTORY - "${mySourceDir}" - COMMENT - " ${myTarget}: Compiling sources and packing library ${myBinJarFile}" VERBATIM - ) - - # Pack the sources - add_custom_command( - OUTPUT - "${myBuildDir}/${mySrcJarFile}" - COMMAND "${Java_JAR_EXECUTABLE}" - "cf" "${myBuildDir}/${mySrcJarFile}" - "-C" "${mySourceDir}" "${myJavaPkgFirstPart}" - DEPENDS - ${mySourcesDeps} - WORKING_DIRECTORY - "${mySourceDir}" - COMMENT - " ${myTarget}: Creating sources archive ${mySrcJarFile}" VERBATIM - ) - - # Install the data files - install(DIRECTORY "${myDir}/data/" DESTINATION ${myInstDataDir}) - # Install the library - install(FILES "${myBuildDir}/${myBinJarFile}" DESTINATION ${myInstDataDir}) - # Install the sources archive (optional) - install(FILES "${myBuildDir}/${mySrcJarFile}" DESTINATION ${myInstDataDir}/jlib OPTIONAL) - - # Install files generated/downloaded during buildtime - install(DIRECTORY "${myBuildDir}/jlib/" DESTINATION ${myInstDataDir}/jlib) - if (EXISTS "${myBuildDir}/resources") - install(DIRECTORY "${myBuildDir}/resources/" DESTINATION ${myInstDataDir}/resources) - endif (EXISTS "${myBuildDir}/resources") - if (EXISTS "${myBuildDir}/config") - install(DIRECTORY "${myBuildDir}/config/" DESTINATION ${myInstDataDir}/config) - endif (EXISTS "${myBuildDir}/config") - if (EXISTS "${myBuildDir}/script") - install(DIRECTORY "${myBuildDir}/script/" DESTINATION ${myInstDataDir}/script) - endif (EXISTS "${myBuildDir}/script") - - # Install special script and config files - if (EXISTS "${myDir}/src/main/groovy") - install(DIRECTORY "${myDir}/src/main/groovy/" DESTINATION ${myInstDataDir}/script) - endif (EXISTS "${myDir}/src/main/groovy") - if (EXISTS "${myDir}/src/main/ruby") - install(DIRECTORY "${myDir}/src/main/ruby/" DESTINATION ${myInstDataDir}/script) - endif (EXISTS "${myDir}/src/main/ruby") - if (EXISTS "${myDir}/src/main/bsh") - install(DIRECTORY "${myDir}/src/main/bsh/" DESTINATION ${myInstDataDir}/script) - endif (EXISTS "${myDir}/src/main/bsh") - if (EXISTS "${myDir}/src/main/config") - install(DIRECTORY "${myDir}/src/main/config/" DESTINATION ${myInstDataDir}/config) - endif (EXISTS "${myDir}/src/main/config") - - # Install jars of wrappers - foreach (wrapperName ${wrapperNames}) - # Install the wrappers main jar - install(FILES "${${wrapperName}_AIWRAPPER_JAR_BIN}" DESTINATION ${myInstDataDir}/jlib) - set(wrapperJLibDir "${CMAKE_SOURCE_DIR}/AI/Wrappers/${wrapperName}/jlib") - # Install the wrappers java libs, if there are any - if (EXISTS ${wrapperJLibDir}) - install(DIRECTORY "${wrapperJLibDir}/" DESTINATION ${myInstDataDir}/jlib) - endif (EXISTS ${wrapperJLibDir}) - endforeach (wrapperName) - endif (BUILD_THIS_SKIRMISHAI) -endmacro (configure_java_skirmish_ai wrapperNames) - - -### END: MACROS_AND_FUNCTIONS -################################################################################ - - -set(myName "Java") -set(myDir "${CMAKE_CURRENT_SOURCE_DIR}") -set(mySourceDirRel "src/main") -set(myJavaSourceDirRel "src/main/java") -set(myNativeSourceDirRel "src/main/native") -set(myPkgFirstPart "com") -set(myPkg "${myPkgFirstPart}/springrts/ai") - - -# Check if the user wants to compile the interface -if ("${AI_TYPES}" STREQUAL "ALL" OR "${AI_TYPES}" STREQUAL "JAVA") - set(AI_TYPES_JAVA TRUE) -else ("${AI_TYPES}" STREQUAL "ALL" OR "${AI_TYPES}" STREQUAL "JAVA") - set(AI_TYPES_JAVA FALSE) -endif ("${AI_TYPES}" STREQUAL "ALL" OR "${AI_TYPES}" STREQUAL "JAVA") - -if (AI_TYPES_JAVA AND myName MATCHES "${AI_EXCLUDE_REGEX}") - set(AI_TYPES_JAVA FALSE) -endif (AI_TYPES_JAVA AND myName MATCHES "${AI_EXCLUDE_REGEX}") - - -# Look for dependencies, but only if the user wants to build the interface -if (AI_TYPES_JAVA) - if (NOT JAVA_FOUND) - find_package(Java REQUIRED COMPONENTS Development) - endif (NOT JAVA_FOUND) - if (MINGW) - set (JNI_FOUND TRUE) - else (MINGW) - # this hack is needed for FindJNI.cmake to use the JDK we want it to use, - # as otherwise it might not find one at all (eg. in the case of OpenJDK) - if ( NOT ENV{JAVA_HOME} AND JAVA_HOME ) - set(ENV{JAVA_HOME} "${JAVA_HOME}") - endif ( NOT ENV{JAVA_HOME} AND JAVA_HOME ) - find_package(JNI REQUIRED) - if (JAVA_INCLUDE_PATH) - set (JNI_FOUND TRUE) - include_directories(${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2} ${JNI_INCLUDE_DIRS}) - else (JAVA_INCLUDE_PATH) - set (JNI_FOUND FALSE) - message(WARNING "No Java includes found!") - endif (JAVA_INCLUDE_PATH) - endif (MINGW) -endif (AI_TYPES_JAVA) - - -# Check if dependencies of the interface are met -if (AI_TYPES_JAVA AND JNI_FOUND AND JAVA_FOUND AND EXISTS ${myDir} AND EXISTS ${myDir}/bin AND AWK_FOUND AND NOT myName MATCHES "${AI_EXCLUDE_REGEX}") - set_global(BUILD_${myName}_AIINTERFACE TRUE) -else (AI_TYPES_JAVA AND JNI_FOUND AND JAVA_FOUND AND EXISTS ${myDir} AND EXISTS ${myDir}/bin AND AWK_FOUND AND NOT myName MATCHES "${AI_EXCLUDE_REGEX}") - set_global(BUILD_${myName}_AIINTERFACE FALSE) - message("warning: Java AI Interface will not be built!") -endif (AI_TYPES_JAVA AND JNI_FOUND AND JAVA_FOUND AND EXISTS ${myDir} AND EXISTS ${myDir}/bin AND AWK_FOUND AND NOT myName MATCHES "${AI_EXCLUDE_REGEX}") - - -# Build -if (BUILD_${myName}_AIINTERFACE) - get_version_plus_dep_file(myVersion myVersionDepFile) - set(myTarget "${myName}-AIInterface") - set(myGenTarget "${myTarget}-generateSources") - set(myNativeTarget "${myTarget}-native") - set(myJavaTarget "${myTarget}-java") - set(myInstLibsDir ${AI_INTERFACES_LIBS}/${myName}/${myVersion}) - set(myInstDataDir ${AI_INTERFACES_DATA}/${myName}/${myVersion}) - set(myJavaSourceDirRel "${mySourceDirRel}/java") - set(myNativeSourceDirRel "${mySourceDirRel}/native") - make_absolute(mySourceDir "${myDir}" "${mySourceDirRel}") - make_absolute(myNativeSourceDir "${myDir}" "${myNativeSourceDirRel}") - make_absolute(myJavaSourceDir "${myDir}" "${myJavaSourceDirRel}") - - ai_interface_message(STATUS "Found AI Interface: ${myTarget} ${myVersion}") - - set_global(${myName}_AIINTERFACE_VERS ${myVersion}) - set_global(${myName}_AIINTERFACE_TARGET ${myTarget}) - set_global(${myName}_AIINTERFACE_TARGET_GENERATE_SOURCES ${myGenTarget}) - - - # Init some vars - # -------------- - set(myAwkScriptsDir "${myDir}/bin") - set(commonAwkScriptsDir "${CMAKE_SOURCE_DIR}/AI/Wrappers/CUtils/bin") - set(myBuildDir "${CMAKE_CURRENT_BINARY_DIR}") - set(springSourceDir "${PROJECT_SOURCE_DIR}") - set(springAIInterfaceSourceDir "${springSourceDir}/rts/ExternalAI/Interface") - set(myJavaBuildDir "${myBuildDir}/classes") - set(myJarFile "AIInterface") - set(myBinJarFile "${myJarFile}.jar") - set(mySrcJarFile "${myJarFile}-src.jar") - set(myGeneratedSourceDir "${myBuildDir}/src-generated/main") - set(myJavaGeneratedSourceDir "${myGeneratedSourceDir}/java") - set(myNativeGeneratedSourceDir "${myGeneratedSourceDir}/native") - set(myJLibDir "${myDir}/data/jlib") - create_classpath(myJavaLibs ${myJLibDir}) - set(myClassPath ".${PATH_DELIM_H}${myJavaLibs}${PATH_DELIM_H}${myJavaSourceDir}") - - # Used by Java Skirmish AIs - set_global(SOURCE_ROOT_${myName}_AIINTERFACE "${myDir}") - set_global(BUILD_ROOT_${myName}_AIINTERFACE "${myBuildDir}") - set_global(${myName}_AIINTERFACE_JAR_BIN "${myBuildDir}/${myBinJarFile}") - set_global(${myName}_AIINTERFACE_JAR_SRC "${myBuildDir}/${mySrcJarFile}") - set_global(${myName}_AIINTERFACE_POM "${myBuildDir}/pom-generated.xml") - set_global(CLASSPATH_${myName}_AIINTERFACE "${myJavaLibs}${PATH_DELIM_H}${myBuildDir}/${myBinJarFile}") - set_global(JAVA_SRC_DIR_${myName}_AIINTERFACE "${myJavaSourceDir}") - set_global(JAVA_GEN_SRC_DIR_${myName}_AIINTERFACE "${myJavaGeneratedSourceDir}") - - - # Generate sources - # ---------------- - - set(commonAwkScriptArgs - "-v" "SPRING_SOURCE_DIR=${springSourceDir}" - "-v" "INTERFACE_SOURCE_DIR=${myJavaSourceDir}" - "-v" "GENERATED_SOURCE_DIR=${myGeneratedSourceDir}" - "-v" "NATIVE_GENERATED_SOURCE_DIR=${myNativeGeneratedSourceDir}" - "-v" "JAVA_GENERATED_SOURCE_DIR=${myJavaGeneratedSourceDir}" - "-f" "${commonAwkScriptsDir}/common.awk" - "-f" "${commonAwkScriptsDir}/commonDoc.awk" - ) - - # A CMake Custom Target will always be built. - # from CMake docu: - # "add_custom_target is ALWAYS CONSIDERED OUT OF DATE" - - # Stub file for dependency tracking - set(myGeneratedSourceDirStubFile "${CMAKE_CURRENT_BINARY_DIR}/myGeneratedSourceDir.stub") - set_source_files_properties(${myGeneratedSourceDirStubFile} PROPERTIES GENERATED TRUE) - - - # source file lists (static and generated) - - set(myNativeSources - "${myNativeSourceDir}/InterfaceExport.c" - "${myNativeSourceDir}/JavaBridge.c" - "${myNativeSourceDir}/JniUtil.c" - "${myNativeSourceDir}/JvmLocater_common.c" - "${myNativeSourceDir}/JvmLocater_linux.c" - "${myNativeSourceDir}/JvmLocater_windows.c" - ) - set(myNativeGeneratedSources - "${myNativeGeneratedSourceDir}/CallbackFunctionPointerBridge.c" - "${myNativeGeneratedSourceDir}/CallbackJNIBridge.c" - "${myNativeGeneratedSourceDir}/EventsJNIBridge.c" - ) - set(myNativeGeneratedHeaders - "${myNativeGeneratedSourceDir}/CallbackFunctionPointerBridge.h" - "${myNativeGeneratedSourceDir}/CallbackJNIBridge.h" - "${myNativeGeneratedSourceDir}/EventsJNIBridge.h" - ) - - set(myJavaSources - "${myJavaSourceDir}/${myPkg}/Util.java" - ) - set(myJavaGeneratedSources - "${myJavaGeneratedSourceDir}/${myPkg}/AI.java" - "${myJavaGeneratedSourceDir}/${myPkg}/AbstractAI.java" - "${myJavaGeneratedSourceDir}/${myPkg}/AICallback.java" - "${myJavaGeneratedSourceDir}/${myPkg}/JniAICallback.java" - "${myJavaGeneratedSourceDir}/${myPkg}/Enumerations.java" - ) - - set(myGeneratedSources - ${myNativeGeneratedSources} - ${myNativeGeneratedHeaders} - ${myJavaGeneratedSources} - ) - set_source_files_properties(${myGeneratedSources} PROPERTIES GENERATED TRUE) - - - # remove all files in the generates sources dir, - # that are not generated sources (of this build) - file(GLOB_RECURSE allGeneratedSrcFiles "${myGeneratedSourceDir}/*") - foreach (genFile ${allGeneratedSrcFiles}) - list(FIND myGeneratedSources "${genFile}" isInList) - if (${isInList} EQUAL -1) - file(REMOVE "${genFile}") - endif () - endforeach (genFile) - - #cleanup generated dir if some input file changed - add_custom_command( - OUTPUT - ${myGeneratedSourceDirStubFile} - COMMAND "${CMAKE_COMMAND}" - "-E" "remove_directory" "${myGeneratedSourceDir}" - COMMAND "${CMAKE_COMMAND}" - "-E" "make_directory" "${myNativeGeneratedSourceDir}" - COMMAND "${CMAKE_COMMAND}" - "-E" "make_directory" "${myJavaGeneratedSourceDir}/${myPkg}" - COMMAND "${CMAKE_COMMAND}" - "-E" "touch" "${myGeneratedSourceDirStubFile}" - DEPENDS - "${commonAwkScriptsDir}/common.awk" - "${commonAwkScriptsDir}/commonDoc.awk" - "${myAwkScriptsDir}/native_wrappCallback.awk" - "${myAwkScriptsDir}/native_wrappCommands.awk" - "${myAwkScriptsDir}/jni_wrappCallback.awk" - "${myAwkScriptsDir}/jni_wrappCommands.awk" - "${myAwkScriptsDir}/jni_wrappEvents.awk" - "${springAIInterfaceSourceDir}/SSkirmishAICallback.h" - "${springAIInterfaceSourceDir}/AISCommands.h" - "${springAIInterfaceSourceDir}/AISEvents.h" - WORKING_DIRECTORY - "${myAwkScriptsDir}" - COMMENT - " ${myTarget}: Cleanup & create generated source directories" VERBATIM - ) - - # 1. & 2. Wrapp Callback (native->native) - add_custom_command( - OUTPUT - "${myNativeGeneratedSourceDir}/CallbackFunctionPointerBridge.h" - "${myNativeGeneratedSourceDir}/CallbackFunctionPointerBridge.c" - COMMAND "${AWK_BIN}" - ${commonAwkScriptArgs} - "-f" "${myAwkScriptsDir}/native_wrappCallback.awk" - "${springAIInterfaceSourceDir}/SSkirmishAICallback.h" - COMMAND "${AWK_BIN}" - ${commonAwkScriptArgs} - "-f" "${myAwkScriptsDir}/native_wrappCommands.awk" - "${springAIInterfaceSourceDir}/AISCommands.h" - DEPENDS - "${myGeneratedSourceDirStubFile}" - "${commonAwkScriptsDir}/common.awk" - "${commonAwkScriptsDir}/commonDoc.awk" - "${myAwkScriptsDir}/native_wrappCallback.awk" - "${myAwkScriptsDir}/native_wrappCommands.awk" - "${springAIInterfaceSourceDir}/SSkirmishAICallback.h" - "${springAIInterfaceSourceDir}/AISCommands.h" - WORKING_DIRECTORY - "${myAwkScriptsDir}" - COMMENT - " ${myTarget}: Generating native callback wrapper sources" VERBATIM - ) - - # 3. Wrapp AI Callback (native-JNI->Java) - add_custom_command( - OUTPUT - "${myNativeGeneratedSourceDir}/CallbackJNIBridge.h" - "${myNativeGeneratedSourceDir}/CallbackJNIBridge.c" - "${myJavaGeneratedSourceDir}/${myPkg}/AICallback.java" - "${myJavaGeneratedSourceDir}/${myPkg}/JniAICallback.java" - COMMAND "${AWK_BIN}" - ${commonAwkScriptArgs} - "-f" "${myAwkScriptsDir}/jni_wrappCallback.awk" - "${myNativeGeneratedSourceDir}/CallbackFunctionPointerBridge.h" - DEPENDS - "${myGeneratedSourceDirStubFile}" - "${commonAwkScriptsDir}/common.awk" - "${commonAwkScriptsDir}/commonDoc.awk" - "${myAwkScriptsDir}/jni_wrappCallback.awk" - "${myNativeGeneratedSourceDir}/CallbackFunctionPointerBridge.h" - WORKING_DIRECTORY - "${myAwkScriptsDir}" - COMMENT - " ${myTarget}: Generating JNI callback wrapper sources" VERBATIM - ) - - # 4. Wrapp AI Events (native-JNI->Java) - add_custom_command( - OUTPUT - "${myNativeGeneratedSourceDir}/EventsJNIBridge.h" - "${myNativeGeneratedSourceDir}/EventsJNIBridge.c" - "${myJavaGeneratedSourceDir}/${myPkg}/AI.java" - "${myJavaGeneratedSourceDir}/${myPkg}/AbstractAI.java" - COMMAND "${AWK_BIN}" - ${commonAwkScriptArgs} - "-f" "${myAwkScriptsDir}/jni_wrappEvents.awk" - "${springAIInterfaceSourceDir}/AISEvents.h" - DEPENDS - "${myGeneratedSourceDirStubFile}" - "${commonAwkScriptsDir}/common.awk" - "${commonAwkScriptsDir}/commonDoc.awk" - "${myAwkScriptsDir}/jni_wrappEvents.awk" - "${springAIInterfaceSourceDir}/AISEvents.h" - WORKING_DIRECTORY - "${myAwkScriptsDir}" - COMMENT - " ${myTarget}: Generating JNI events wrapper sources" VERBATIM - ) - # 5. Wrapp ENUMS - - add_custom_command( - OUTPUT - "${myJavaGeneratedSourceDir}/${myPkg}/Enumerations.java" - COMMAND "${AWK_BIN}" - ${commonAwkScriptArgs} - "-f" "${myAwkScriptsDir}/jni_wrappCommands.awk" - "${springAIInterfaceSourceDir}/AISCommands.h" - DEPENDS - "${myGeneratedSourceDirStubFile}" - "${commonAwkScriptsDir}/common.awk" - "${commonAwkScriptsDir}/commonDoc.awk" - "${myAwkScriptsDir}/jni_wrappCommands.awk" - "${springAIInterfaceSourceDir}/AISCommands.h" - WORKING_DIRECTORY - "${myAwkScriptsDir}" - COMMENT - " ${myTarget}: Generating enums wrapper sources" VERBATIM - ) - add_custom_target(${myGenTarget} DEPENDS ${myGeneratedSources}) - add_dependencies(generateSources ${myGenTarget}) - - # Locate the manifest file - find_manifest_file("${myDir}" myManifestFile) - if (myManifestFile) - set(myBinJarArgs "cmf" "${myManifestFile}") - else (myManifestFile) - set(myBinJarArgs "cf") - endif (myManifestFile) - - # Build the native part - # --------------------- - if (MINGW) - # It is important that this is used instead of the one - # from the installed JDK, as the jni_md.h is in here too, - # and this file contains OS (win32) specific information. - include_directories(BEFORE ${MINGWLIBS}/include/java) - endif (MINGW) - include_directories(BEFORE "${rts}/lib/streflop" "${myNativeSourceDir}" "${myNativeGeneratedSourceDir}") - add_library(${myNativeTarget} MODULE ${myNativeSources} ${myNativeGeneratedSources} ${ai_common_SRC} ${myVersionDepFile}) - add_dependencies(${myNativeTarget} generateVersionFiles) - target_link_libraries(${myNativeTarget} CUtils streflop) - set_target_properties(${myNativeTarget} PROPERTIES COMPILE_FLAGS "-DUSING_STREFLOP") - set_target_properties(${myNativeTarget} PROPERTIES OUTPUT_NAME "AIInterface") - fix_lib_name(${myNativeTarget}) - - - # Build the java part - # ------------------- - - # Write list of source files to an arg-file - set(mySrcArgFile "${myBuildDir}/sourceFiles.txt") - if (EXISTS "${mySrcArgFile}") - file(REMOVE "${mySrcArgFile}") - endif (EXISTS "${mySrcArgFile}") - foreach (srcFile ${myJavaSources} ${myJavaGeneratedSources}) - file(APPEND "${mySrcArgFile}" "\"${srcFile}\"\n") - endforeach (srcFile) - - add_custom_command( - OUTPUT - "${myBuildDir}/${myBinJarFile}" - COMMAND "${CMAKE_COMMAND}" - "-E" "remove_directory" "${myJavaBuildDir}" - COMMAND "${CMAKE_COMMAND}" - "-E" "make_directory" "${myJavaBuildDir}" - COMMAND "${Java_JAVAC_EXECUTABLE}" ARGS - "${JAVA_COMPILE_FLAG_CONDITIONAL}" - "-Xlint:deprecation" - "-cp" "${myClassPath}" - "-d" "${myJavaBuildDir}" - "@${mySrcArgFile}" - COMMAND "${Java_JAR_EXECUTABLE}" ARGS - ${myBinJarArgs} "${myBuildDir}/${myBinJarFile}" - "-C" "${myJavaBuildDir}" "${myPkgFirstPart}" - DEPENDS - ${myJavaSources} - # Using the native target reduces parallelism slightly - # (less then 1s per build => negliable), but prevetns multiple - # execution of the source generating custom-commands - # (which results in wrongly generated source files with "make -j ") - ${myJavaGeneratedSources} - ${myNativeTarget} - WORKING_DIRECTORY - "${myJavaGeneratedSourceDir}" - COMMENT - " ${myTarget}: Compiling sources and packing library ${myBinJarFile}" VERBATIM - ) - - add_custom_command( - OUTPUT - "${myBuildDir}/${mySrcJarFile}" - COMMAND "${Java_JAR_EXECUTABLE}" ARGS - "cf" "${myBuildDir}/${mySrcJarFile}" - "-C" "${myJavaSourceDir}" "${myPkgFirstPart}" - COMMAND "${Java_JAR_EXECUTABLE}" ARGS - "uf" "${myBuildDir}/${mySrcJarFile}" - "-C" "${myJavaGeneratedSourceDir}" "${myPkgFirstPart}" - COMMAND "${Java_JAR_EXECUTABLE}" ARGS - "uf" "${myBuildDir}/${mySrcJarFile}" - "-C" "${mySourceDir}" "native" - COMMAND "${Java_JAR_EXECUTABLE}" ARGS - "uf" "${myBuildDir}/${mySrcJarFile}" - "-C" "${myGeneratedSourceDir}" "native" - COMMAND "${Java_JAR_EXECUTABLE}" ARGS - "uf" "${myBuildDir}/${mySrcJarFile}" - "-C" "${myDir}" "VERSION" - DEPENDS - ${myJavaSources} - # Using the native target reduces parallelism slightly - # (less then 1s per build => negliable), but prevetns multiple - # execution of the source generating custom-commands - # (which results in wrongly generated source files with "make -j ") - ${myJavaGeneratedSources} - ${myNativeTarget} - WORKING_DIRECTORY - "${myBuildDir}" - COMMENT - " ${myTarget}: Creating sources archive ${mySrcJarFile}" VERBATIM - ) - - add_custom_target(${myJavaTarget} - DEPENDS - "${myBuildDir}/${myBinJarFile}" - "${myBuildDir}/${mySrcJarFile}" - ) - - add_custom_target(${myTarget} ALL) - add_dependencies(${myTarget} ${myNativeTarget}) - - # this sets the version in pom.xml - set(myMavenProperties "-Dmy.version=${myVersion}") - add_custom_command( - OUTPUT - "${myBuildDir}/pom-generated.xml" - COMMAND "${CMAKE_COMMAND}" - "-Dfile.in=${myDir}/pom.xml" - "-Dfile.out=${myBuildDir}/pom-generated.xml" - ${myMavenProperties} - "-P" "${CMAKE_MODULES_SPRING}/ConfigureFile.cmake" - DEPENDS - "${myDir}/pom.xml" - WORKING_DIRECTORY - "${myDir}" - COMMENT - " ${myTarget}: Configure pom.xml" VERBATIM - ) - set_source_files_properties("${myBuildDir}/pom-generated.xml" PROPERTIES GENERATED TRUE) - - add_dependencies(${myTarget} ${myJavaTarget}) - - # Install the native library - install(TARGETS ${myNativeTarget} DESTINATION ${myInstLibsDir}) - # Install the data files - install(DIRECTORY "${myDir}/data/" DESTINATION ${myInstLibsDir} FILES_MATCHING PATTERN REGEX "InterfaceInfo\\.lua$") - install(DIRECTORY "${myDir}/data/" DESTINATION ${myInstDataDir} FILES_MATCHING PATTERN REGEX "InterfaceInfo\\.lua$" EXCLUDE PATTERN "*") - # Install the library - install(FILES "${myBuildDir}/${myBinJarFile}" DESTINATION ${myInstDataDir}) - # Install the sources archive - install(FILES "${myBuildDir}/${mySrcJarFile}" DESTINATION ${myInstDataDir}/jlib) -endif (BUILD_${myName}_AIINTERFACE) diff --git a/AI/Interfaces/Java/VERSION b/AI/Interfaces/Java/VERSION deleted file mode 100644 index ceab6e11ece..00000000000 --- a/AI/Interfaces/Java/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.1 \ No newline at end of file diff --git a/AI/Interfaces/Java/bin/ant.properties b/AI/Interfaces/Java/bin/ant.properties deleted file mode 100644 index b9c0a35d3ec..00000000000 --- a/AI/Interfaces/Java/bin/ant.properties +++ /dev/null @@ -1,21 +0,0 @@ -# Paths are relative to the project home (which is ../ from this file). -# All values are optional. - -;spring.home=../../.. - -# This is used only in the next property -;build.home=${spring.home}/build -# Where jar files will be built -;build.dir=${build.home}/AI/Interfaces/${interface.name} - -# Where generated sources shall be created in -;src.generated=${build.dir}/src-generated/main -;src.generated.native=${src.generated}/native -;src.generated.java=${src.generated}/java - -# This is used only in the next property -;dist.home=${spring.home}/dist -# Where jar files will be installed to -;dist.dir=${dist.home}/AI/Interfaces/${interface.name}/${interface.version} -# Where HTML JavaDoc files will be generated -;doc.dir=${dist.home}/AI/Interfaces/${interface.name}/${interface.version}/doc/jdoc diff --git a/AI/Interfaces/Java/bin/build.xml b/AI/Interfaces/Java/bin/build.xml deleted file mode 100644 index 86e0033068a..00000000000 --- a/AI/Interfaces/Java/bin/build.xml +++ /dev/null @@ -1,258 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AI/Interfaces/Java/bin/jni_wrappCallback.awk b/AI/Interfaces/Java/bin/jni_wrappCallback.awk deleted file mode 100755 index 833bdb90ec3..00000000000 --- a/AI/Interfaces/Java/bin/jni_wrappCallback.awk +++ /dev/null @@ -1,573 +0,0 @@ -#!/usr/bin/awk -f -# -# This awk script creates a Java class with native/JNI functions, -# plus their respective native counterparts, -# to call to C functions in: -# NATIVE_GENERATED_SOURCE_DIR/CallbackFunctionPointerBridge.h -# -# This script uses functions from the following files: -# * common.awk -# * commonDoc.awk -# Variables that can be set on the command-line (with -v): -# * GENERATED_SOURCE_DIR: the generated sources root dir -# -# usage: -# awk -f thisScript.awk -f common.awk -f commonDoc.awk -# awk -f thisScript.awk -f common.awk -f commonDoc.awk \ -# -v 'GENERATED_SOURCE_DIR=/tmp/build/AI/Interfaces/Java/src-generated/main' -# - -BEGIN { - # initialize things - - # define the field splitter(-regex) - FS = "(,)|(\\()|(\\);)"; - - # Used by other scripts - JAVA_MODE = 1; - - # These vars can be assigned externally, see file header. - # Set the default values if they were not supplied on the command line. - if (!GENERATED_SOURCE_DIR) { - GENERATED_SOURCE_DIR = "../src-generated/main"; - } - if (!JAVA_GENERATED_SOURCE_DIR) { - JAVA_GENERATED_SOURCE_DIR = GENERATED_SOURCE_DIR "/java"; - } - if (!NATIVE_GENERATED_SOURCE_DIR) { - NATIVE_GENERATED_SOURCE_DIR = GENERATED_SOURCE_DIR "/native"; - } - - nativeBridge = "CallbackFunctionPointerBridge"; - bridgePrefix = "bridged__"; - - jniBridge = "CallbackJNIBridge"; - - myPkgA = "com.springrts.ai"; - myPkgD = convertJavaNameFormAToD(myPkgA); - myPkgC = convertJavaNameFormAToC(myPkgA); - myInterface = "AICallback"; - myClass = "JniAICallback"; - - fi = 0; -} - -function doWrapp(funcIndex_dw) { - - paramListJava_dw = funcParamList[funcIndex_dw]; - doWrapp_dw = 1; - - fullName_dw = funcFullName[funcIndex_dw]; - if (doWrapp_dw) { - metaInf_dw = funcMetaInf[funcIndex_dw]; - - if (match(metaInf_dw, /ARRAY:/)) { - #doWrapp_dw = 0; - } - if (match(metaInf_dw, /MAP:/)) { - #doWrapp_dw = 0; - } - if (match(fullName_dw, "^" bridgePrefix "File_")) { - doWrapp_dw = 0; - } - } else { - print("Java-AIInterface: NOTE: JNI level: Callback: intentionally not wrapped: " fullName_dw); - } - - return doWrapp_dw; -} - -function createNativeFileName(fileName_fn, isHeader_fn) { - - absFileName_fn = NATIVE_GENERATED_SOURCE_DIR "/" fileName_fn; - if (isHeader_fn) { - absFileName_fn = absFileName_fn ".h"; - } else { - absFileName_fn = absFileName_fn ".c"; - } - - return absFileName_fn; -} - -function printNativeJNI() { - - outFile_nh = createNativeFileName(jniBridge, 1); - outFile_nc = createNativeFileName(jniBridge, 0); - - printCommentsHeader(outFile_nh); - printCommentsHeader(outFile_nc); - - print("") >> outFile_nh; - print("#ifndef __CALLBACK_JNI_BRIDGE_H") >> outFile_nh; - print("#define __CALLBACK_JNI_BRIDGE_H") >> outFile_nh; - print("") >> outFile_nh; - print("#include ") >> outFile_nh; - print("") >> outFile_nh; - print("#ifdef __cplusplus") >> outFile_nh; - print("extern \"C\" {") >> outFile_nh; - print("#endif") >> outFile_nh; - print("") >> outFile_nh; - - print("") >> outFile_nc; - print("#include \"" jniBridge ".h\"") >> outFile_nc; - print("") >> outFile_nc; - print("#include \"" nativeBridge ".h\"") >> outFile_nc; - print("") >> outFile_nc; - print("") >> outFile_nc; - - # print the wrapping functions - for (i=0; i < fi; i++) { - fullName = funcFullName[i]; - retType = funcRetTypeC[i]; - paramList = funcParamListC[i]; - paramListNoTypes = removeParamTypes(paramList); - metaInf = funcMetaInf[i]; - - if (doWrapp(i)) { - isRetString = part_isRetString(fullName, metaInf); - if (isRetString) { - retString = part_getRetString(metaInf); - split(retString, retStringNames, ":"); # must return 2 elements - } - - javaName = fullName; - sub("^" bridgePrefix, "", javaName); - gsub(/_/, "_1", javaName); - jni_funcName = "Java_" myPkgC "_" myClass "_" javaName; - jni_retType = convertCToJNIType(retType); - jni_paramList = ""; - size_params = split(paramList, params, ","); - for (p=1; p <= size_params; p++) { - pType_c = params[p]; - sub(/ [^ ]+$/, "", pType_c); - pType_c = trim(pType_c); - - pName = params[p]; - sub(/^.* /, "", pName); - - pType_jni = convertCToJNIType(pType_c, pName); - - # these are later used for conversion - c_paramTypes[p] = pType_c; - c_paramNames[p] = pName; - jni_paramTypes[p] = pType_jni; - jni_paramNames[p] = pName; - - if (isRetString && (pName == retStringNames[1] || pName == retStringNames[2])) { - if (pName == retStringNames[1]) - jni_retType = convertCToJNIType(pType_c); - continue; - } - jni_paramList = jni_paramList ", " pType_jni " " pName; - } - jni_paramListNoTypes = removeParamTypes(jni_paramList); - isVoidRet = match(jni_retType, /^void$/); - - # print function declaration to *.h - #printFunctionComment_Common(outFile_nh, funcDocComment, i, ""); - print("JNIEXPORT " jni_retType " JNICALL " jni_funcName "(JNIEnv* __env, jobject __obj" jni_paramList ");") >> outFile_nh; - print("") >> outFile_nh; - - # print function definition to *.c - print("JNIEXPORT " jni_retType " JNICALL " jni_funcName "(JNIEnv* __env, jobject __obj" jni_paramList ") {") >> outFile_nc; - print("") >> outFile_nc; - - if (!isVoidRet) { - print("\t" jni_retType " _ret;") >> outFile_nc; - print("") >> outFile_nc; - } - - # Return value conversion - pre call - retType_isString = ((match(retType, /^(const )?char*/) || isRetString) && (jni_retType == "jstring")); - retTypeConv = 0; - if (retType_isString) { - if (isRetString) - print("\t" "char " retStringNames[1] "[2048] = {'\\0'};") >> outFile_nc; - print("\t" retType " _retNative;") >> outFile_nc; - retTypeConv = 1; - } - - hasRetParam = 0; - # Params conversion - pre call - for (p=1; p <= size_params; p++) { - pType_jni = jni_paramTypes[p]; - - if (pType_jni == "jstring") { - # jstring - if (!isRetString || c_paramNames[p] != retStringNames[1]) { - c_paramNames[p] = c_paramNames[p] "_native"; - sub(" " jni_paramNames[p], " " c_paramNames[p], paramListNoTypes); - print("\t" c_paramTypes[p] " " c_paramNames[p] " = (" c_paramTypes[p] ") (*__env)->GetStringUTFChars(__env, " jni_paramNames[p] ", NULL);") >> outFile_nc; - } - } else if (pType_jni == "jint") { - if (isRetString && c_paramNames[p] == retStringNames[2]) - sub(" " jni_paramNames[p], " sizeof(" retStringNames[1] ")", paramListNoTypes); - } else if (match(pType_jni, /^j.+Array$/)) { - # primitive array - c_paramNames[p] = c_paramNames[p] "_native"; - sub(" " jni_paramNames[p], " " c_paramNames[p], paramListNoTypes); - - capArrType = pType_jni; - sub(/^j/, "", capArrType); - sub(/Array$/, "", capArrType); - capArrType = capitalize(capArrType); - - _isPrimitive = (capArrType != "Object"); - _isString = !_isPrimitive && match(c_paramTypes[p], /(const )?char\*\*/); - - print("\t" c_paramTypes[p] " " c_paramNames[p] " = NULL;") >> outFile_nc; - print("\t" "if (" jni_paramNames[p] " != NULL) {") >> outFile_nc; - if (_isPrimitive) { - print("\t\t" c_paramNames[p] " = (" c_paramTypes[p] ") (*__env)->Get" capArrType "ArrayElements(__env, " jni_paramNames[p] ", NULL);") >> outFile_nc; - } else if (_isString) { - print("\t\t" "const int " c_paramNames[p] "_size = (int) (*__env)->GetArrayLength(__env, " jni_paramNames[p] ");") >> outFile_nc; - print("\t\t" c_paramNames[p] " = (" c_paramTypes[p] ") malloc(sizeof(char*) * " c_paramNames[p] "_size);") >> outFile_nc; - } else { - print("ERROR: do not know how to convert parameter type: " pType_jni); - exit(1); - } - print("\t" "}") >> outFile_nc; - } else if (pType_jni == "jobject") { - # StringBuffer - hasRetParam = 1; - cPaNa = c_paramNames[p]; - c_paramNames[p] = cPaNa "_native"; - sub(" " jni_paramNames[p], " " c_paramNames[p], paramListNoTypes); - print("\t" "char " c_paramNames[p] "[MAX_RESPONSE_SIZE];") >> outFile_nc; - retParamConversion = "\t" "jclass clazz = (*__env)->GetObjectClass(__env, " cPaNa ");" "\n"; - retParamConversion = retParamConversion "\t" "jmethodID mid = (*__env)->GetMethodID(__env, clazz, \"append\", \"(Ljava/lang/String;)Ljava/lang/StringBuffer;\");" "\n" - retParamConversion = retParamConversion "\t" "jstring " cPaNa "_jStr = (*__env)->NewStringUTF(__env, " c_paramNames[p] ");" "\n"; - retParamConversion = retParamConversion "\t" "(*__env)->CallObjectMethod(__env, " cPaNa ", mid, " cPaNa "_jStr);" - } - } - - condRet = ""; - if (!isVoidRet) { - if (retTypeConv) { - condRet = "_retNative = "; - } else { - condRet = "_ret = (" jni_retType ") "; - } - } - print("\t" condRet fullName "(" paramListNoTypes ");") >> outFile_nc; - if (hasRetParam) { - print(retParamConversion) >> outFile_nc; - } - - # Params conversion - post call - for (p=1; p <= size_params; p++) { - pType_jni = jni_paramTypes[p]; - - if (pType_jni == "jstring") { - # jstring - if (!isRetString || c_paramNames[p] != retStringNames[1]) - print("\t" "(*__env)->ReleaseStringUTFChars(__env, " jni_paramNames[p] ", " c_paramNames[p] ");") >> outFile_nc; - } else if (match(pType_jni, /^j.+Array$/)) { - # primitive array - capArrType = pType_jni; - sub(/^j/, "", capArrType); - sub(/Array$/, "", capArrType); - capArrType = capitalize(capArrType); - - _isPrimitive = (capArrType != "Object"); - _isString = !_isPrimitive && match(c_paramTypes[p], /(const )?char\*\*/); - - print("\t" "if (" jni_paramNames[p] " != NULL) {") >> outFile_nc; - if (_isPrimitive) { - _elementJNativeType = jni_paramTypes[p]; - sub(/Array$/, "", _elementJNativeType); # jfloatArray -> jfloat - print("\t\t" "(*__env)->Release" capArrType "ArrayElements(__env, " jni_paramNames[p] ", (" _elementJNativeType "*) " c_paramNames[p] ", 0 /* copy back changes and release */);") >> outFile_nc; - } else if (_isString) { - print("\t\t" "const int " c_paramNames[p] "_size = (int) (*__env)->GetArrayLength(__env, " jni_paramNames[p] ");") >> outFile_nc; - print("\t\t" "int " c_paramNames[p] "_i;") >> outFile_nc; - print("\t\t" "jstring " c_paramNames[p] "_jStr;") >> outFile_nc; - print("\t\t" "for (" c_paramNames[p] "_i=0; " c_paramNames[p] "_i < " c_paramNames[p] "_size; ++" c_paramNames[p] "_i) {") >> outFile_nc; - print("\t\t\t" c_paramNames[p] "_jStr = (jstring) (*__env)->NewStringUTF(__env, " c_paramNames[p] "[" c_paramNames[p] "_i]);") >> outFile_nc; - print("\t\t\t" "(*__env)->SetObjectArrayElement(__env, " jni_paramNames[p] ", " c_paramNames[p] "_i, " c_paramNames[p] "_jStr);") >> outFile_nc; - print("\t\t\t" "(*__env)->DeleteLocalRef(__env, " c_paramNames[p] "_jStr);") >> outFile_nc; - print("\t\t" "}") >> outFile_nc; - print("\t\t" "free(" c_paramNames[p] ");") >> outFile_nc; - } - print("\t" "}") >> outFile_nc; - } else if (pType_jni == "jobject" ) { - # StringBuffer - print("\t" "(*__env)->DeleteLocalRef(__env, " cPaNa "_jStr);") >> outFile_nc; - } - } - - # Return value conversion - post call - if (retType_isString) { - print("\t" "_ret = (*__env)->NewStringUTF(__env, " (isRetString ? retStringNames[1] : "_retNative") ");") >> outFile_nc; - } - - if (!isVoidRet) { - print("") >> outFile_nc; - print("\t" "return _ret;") >> outFile_nc; - } - print("" "}") >> outFile_nc; - print("") >> outFile_nc; - } else { - print("Note: The following function is intentionally not wrapped: " fullName); - } - } - - - print("#ifdef __cplusplus") >> outFile_nh; - print("} // extern \"C\"") >> outFile_nh; - print("#endif") >> outFile_nh; - print("") >> outFile_nh; - print("#endif // __CALLBACK_JNI_BRIDGE_H") >> outFile_nh; - print("") >> outFile_nh; - - close(outFile_nh); - close(outFile_nc); -} - - -function printHeader(outFile_h, javaPkg_h, javaClassName_h) { - - printCommentsHeader(outFile_h); - print("") >> outFile_h; - print("package " javaPkg_h ";") >> outFile_h; - print("") >> outFile_h; - print("") >> outFile_h; - print("/**") >> outFile_h; - print(" * Lets Java Skirmish AIs call back to the Spring engine.") >> outFile_h; - print(" * We are using JNI for best speed.") >> outFile_h; - print(" *") >> outFile_h; - print(" * @author AWK wrapper script") >> outFile_h; - print(" * @version GENERATED") >> outFile_h; - print(" */") >> outFile_h; - if (javaClassName_h == myClass) { - print("public class " javaClassName_h " implements " myInterface " {") >> outFile_h; - } else { - print("public interface " javaClassName_h " {") >> outFile_h; - } - print("") >> outFile_h; -} - -function createJavaFileName(clsName_f) { - return JAVA_GENERATED_SOURCE_DIR "/" myPkgD "/" clsName_f ".java"; -} - -function printJavaClsAndInt() { - - outFile_i = createJavaFileName(myInterface); - outFile_c = createJavaFileName(myClass); - - printHeader(outFile_i, myPkgA, myInterface); - printHeader(outFile_c, myPkgA, myClass); - - # print the static registrator - print("\tstatic {") >> outFile_c; - print("\t\tSystem.loadLibrary(\"AIInterface\");") >> outFile_c; - print("\t}") >> outFile_c; - print("") >> outFile_c; - - # print skirmishAIId getter in interface - print("\t" "public int SkirmishAI_getSkirmishAIId();") >> outFile_i; - print("") >> outFile_i; - - # print skirmishAIId member, constructor and getter - print("\t" "private int skirmishAIId;") >> outFile_c; - print("") >> outFile_c; - print("\t" "public " myClass "(int skirmishAIId) {") >> outFile_c; - print("\t\t" "this.skirmishAIId = skirmishAIId;") >> outFile_c; - print("\t}") >> outFile_c; - print("") >> outFile_c; - print("\t" "@Override") >> outFile_c; - print("\t" "public int SkirmishAI_getSkirmishAIId() {") >> outFile_c; - print("\t\t" "return this.skirmishAIId;") >> outFile_c; - print("\t}") >> outFile_c; - print("") >> outFile_c; - - # print the callback methods - for (i=0; i < fi; i++) { - if (doWrapp(i)) { - fullName = funcFullName[i]; - retType = funcRetTypeJ[i]; - paramList = funcParamListJ[i]; - metaInf = funcMetaInf[i]; - - isRetString = part_isRetString(fullName, metaInf); - if (isRetString) { - retString = part_getRetString(metaInf); - split(retString, retStringNames, ":"); # must return 2 elements - size_params = split(paramList, params, ","); - for (p=1; p <= size_params; p++) { - pName = params[p]; - sub(/^.* /, "", pName); - if (pName == retStringNames[1]) { - ps = 1; - } else if (pName == retStringNames[2]) { - ps = 2; - } else continue; - pType_c = params[p]; - sub(/ [^ ]+$/, "", pType_c); - pType_c = trim(pType_c); - retStringTypes[ps] = convertJNIToJavaType(convertCToJNIType(pType_c, pName)); - } - retType = retStringTypes[1]; - sub(", " retStringTypes[1] " " retStringNames[1] ", " retStringTypes[2] " " retStringNames[2], "", paramList); - } - - paramListNoSID = paramList; - sub(/int _skirmishAIId(, )?/, "", paramListNoSID); - paramListNoSIDNoTypes = removeParamTypes(paramListNoSID); - if (paramListNoSIDNoTypes != "") { - paramListNoSIDNoTypes = ", " paramListNoSIDNoTypes; - } - condRet = ""; - if (retType != "void") { - condRet = "return "; - } - - sub("^" bridgePrefix, "", fullName); - - metaInfCommand = ""; - if (metaInf != "") { - metaInfCommand = " // " metaInf; - } - - # print the interface function - printFunctionComment_Common(outFile_i, funcDocComment, i, "\t"); - print("\t" "public " retType " " fullName "(" paramListNoSID ");" metaInfCommand) >> outFile_i; - print("") >> outFile_i; - - # print the interface implementing function - commentText = getFunctionComment_Common(funcDocComment, i); - if (match(commentText, /@deprecated/)) { - # this prevents javac from outputting a warning - print("\t" "/** @deprecated */") >> outFile_c; - } - print("\t" "@Override") >> outFile_c; - print("\t" "public " retType " " fullName "(" paramListNoSID ") {") >> outFile_c; - print("\t\t" condRet "this." fullName "(this.skirmishAIId" paramListNoSIDNoTypes ");") >> outFile_c; - print("\t" "}") >> outFile_c; - - # print the private native function - print("\t" "private native " retType " " fullName "(" paramList ");") >> outFile_c; - print("") >> outFile_c; - } - } - - print("}") >> outFile_i; - print("") >> outFile_i; - close(outFile_i); - - print("}") >> outFile_c; - print("") >> outFile_c; - close(outFile_c); -} - - -function wrappFunction(funcDef, commentEol) { - - doParse = 1; - - if (doParse) { - size_funcParts = split(funcDef, funcParts, "(,)|(\\()|(\\))"); - # because the empty part after ");" would count as part as well - size_funcParts--; - retType_c = trim(funcParts[2]); - retType_j = convertJNIToJavaType(convertCToJNIType(retType_c)); - fullName = trim(funcParts[3]); - - # function parameters - paramList_c = ""; - paramList_j = ""; - paramList = ""; - - for (i=4; i<=size_funcParts && !match(funcParts[i], /.*\/\/.*/); i++) { - type_c = extractParamType(funcParts[i]); - type_c = cleanupCType(type_c); - name = extractParamName(funcParts[i]); - type_j = convertJNIToJavaType(convertCToJNIType(type_c, name)); - if (i == 4) { - cond_comma = ""; - } else { - cond_comma = ", "; - } - paramList_c = paramList_c cond_comma type_c " " name; - paramList_j = paramList_j cond_comma type_j " " name; - } - - funcFullName[fi] = fullName; - funcRetTypeC[fi] = retType_c; - funcRetTypeJ[fi] = retType_j; - funcParamListC[fi] = paramList_c; - funcParamListJ[fi] = paramList_j; - funcMetaInf[fi] = trim(commentEol); - storeDocLines(funcDocComment, fi); - fi++; - } else { - print("warning: function intentionally NOT wrapped: " funcDef); - } -} - - - -# This function has to return true (1) if a doc comment (eg: /** foo bar */) -# can be deleted. -# If there is no special condition you want to apply, -# it should always return true (1), -# cause there are additional mechanism to prevent accidental deleting. -# see: commonDoc.awk -function canDeleteDocumentation() { - return isMultiLineFunc != 1; -} - - - -# save function pointer info into arrays -# ... 2nd, 3rd, ... line of a function pointer definition -{ - if (isMultiLineFunc) { # function is defined on one single line - funcIntermLine = $0; - # separate possible comment at end of line: // fu bar - commentEol = funcIntermLine; - if (sub(/.*\/\//, "", commentEol)) { - commentEolTot = commentEolTot commentEol; - } - # remove possible comment at end of line: // fu bar - sub(/[ \t]*\/\/.*/, "", funcIntermLine); - funcIntermLine = trim(funcIntermLine); - funcSoFar = funcSoFar " " funcIntermLine; - if (match(funcSoFar, /;$/)) { - # function ends in this line - wrappFunction(funcSoFar, commentEolTot); - isMultiLineFunc = 0; - } - } -} -# 1st line of a function pointer definition -/^EXPORT\(/ { - - funcStartLine = $0; - # separate possible comment at end of line: // foo bar - commentEolTot = ""; - commentEol = funcStartLine; - if (sub(/.*\/\//, "", commentEol)) { - commentEolTot = commentEolTot commentEol; - } - # remove possible comment at end of line: // fu bar - sub(/\/\/.*$/, "", funcStartLine); - funcStartLine = trim(funcStartLine); - if (match(funcStartLine, /;$/)) { - # function ends in this line - wrappFunction(funcStartLine, commentEolTot); - } else { - funcSoFar = funcStartLine; - isMultiLineFunc = 1; - } -} - - - - -END { - # finalize things - - printNativeJNI(); - printJavaClsAndInt(); -} diff --git a/AI/Interfaces/Java/bin/jni_wrappCommands.awk b/AI/Interfaces/Java/bin/jni_wrappCommands.awk deleted file mode 100755 index abd4c0e62e1..00000000000 --- a/AI/Interfaces/Java/bin/jni_wrappCommands.awk +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/awk -f -# -# This awk script creates a Java class containing important enumerations. -# These enumerations are taken from: rts/ExternalAI/Interface/AISCommands.h -# It currently only takes CommandTopic and UnitCommandOptions enumerations -# -# This script uses functions from the following files: -# * common.awk -# * commonDoc.awk -# Variables that can be set on the command-line (with -v): -# * GENERATED_SOURCE_DIR: the generated sources root dir -# -# usage: -# awk -f thisScript.awk -f common.awk -f commonDoc.awk -# awk -f thisScript.awk -f common.awk -f commonDoc.awk \ -# -v 'GENERATED_SOURCE_DIR=/tmp/build/AI/Interfaces/Java/src-generated/main' -# - -BEGIN { - # initialize things - - # define the field splitter(-regex) - FS = "[ \t]+"; - - # Used by other scripts - JAVA_MODE = 1; - - # These vars can be assigned externally, see file header. - # Set the default values if they were not supplied on the command line. - if (!GENERATED_SOURCE_DIR) { - GENERATED_SOURCE_DIR = "../src-generated/main"; - } - if (!JAVA_GENERATED_SOURCE_DIR) { - JAVA_GENERATED_SOURCE_DIR = GENERATED_SOURCE_DIR "/java"; - } - if (!NATIVE_GENERATED_SOURCE_DIR) { - NATIVE_GENERATED_SOURCE_DIR = GENERATED_SOURCE_DIR "/native"; - } - - myPkgA = "com.springrts.ai"; - myPkgD = convertJavaNameFormAToD(myPkgA); - myClass = "Enumerations"; - - #create empty arrays, holding names and values of the two enumerations - cmdsTopicNamesLength = 0; - split("", cmdsTopicNames); - cmdsTopicValuesLength = 0; - split("", cmdsTopicValues); - unitCmdsTopicNamesLength = 0; - split("", unitCmdsTopicNames); - unitCmdsTopicValuesLength = 0; - split("", unitCmdsTopicValues); -} - - -function createJavaFileName(fileName_fn) { - return JAVA_GENERATED_SOURCE_DIR "/" myPkgD "/" fileName_fn ".java"; -} - -function printGeneralJavaHeader(outFile_h, javaPkg_h, javaClassName_h) { - - printCommentsHeader(outFile_h); - print("") >> outFile_h; - print("package " javaPkg_h ";") >> outFile_h; - print("") >> outFile_h; - print("") >> outFile_h; - print("/**") >> outFile_h; - print(" * These are the Java exposed enumerations.") >> outFile_h; - print(" * We are not calling the engine, this is a pure Java class.") >> outFile_h; - print(" *") >> outFile_h; - print(" * @author AWK wrapper script") >> outFile_h; - print(" * @version GENERATED") >> outFile_h; - print(" */") >> outFile_h; - print("public abstract class " javaClassName_h " {") >> outFile_h; - print("") >> outFile_h; -} - -function printJavaHeader() { - - outFile_i = createJavaFileName(myClass); - - printGeneralJavaHeader(outFile_i, myPkgA, myClass); -} - -function printJavaEnums(enums) { - printJavaEnum("CommandTopic", cmdsTopicNames, cmdsTopicNamesLength, cmdsTopicValues); - print("") >> outFile_i; - printJavaEnum("UnitCommandOptions", unitCmdsTopicNames, unitCmdsTopicNamesLength, unitCmdsTopicValues); -} - -function printJavaEnum(enumName, names, namesLength, values) { - outFile_i = createJavaFileName(myClass); - printEnumHeader(enumName); - - # Prints the enum members and values - first = 0; - for (i=0; i> outFile_i; - } else { - printf(",\n\t\t") >> outFile_i; - } - printJavaEnumItem(names[i], values[i]); - } - - if (first != 0) { - print(";\n") >> outFile_i; - } - printEnumEnd(enumName); -} - -function printEnumHeader(name) { - outFile_i = createJavaFileName(myClass); - - # Prints the enum start - print("\tpublic enum " name " {") >> outFile_i; -} - -function printEnumEnd(name) { - outFile_i = createJavaFileName(myClass); - - # Prints the enum end - print("\t\tprivate final int id;") >> outFile_i; - print("\t\t" name "(int id) { this.id = id; }") >> outFile_i; - print("\t\tpublic int getValue() { return id; }") >> outFile_i; - print("\t}") >> outFile_i; -} - -function printJavaEnumItem(key, value) { - printf(key "(" value ")") >> outFile_i; -} - -function printJavaEnd() { - - outFile_i = createJavaFileName(myClass); - - print("}") >> outFile_i; - print("") >> outFile_i; - - close(outFile_i); -} - -/^[ \t]*COMMAND_.*$/ { - - doWrapp = !match(($0), /.*COMMAND_NULL.*/); - if (doWrapp) { - #parse enumeration of format x = number1, - sub(",", "", $4); - cmdsTopicNames[cmdsTopicNamesLength] = $2; - cmdsTopicNamesLength++; - cmdsTopicValues[cmdsTopicValuesLength] = $4; - cmdsTopicValuesLength++; - } else { - print("Java-AIInterface: NOTE: JNI level: COMMANDS: intentionally not wrapped: " $2); - } -} - -/^[ \t]*UNIT_COMMAND_.*$/ { - doWrapp = !match(($0), /.*COMMAND_NULL.*/); - if (doWrapp) { - unitCmdsTopicNames[unitCmdsTopicNamesLength] = $2; - unitCmdsTopicNamesLength++; - #parse enumeration of format x = (number1 << number2), - sub("[ \t]*//.*", "", $0); - sub(",", "", $0); - sub($3, "", $0); - sub($2, "", $0); - sub("[ \t]*", "", $0); - unitCmdsTopicValues[unitCmdsTopicValuesLength] = $0; - unitCmdsTopicValuesLength++; - } else { - print("Java-AIInterface: NOTE: JNI level: UNIT_COMMANDS: intentionally not wrapped: " $2); - } -} - -# This function has to return true (1) if a doc comment (eg: /** foo bar */) -# can be deleted. -# If there is no special condition you want to apply, -# it should always return true (1), -# cause there are additional mechanism to prevent accidental deleting. -# see: commonDoc.awk -function canDeleteDocumentation() { - return isInsideEvtStruct != 1; -} - -END { - # finalize things - printJavaHeader(); - printJavaEnums(); - printJavaEnd(); -} diff --git a/AI/Interfaces/Java/bin/jni_wrappEvents.awk b/AI/Interfaces/Java/bin/jni_wrappEvents.awk deleted file mode 100755 index a1907084b30..00000000000 --- a/AI/Interfaces/Java/bin/jni_wrappEvents.awk +++ /dev/null @@ -1,570 +0,0 @@ -#!/usr/bin/awk -f -# -# This awk script creates a Java class with one function per event, -# plus a C wrapper to easily/comfortably call these functions from C. -# input is taken from file: -# rts/ExternalAI/Interface/AISEvents.h -# -# This script uses functions from the following files: -# * common.awk -# * commonDoc.awk -# Variables that can be set on the command-line (with -v): -# * GENERATED_SOURCE_DIR: the generated sources root dir -# -# usage: -# awk -f thisScript.awk -f common.awk -f commonDoc.awk -# awk -f thisScript.awk -f common.awk -f commonDoc.awk \ -# -v 'GENERATED_SOURCE_DIR=/tmp/build/AI/Interfaces/Java/src-generated/main' -# - -BEGIN { - # initialize things - - # define the field splitter(-regex) - FS = "[ \t]+"; - - # Used by other scripts - JAVA_MODE = 1; - - # These vars can be assigned externally, see file header. - # Set the default values if they were not supplied on the command line. - if (!GENERATED_SOURCE_DIR) { - GENERATED_SOURCE_DIR = "../src-generated/main"; - } - if (!JAVA_GENERATED_SOURCE_DIR) { - JAVA_GENERATED_SOURCE_DIR = GENERATED_SOURCE_DIR "/java"; - } - if (!NATIVE_GENERATED_SOURCE_DIR) { - NATIVE_GENERATED_SOURCE_DIR = GENERATED_SOURCE_DIR "/native"; - } - - myWrapperName = "EventsJNIBridge"; - myWrapperPrefix = "eventsJniBridge_"; - - myPkgA = "com.springrts.ai"; - myPkgD = convertJavaNameFormAToD(myPkgA); - myAIClass = "AI"; - myAIAbstractClass = "AbstractAI"; - myAICallbackInt = "AICallback"; - - ind_evtTopics = 0; - ind_evtStructs = 0; - isInsideEvtStruct = 0; -} - - - -# Checks if a field is available and is no comment -function isFieldUsable(f) { - - valid = 0; - - if (f && !match(f, /.*\/\/.*/)) { - valid = 1; - } - - return valid; -} - -function createNativeFileName(fileName_fn, isHeader_fn) { - - absFileName_fn = NATIVE_GENERATED_SOURCE_DIR "/" fileName_fn; - if (isHeader_fn) { - absFileName_fn = absFileName_fn ".h"; - } else { - absFileName_fn = absFileName_fn ".c"; - } - - return absFileName_fn; -} -function createJavaFileName(fileName_fn) { - return JAVA_GENERATED_SOURCE_DIR "/" myPkgD "/" fileName_fn ".java"; -} - - -function printNativeHeader() { - - outFile_nh = createNativeFileName(myWrapperName, 1); - outFile_nc = createNativeFileName(myWrapperName, 0); - printCommentsHeader(outFile_nh); - printCommentsHeader(outFile_nc); - - # print includes (header) - print("") >> outFile_nh; - print("#ifndef __EVENTS_JNI_BRIDGE_H") >> outFile_nh; - print("#define __EVENTS_JNI_BRIDGE_H") >> outFile_nh; - print("") >> outFile_nh; - print("#include ") >> outFile_nh; - print("") >> outFile_nh; - print("#ifdef __cplusplus") >> outFile_nh; - print("extern \"C\" {") >> outFile_nh; - print("#endif") >> outFile_nh; - print("") >> outFile_nh; - - # print includes (source) - print("") >> outFile_nc; - print("#include \"" myWrapperName ".h\"") >> outFile_nc; - print("") >> outFile_nc; - print("#include \"JavaBridge.h\" // for INT_AI") >> outFile_nc; - print("#include \"JniUtil.h\"") >> outFile_nc; - print("#include \"ExternalAI/Interface/AISEvents.h\"") >> outFile_nc; - print("#include // for calloc(), free()") >> outFile_nc; - print("") >> outFile_nc; - print("") >> outFile_nc; - - # print global vars (source) - print("size_t skirmishAIId_size = 0;") >> outFile_nc; - print("jobject* skirmishAIId_callback = NULL;") >> outFile_nc; - print("") >> outFile_nc; -} - -function printNativeEventMethodVars() { - - for (e=0; e < ind_evtStructs; e++) { - printNativeEventMethodVar(e); - } - print("") >> outFile_nc; -} -function printNativeEventMethodVar(evtIndex) { - - outFile_nc = createNativeFileName(myWrapperName, 0); - - eName = evtsName[evtIndex]; - eNameLowerized = lowerize(eName); - - print("jmethodID m_ai_" eNameLowerized " = NULL;") >> outFile_nc; -} - -function printNativeStaticInitFuncHead() { - - # ... header - print("/**") >> outFile_nh; - print(" * Initialized stuff needed for sending events to an AI library instance.") >> outFile_nh; - print(" *") >> outFile_nh; - print(" * @author hoijui") >> outFile_nh; - print(" * @version GENERATED") >> outFile_nh; - print(" */") >> outFile_nh; - print("int " myWrapperPrefix "initStatic(JNIEnv* env, size_t skirmishAIId_size);") >> outFile_nh; - print("") >> outFile_nh; - # ... source - print("int " myWrapperPrefix "initStatic(JNIEnv* env, size_t _skirmishAIId_size) {") >> outFile_nc; - print("") >> outFile_nc; - print("\t" "skirmishAIId_size = _skirmishAIId_size;") >> outFile_nc; - print("\t" "skirmishAIId_callback = (jobject*) calloc(skirmishAIId_size, sizeof(jobject));") >> outFile_nc; - print("\t" "size_t t;") >> outFile_nc; - print("\t" "for (t=0; t < skirmishAIId_size; ++t) {") >> outFile_nc; - print("\t\t" "skirmishAIId_callback[t] = NULL;") >> outFile_nc; - print("\t" "}") >> outFile_nc; - print("") >> outFile_nc; - print("\t" "jobject c_aiInt = (*env)->FindClass(env, \"" myPkgD "/" myAIClass "\");") >> outFile_nc; - print("\t" "if (jniUtil_checkException(env, \"Failed fetching AI interface class " myPkgA "." myAIClass "\")) { return -2; }") >> outFile_nc; - print("") >> outFile_nc; -} - -function printNativeEventStaticInits() { - - for (e=0; e < ind_evtStructs; e++) { - printNativeEventStaticInit(e); - } - print("") >> outFile_nc; -} -function printNativeEventStaticInit(evtIndex) { - - outFile_nc = createNativeFileName(myWrapperName, 0); - - eName = evtsName[evtIndex]; - eNameLowerized = lowerize(eName); - eSignature = "("; - for (m=0; m < evtsNumMembers[evtIndex]; m++) { - type_c = evtsMembers_type_c[evtIndex, m]; - type_jni = convertCToJNIType(type_c); - type_sig = convertJNIToSignatureType(type_jni); - eSignature = eSignature type_sig; - } - eSignature = eSignature ")I"; - if (eNameLowerized == "init") { - eSignature = "(IL" myPkgD "/" myAICallbackInt ";)I"; - } - - print("\t" "m_ai_" eNameLowerized " = jniUtil_getMethodID(env, c_aiInt, \"" eNameLowerized "\", \"" eSignature "\");") >> outFile_nc; - print("\t" "if (jniUtil_checkException(env, \"Failed fetching Java AI method ID for: " eNameLowerized "\")) { return -3; }") >> outFile_nc; - print("") >> outFile_nc; -} - -function printNativeInitFunc() { - - print("\t" "return 0; // -> no error") >> outFile_nc; - print("}") >> outFile_nc; - print("") >> outFile_nc; - - # ... header - print("/**") >> outFile_nh; - print(" * Initialized stuff needed for an AI instance.") >> outFile_nh; - print(" *") >> outFile_nh; - print(" * @author hoijui") >> outFile_nh; - print(" * @version GENERATED") >> outFile_nh; - print(" */") >> outFile_nh; - print("int " myWrapperPrefix "initAI(JNIEnv* env, int skirmishAIId, jobject callback);") >> outFile_nh; - print("") >> outFile_nh; - # ... source - print("int " myWrapperPrefix "initAI(JNIEnv* env, int skirmishAIId, jobject callback) {") >> outFile_nc; - print("") >> outFile_nc; - print("\t" "int res = -1;") >> outFile_nc; - print("") >> outFile_nc; - print("\t" "skirmishAIId_callback[skirmishAIId] = callback;") >> outFile_nc; - print("\t" "res = 0;") >> outFile_nc; - print("") >> outFile_nc; - print("\t" "return res;") >> outFile_nc; - print("}") >> outFile_nc; - print("") >> outFile_nc; -} - -function printNativeHandleFuncHead() { - - # ... header - print("/**") >> outFile_nh; - print(" * For documentation, see SSkirmishAILibrary::handleEvent() in:") >> outFile_nh; - print(" * ExternalAI/Interface/SSkirmishAILibrary.h") >> outFile_nh; - print(" *") >> outFile_nh; - print(" * @author hoijui") >> outFile_nh; - print(" * @version GENERATED") >> outFile_nh; - print(" */") >> outFile_nh; - print("int " myWrapperPrefix "handleEvent(JNIEnv* env, jobject aiInstance, int skirmishAIId, int topic, const void* data);") >> outFile_nh; - print("") >> outFile_nh; - # ... source - print("int " myWrapperPrefix "handleEvent(JNIEnv* env, jobject aiInstance, int skirmishAIId, int topic, const void* data) {") >> outFile_nc; - print("") >> outFile_nc; - print("\t" "int _ret = -1;") >> outFile_nc; - print("") >> outFile_nc; -# print("\t" "jobject o_ai = skirmishAIId_aiObject[skirmishAIId];") >> outFile_nc; -# print("\t" "//assert(o_ai != NULL);") >> outFile_nc; -# print("") >> outFile_nc; - - # only add this for debugging purposes -# print("\t" "// in case we missed handling an exception elsewhere") >> outFile_nc; -# print("\t" "// and it is therefore still pending, we handle it here") >> outFile_nc; -# print("\t" "if ((*env)->ExceptionCheck(env)) {") >> outFile_nc; -# print("\t\t" "(*env)->ExceptionDescribe(env);") >> outFile_nc; -# print("\t\t" "_ret = -6;") >> outFile_nc; -# print("\t\t" "return _ret;") >> outFile_nc; -# print("\t" "}") >> outFile_nc; -# print("") >> outFile_nc; - - print("\t" "switch (topic) {") >> outFile_nc; -} - - -function printNativeEventCases() { - - for (e=0; e < ind_evtStructs; e++) { - printNativeEventCase(e); - } -} -function printNativeEventCase(evtIndex) { - - outFile_nc = createNativeFileName(myWrapperName, 0); - - topicName = evtsTopicName[evtIndex]; - topicValue = evtsTopicNameValue[topicName]; - eName = evtsName[evtIndex]; - eNameLowerized = lowerize(eName); - eStruct = "S" eName "Event"; - - print("\t\t" "case " topicName ": {") >> outFile_nc; - print("\t\t\t" "const struct " eStruct "* evt = (const struct "eStruct"*) data;") >> outFile_nc; - - paramsEvt = ""; - conversion_pre = ""; - for (m=0; m < evtsNumMembers[evtIndex]; m++) { - type_c = evtsMembers_type_c[evtIndex, m]; - name_c = evtsMembers_name[evtIndex, m]; - value_c = "evt->" name_c; - name_jni = value_c; - - type_jni = convertCToJNIType(type_c); - if (type_jni == "jstring") { - name_jni = name_c "_jni"; - conversion_pre = conversion_pre "\n\t\t\t" type_jni " " name_jni " = (*env)->NewStringUTF(env, " value_c ");"; - } else if (type_jni == "jfloatArray") { - name_jni = name_c "_jni"; - - array_size = "sizeof(" value_c ")"; - if (match(name_c, /_posF3$/)) { - array_size = "3"; - } - - conversion_pre = conversion_pre "\n\t\t\t" type_jni " " name_jni " = (*env)->NewFloatArray(env, " array_size ");"; - conversion_pre = conversion_pre "\n\t\t\t" "(*env)->SetFloatArrayRegion(env, " name_jni ", 0, " array_size ", " value_c ");"; - } else if (type_jni == "jintArray") { - name_jni = name_c "_jni"; - - array_size = "sizeof(" value_c ")"; - - conversion_pre = conversion_pre "\n\t\t\t" type_jni " " name_jni " = (*env)->NewIntArray(env, " array_size ");"; - conversion_pre = conversion_pre "\n\t\t\t" "(*env)->SetIntArrayRegion(env, " name_jni ", 0, " array_size ", " value_c ");"; - } - - paramsEvt = paramsEvt ", " name_jni; - } - sub(/^, /, "", paramsEvt); - if (eNameLowerized == "init") { - sub(/evt->callback/, "skirmishAIId_callback[evt->skirmishAIId]", paramsEvt); - } - - print(conversion_pre) >> outFile_nc; - print("\t\t\t" "_ret = (*env)->CallIntMethod(env, aiInstance, m_ai_" eNameLowerized ", " paramsEvt ");") >> outFile_nc; - - print("\t\t\t" "break;") >> outFile_nc; - print("\t\t" "}") >> outFile_nc; -} - -function printNativeEnd() { - - outFile_nh = createNativeFileName(myWrapperName, 1); - outFile_nc = createNativeFileName(myWrapperName, 0); - - print("#ifdef __cplusplus") >> outFile_nh; - print("} // extern \"C\"") >> outFile_nh; - print("#endif") >> outFile_nh; - print("") >> outFile_nh; - print("#endif // __EVENTS_JNI_BRIDGE_H") >> outFile_nh; - print("") >> outFile_nh; - - print("\t\t" "default: {") >> outFile_nc; - print("\t\t\t" "_ret = -4;") >> outFile_nc; - print("\t\t\t" "break;") >> outFile_nc; - print("\t\t" "}") >> outFile_nc; - print("\t" "}") >> outFile_nc; - print("") >> outFile_nc; - print("\t" "if ((*env)->ExceptionCheck(env)) {") >> outFile_nc; - print("\t\t" "(*env)->ExceptionDescribe(env);") >> outFile_nc; - print("\t\t" "_ret = -5;") >> outFile_nc; - print("\t" "}") >> outFile_nc; - print("") >> outFile_nc; - print("\t" "return _ret;") >> outFile_nc; - print("}") >> outFile_nc; - - close(outFile_nh); - close(outFile_nc); -} - - -function printGeneralJavaHeader(outFile_h, javaPkg_h, javaClassName_h) { - - printCommentsHeader(outFile_h); - print("") >> outFile_h; - print("package " javaPkg_h ";") >> outFile_h; - print("") >> outFile_h; - print("") >> outFile_h; - print("/**") >> outFile_h; - print(" * This is the Java entry point from events coming from the engine.") >> outFile_h; - print(" * We are using JNI for best in speed.") >> outFile_h; - print(" *") >> outFile_h; - print(" * @author AWK wrapper script") >> outFile_h; - print(" * @version GENERATED") >> outFile_h; - print(" */") >> outFile_h; - if (javaClassName_h == myAIAbstractClass) { - print("public abstract class " javaClassName_h " implements " myAIClass " {") >> outFile_h; - } else { - print("public interface " javaClassName_h " {") >> outFile_h; - } - print("") >> outFile_h; -} - -function printJavaHeader() { - - outFile_i = createJavaFileName(myAIClass); - outFile_a = createJavaFileName(myAIAbstractClass); - - printGeneralJavaHeader(outFile_i, myPkgA, myAIClass); - printGeneralJavaHeader(outFile_a, myPkgA, myAIAbstractClass); -} - -function printJavaEvents() { - - for (e=0; e < ind_evtStructs; e++) { - printJavaEvent(e); - } -} -function printJavaEvent(evtIndex) { - - outFile_i = createJavaFileName(myAIClass); - outFile_a = createJavaFileName(myAIAbstractClass); - - topicName = evtsTopicName[evtIndex]; - topicValue = evtsTopicNameValue[topicName]; - eName = evtsName[evtIndex]; - eMetaComment = evtsMetaComment[evtIndex]; - eNameLowerized = lowerize(eName); - eCls = eName "AIEvent"; - - # Add the member comments to the main comment as @param attributes - numEvtDocLines = evtsDocComment[evtIndex, "*"]; - for (m=firstMember; m < evtsNumMembers[evtIndex]; m++) { - numLines = evtMbrsDocComments[evtIndex*1000 + m, "*"]; - if (numLines > 0) { - evtsDocComment[evtIndex, numEvtDocLines] = "@param " evtsMembers_name[evtIndex, m]; - } - for (l=0; l < numLines; l++) { - if (l == 0) { - _preDocLine = "@param " evtsMembers_name[evtIndex, m] " "; - } else { - _preDocLine = " " lengtAsSpaces(evtsMembers_name[evtIndex, m]) " "; - } - evtsDocComment[evtIndex, numEvtDocLines] = _preDocLine evtMbrsDocComments[evtIndex*1000 + m, l]; - numEvtDocLines++; - } - } - evtsDocComment[evtIndex, "*"] = numEvtDocLines; - - paramsTypes = ""; - for (m=0; m < evtsNumMembers[evtIndex]; m++) { - name_c = evtsMembers_name[evtIndex, m]; - type_c = evtsMembers_type_c[evtIndex, m]; - type_java = convertJNIToJavaType(convertCToJNIType(type_c)); - - paramsTypes = paramsTypes ", " type_java " " name_c; - } - sub(/^, /, "", paramsTypes); - if (eNameLowerized == "init") { - paramsTypes = "int skirmishAIId, AICallback callback"; - } - - _condMetaComments = eMetaComment; - if (_condMetaComments != "") { - _condMetaComments = " // " _condMetaComments; - } - - print("") >> outFile_i; - printFunctionComment_Common(outFile_i, evtsDocComment, evtIndex, "\t"); - print("\t" "public int " eNameLowerized "(" paramsTypes ");" _condMetaComments) >> outFile_i; - - print("") >> outFile_a; - print("\t" "@Override") >> outFile_a; - print("\t" "public int " eNameLowerized "(" paramsTypes ") {") >> outFile_a; - print("") >> outFile_a; - print("\t\t" "// signal: event handled OK") >> outFile_a; - print("\t\t" "return 0;") >> outFile_a; - print("\t" "}") >> outFile_a; -} - -function printJavaEnd() { - - outFile_i = createJavaFileName(myAIClass); - outFile_a = createJavaFileName(myAIAbstractClass); - - print("}") >> outFile_i; - print("") >> outFile_i; - - print("}") >> outFile_a; - print("") >> outFile_a; - - close(outFile_i); - close(outFile_a); -} - - -function saveMember(ind_mem_s, member_s) { - - name_s = extractParamName(member_s); - type_c_s = extractCType(member_s); - - evtsMembers_name[ind_evtStructs, ind_mem_s] = name_s; - evtsMembers_type_c[ind_evtStructs, ind_mem_s] = type_c_s; -} - - -# aggare te los event defines in order -/^[ \t]*EVENT_.*$/ { - - doWrapp = !match(($0), /.*EVENT_NULL.*/) && !match(($0), /.*EVENT_TO_ID_ENGINE.*/); - if (doWrapp) { - sub(",", "", $4); - evtsTopicNameValue[$2] = $4; - } else { - print("Java-AIInterface: NOTE: JNI level: Events: intentionally not wrapped: " $2); - } -} - - -# This function has to return true (1) if a doc comment (eg: /** foo bar */) -# can be deleted. -# If there is no special condition you want to apply, -# it should always return true (1), -# cause there are additional mechanism to prevent accidental deleting. -# see: commonDoc.awk -function canDeleteDocumentation() { - return isInsideEvtStruct != 1; -} - -################################################################################ -### BEGIN: parsing and saving the event structs - -# end of struct S*Event -/^}; \/\/\$ EVENT_/ { - - evtsNumMembers[ind_evtStructs] = ind_evtMember; - evtsTopicName[ind_evtStructs] = $3; - _metaComment = $0; - sub("^.*" evtsTopicName[ind_evtStructs], "", _metaComment); - evtsMetaComment[ind_evtStructs] = trim(_metaComment); - - ind_evtStructs++; - isInsideEvtStruct = 0; -} - - -# inside of struct S*Event -{ - if (isInsideEvtStruct == 1) { - size_tmpMembers = split($0, tmpMembers, ";"); - # cause there is an empty part behind the ';' - size_tmpMembers--; - for (i=1; i<=size_tmpMembers; i++) { - tmpMembers[i] = trim(tmpMembers[i]); - if (tmpMembers[i] == "" || match(tmpMembers[i], /^\/\//)) { - break; - } - # This would bork with more then 1000 members in an event, - # or more then 1000 events - storeDocLines(evtMbrsDocComments, ind_evtStructs*1000 + ind_evtMember); - saveMember(ind_evtMember, tmpMembers[i]); - ind_evtMember++; - } - } -} - -# beginning of struct S*Event -/^struct S.*Event( \{)?/ { - - isInsideEvtStruct = 1; - ind_evtMember = 0; - eventName = $2; - sub(/^S/, "", eventName); - sub(/Event$/, "", eventName); - - evtsName[ind_evtStructs] = eventName; - storeDocLines(evtsDocComment, ind_evtStructs); -} - -### END: parsing and saving the event structs -################################################################################ - - - - -END { - # finalize things - printNativeHeader(); - printNativeEventMethodVars(); - printNativeStaticInitFuncHead(); - printNativeEventStaticInits(); - printNativeInitFunc(); - printNativeHandleFuncHead(); - printNativeEventCases(); - printNativeEnd(); - - printJavaHeader(); - printJavaEvents(); - printJavaEnd(); -} diff --git a/AI/Interfaces/Java/bin/native_createCallbackFPInitializations.awk b/AI/Interfaces/Java/bin/native_createCallbackFPInitializations.awk deleted file mode 100755 index c6aed9bdeef..00000000000 --- a/AI/Interfaces/Java/bin/native_createCallbackFPInitializations.awk +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/awk -f -# -# This awk script creates the function pointer initializations -# for the C callbacks; eg: -# rts/ExternalAI/Interface/SSkirmishAICallback.h -# rts/ExternalAI/Interface/SAIInterfaceCallback.h -# - -BEGIN { - # initialize things - - # define the field splitter(-regex) - #FS="[ \t]+" -} - - -# Some utility functions - -function ltrim(s) { sub(/^[ \t]+/, "", s); return s; } -function rtrim(s) { sub(/[ \t]+$/, "", s); return s; } -function trim(s) { return rtrim(ltrim(s)); } - - -function printInit(functionPointerName) { - - functionName = functionPointerName; - sub(/Clb_/, "skirmishAiCallback_", functionName); - - print(" callback->" functionPointerName " = &" functionName ";") -} - - -/^.*CALLING_CONV.*$/ { - - line = trim($0); - doWrapp = !match(line, /^[ \t]*\/\/.*/); - if (doWrapp) { - numParts = split(line, parts, /\(CALLING_CONV \*/); - if (numParts == 2) { - numParts2 = split(parts[2], parts2, /\)\(/); - if (numParts2 == 2) { - fn = parts2[1]; - printInit(fn); - } - } - } -} - - - -END { - # finalize things -} diff --git a/AI/Interfaces/Java/bin/native_createCallbackFunctionImpls.awk b/AI/Interfaces/Java/bin/native_createCallbackFunctionImpls.awk deleted file mode 100755 index c9a789ab005..00000000000 --- a/AI/Interfaces/Java/bin/native_createCallbackFunctionImpls.awk +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/awk -f -# -# This awk script creates function impls that can be used in the C callbacks -# with some little adjustments; eg: -# rts/ExternalAI/SSkirmishAICallbackImpl.cpp -# rts/ExternalAI/SAIInterfaceCallbackImpl.cpp -# -# Accepts input like this: -# [code] -# bool requireSonarUnderWater; -# -# // construction related fields -# /// Should constructions without builders decay? -# bool constructionDecay; -# [/code] -# -# use like this: -# awk -f yourScript.awk -f common.awk -f commonDoc.awk [additional-params] -# this should work with all flavours of AWK (eg. gawk, mawk, nawk, ...) -# - -BEGIN { - # initialize things - - # define the field splitter(-regex) - #FS="[ \t]+" - - # 0 -> print impl - # 1 -> print impl header - # 2 -> print interface header - printingHeader = 1; -} - -function getCType(gct_type) { - - if (match(gct_type, /string/)) { - return "const char* const"; - } else { - return gct_type; - } -} -function getToCTypeConv(gtctc_type) { - - if (match(gtctc_type, /string/)) { - return ".c_str()"; - } else { - return ""; - } -} -function getFuncPrefix(gfp_type) { - - #if (match(gfp_type, /bool/)) { - # return "is"; - #} else { - return "get"; - #} -} - -function printFunc(pf_fieldName, pf_type, pf_header) { - - pf_cType = getCType(pf_type); - pf_toCTypeConv = getToCTypeConv(pf_type); - pf_funcPrefix = getFuncPrefix(pf_type); - - if (pf_header == 2 && hasStoredDoc()) { - printStoredDoc(""); - } - - if (pf_header != 0) { - pf_firstLineEnd = ";"; - pf_sizeToFill = length("const char* const") - length(pf_cType); - if (pf_sizeToFill < 0) { - pf_sizeToFill = 0; - } - pf_filler = ""; - for (pf_i = 0; pf_i < pf_sizeToFill; pf_i++) { - pf_filler = pf_filler " "; - } - } else { - pf_firstLineEnd = " {"; - pf_filler = ""; - } - - if (pf_header == 2) { - print(pf_cType pf_filler " (CALLING_CONV *PreFixName_" pf_funcPrefix capitalize(pf_fieldName) ")(int teamId)" pf_firstLineEnd); - } else { - print("EXPORT(" pf_cType pf_filler ") PreFixName_" pf_funcPrefix capitalize(pf_fieldName) "(int teamId)" pf_firstLineEnd); - } - if (!pf_header) { - print("\t" "return modInfo->" pf_fieldName pf_toCTypeConv ";"); - print("}"); - } -} - -# needed by commonDoc.awk -function canDeleteDocumentation() { - return 1; -} - - -{ - line = trim($0); - - if (line == "") { - print(line); - } else if (!isInsideDoc()) { - sub(/\;$/, "", line); - - name = line; - sub(/^.*[ \t]/, "", name); - - cppType = line; - sub(name, "", cppType); - cppType = rtrim(cppType); - - printFunc(name, cppType, printingHeader); - } -} - - - -END { - # finalize things -} diff --git a/AI/Interfaces/Java/bin/native_wrappCallback.awk b/AI/Interfaces/Java/bin/native_wrappCallback.awk deleted file mode 100755 index b920c196a6e..00000000000 --- a/AI/Interfaces/Java/bin/native_wrappCallback.awk +++ /dev/null @@ -1,290 +0,0 @@ -#!/usr/bin/awk -f -# -# This awk script creates C functions which call C function pointers in: -# rts/ExternalAI/Interface/SSkirmishAICallback.h -# -# Right after running this script, you have to wrap the native command structs -# into functions. -# -# This script uses functions from the following files: -# * common.awk -# * commonDoc.awk -# Variables that can be set on the command-line (with -v): -# * GENERATED_SOURCE_DIR the root generated sources dir -# default: "../src-generated/main" -# * NATIVE_GENERATED_SOURCE_DIR the native generated sources dir -# default: GENERATED_SOURCE_DIR + "/native" -# -# usage: -# awk -f thisScript.awk -f common.awk -f commonDoc.awk -# awk -f thisScript.awk -f common.awk -f commonDoc.awk \ -# -v 'NATIVE_GENERATED_SOURCE_DIR=/tmp/build/AI/Interfaces/Java/src-generated/main/native' -# - -BEGIN { - # initialize things - - # define the field splitter(-regex) - FS = "(,)|(\\()|(\\);)"; - - # These vars can be assigned externally, see file header. - # Set the default values if they were not supplied on the command line. - if (!GENERATED_SOURCE_DIR) { - GENERATED_SOURCE_DIR = "../src-generated/main"; - } - if (!NATIVE_GENERATED_SOURCE_DIR) { - NATIVE_GENERATED_SOURCE_DIR = GENERATED_SOURCE_DIR "/native"; - } - - nativeBridge = "CallbackFunctionPointerBridge"; - bridgePrefix = "bridged__"; - - fi = 0; -} - -function doWrapp(funcIndex_dw) { - - paramListC_dw = funcParamListC[funcIndex_dw]; - doWrapp_dw = 1; - - fullName_dw = funcFullName[funcIndex_dw]; - if (doWrapp_dw) { - metaInf_dw = funcMetaInf[funcIndex_dw]; - - if (match(metaInf_dw, /ARRAY:/)) { - #doWrapp_dw = 0; - } - if (match(metaInf_dw, /MAP:/)) { - #doWrapp_dw = 0; - } - if (match(fullName_dw, "^" bridgePrefix "File_")) { - doWrapp_dw = 0; - } - if (fullName_dw == "Engine_handleCommand") { - doWrapp_dw = 0; - } - # not wrapped, legacy C++ only - if (fullName_dw == "Engine_executeCommand") { - doWrapp_dw = 0; - } - } else { - print("Java-AIInterface: NOTE: native level: Callback: intentionally not wrapped: " fullName_dw); - } - - return doWrapp_dw; -} - -function createNativeFileName(fileName_fn, isHeader_fn) { - - absFileName_fn = NATIVE_GENERATED_SOURCE_DIR "/" fileName_fn; - if (isHeader_fn) { - absFileName_fn = absFileName_fn ".h"; - } else { - absFileName_fn = absFileName_fn ".c"; - } - - return absFileName_fn; -} - -function printNativeFP2F() { - - outFile_nh = createNativeFileName(nativeBridge, 1); - outFile_nc = createNativeFileName(nativeBridge, 0); - - printCommentsHeader(outFile_nh); - printCommentsHeader(outFile_nc); - - print("") >> outFile_nh; - print("#ifndef __CALLBACK_FUNCTION_POINTER_BRIDGE_H") >> outFile_nh; - print("#define __CALLBACK_FUNCTION_POINTER_BRIDGE_H") >> outFile_nh; - print("") >> outFile_nh; - print("#include \"ExternalAI/Interface/aidefines.h\"") >> outFile_nh; - print("") >> outFile_nh; - print("#include // size_t") >> outFile_nh; - print("#include // bool, true, false") >> outFile_nh; - print("") >> outFile_nh; - print("struct SSkirmishAICallback;") >> outFile_nh; - print("") >> outFile_nh; - print("#ifdef __cplusplus") >> outFile_nh; - print("extern \"C\" {") >> outFile_nh; - print("#endif") >> outFile_nh; - print("") >> outFile_nh; - print("void funcPntBrdg_addCallback(const size_t skirmishAIId, const struct SSkirmishAICallback* clb);") >> outFile_nh; - print("void funcPntBrdg_removeCallback(const size_t skirmishAIId);") >> outFile_nh; - print("") >> outFile_nh; - - print("") >> outFile_nc; - print("#include \"" nativeBridge ".h\"") >> outFile_nc; - print("") >> outFile_nc; - print("#include \"ExternalAI/Interface/SSkirmishAICallback.h\"") >> outFile_nc; - print("#include \"ExternalAI/Interface/AISCommands.h\"") >> outFile_nc; - print("") >> outFile_nc; - print("") >> outFile_nc; - print("#define id_clb_sizeMax 8192") >> outFile_nc; - print("static const struct SSkirmishAICallback* id_clb[id_clb_sizeMax];") >> outFile_nc; - print("") >> outFile_nc; - print("void funcPntBrdg_addCallback(const size_t skirmishAIId, const struct SSkirmishAICallback* clb) {") >> outFile_nc; - print(" //assert(skirmishAIId < id_clb_sizeMax);") >> outFile_nc; - print(" id_clb[skirmishAIId] = clb;") >> outFile_nc; - print("}") >> outFile_nc; - print("void funcPntBrdg_removeCallback(const size_t skirmishAIId) {") >> outFile_nc; - print(" //assert(skirmishAIId < id_clb_sizeMax);") >> outFile_nc; - print(" id_clb[skirmishAIId] = NULL;") >> outFile_nc; - print("}") >> outFile_nc; - print("") >> outFile_nc; - - # print the wrapping functions - for (i=0; i < fi; i++) { - fullName = funcFullName[i]; - retType = funcRetTypeC[i]; - paramList = funcParamListC[i]; - sub(/int skirmishAIId/, "int _skirmishAIId", paramList); - paramListNoTypes = removeParamTypes(paramList); - commentEol = funcCommentEol[i]; - - if (doWrapp(i)) { - # print function declaration to *.h - printFunctionComment_Common(outFile_nh, funcDocComment, i, ""); - - outName = bridgePrefix fullName; - - if (commentEol != "") { - commentEol = " // " commentEol; - } - print("EXPORT(" retType ") " outName "(" paramList ");" commentEol) >> outFile_nh; - print("") >> outFile_nh; - - # print function definition to *.c - print("EXPORT(" retType ") " outName "(" paramList ") {") >> outFile_nc; - - print(preConversion) >> outFile_nc; - if (hasRetParam) { - print("\t" retParamType " " retNameTmp " = id_clb[_skirmishAIId]->" fullName "(" paramListNoTypes ");") >> outFile_nc; - print(retParamConversion) >> outFile_nc; - } else { - condRet = "return "; - if (retType == "void") { - condRet = ""; - } - print("\t" condRet "id_clb[_skirmishAIId]->" fullName "(" paramListNoTypes ");") >> outFile_nc; - } - print("" "}") >> outFile_nc; - } else { - print("Note: The following function is intentionally not wrapped: " fullName); - } - } - - - print("") >> outFile_nh; - print("") >> outFile_nc; - - close(outFile_nh); - close(outFile_nc); -} - - -function wrappFunction(funcDef, commentEolTot) { - - doParse = 1; # add a function in case you want to exclude some - - if (doParse) { - size_funcParts = split(funcDef, funcParts, "(,)|(\\()|(\\);)"); - # because the empty part after ");" would count as part as well - size_funcParts--; - retType_c = trim(funcParts[1]); - fullName = funcParts[2]; - sub(/CALLING_CONV \*/, "", fullName); - sub(/\)/, "", fullName); - - # function parameters - paramList_c = ""; - paramList = ""; - - for (i=3; i<=size_funcParts && !match(funcParts[i], /.*\/\/.*/); i++) { - type_c = extractParamType(funcParts[i]); - type_c = cleanupCType(type_c); - name = extractParamName(funcParts[i]); - if (i == 3) { - cond_comma = ""; - } else { - cond_comma = ", "; - } - paramList_c = paramList_c cond_comma type_c " " name; - } - - funcFullName[fi] = fullName; - funcRetTypeC[fi] = retType_c; - funcParamListC[fi] = paramList_c; - funcParamList[fi] = paramList; - funcCommentEol[fi] = trim(commentEolTot); - storeDocLines(funcDocComment, fi); - fi++; - } else { - print("warninig: function intentionally NOT wrapped: " funcDef); - } -} - - - -# This function has to return true (1) if a doc comment (eg: /** foo bar */) -# can be deleted. -# If there is no special condition you want to apply, -# it should always return true (1), -# cause there are additional mechanism to prevent accidental deleting. -# see: commonDoc.awk -function canDeleteDocumentation() { - return isMultiLineFunc != 1; -} - - - -# save function pointer info into arrays -# ... 2nd, 3rd, ... line of a function pointer definition -{ - if (isMultiLineFunc) { # function is defined on one single line - funcIntermLine = $0; - # separate possible comment at end of line: //$ foo bar - commentEol = funcIntermLine; - if (sub(/.*\/\/\$/, "", commentEol)) { - commentEolTot = commentEolTot commentEol; - } - # remove possible comment at end of line: //$ foo bar - sub(/[ \t]*\/\/.*/, "", funcIntermLine); - funcIntermLine = trim(funcIntermLine); - funcSoFar = funcSoFar " " funcIntermLine; - if (match(funcSoFar, /;$/)) { - # function ends in this line - wrappFunction(funcSoFar, commentEolTot); - isMultiLineFunc = 0; - } - } -} -# 1st line of a function pointer definition -/^[^\/]*CALLING_CONV.*$/ { - - funcStartLine = $0; - # separate possible comment at end of line: //$ foo bar - commentEolTot = ""; - commentEol = funcStartLine; - if (sub(/.*\/\/\$/, "", commentEol)) { - commentEolTot = commentEolTot commentEol; - } - # remove possible comment at end of line: //$ foo bar - sub(/\/\/.*$/, "", funcStartLine); - funcStartLine = trim(funcStartLine); - if (match(funcStartLine, /;$/)) { - # function ends in this line - wrappFunction(funcStartLine, commentEolTot); - } else { - funcSoFar = funcStartLine; - isMultiLineFunc = 1; - } -} - - - -END { - # finalize things - - printNativeFP2F(); -} diff --git a/AI/Interfaces/Java/bin/native_wrappCommands.awk b/AI/Interfaces/Java/bin/native_wrappCommands.awk deleted file mode 100755 index 9de74c4a95f..00000000000 --- a/AI/Interfaces/Java/bin/native_wrappCommands.awk +++ /dev/null @@ -1,384 +0,0 @@ -#!/usr/bin/awk -f -# -# This awk script creates the C functions for wrapping the C command structs in: -# rts/ExternalAI/Interface/AISCommands.h -# -# Before running this script, you have to wrap the native callback struct -# into functions. -# -# This script uses functions from the following files: -# * common.awk -# * commonDoc.awk -# Variables that can be set on the command-line (with -v): -# * GENERATED_SOURCE_DIR the root generated sources dir -# default: "../src-generated/main" -# * NATIVE_GENERATED_SOURCE_DIR the native generated sources dir -# default: GENERATED_SOURCE_DIR + "/native" -# -# usage: -# awk -f thisScript.awk -f common.awk -f commonDoc.awk -# awk -f thisScript.awk -f common.awk -f commonDoc.awk \ -# -v 'NATIVE_GENERATED_SOURCE_DIR=/tmp/build/AI/Interfaces/Java/src-generated/main/native' -# - -BEGIN { - # initialize things - - # define the field splitter(-regex) - FS = "[ \t]+"; - - # These vars can be assigned externally, see file header. - # Set the default values if they were not supplied on the command line. - if (!GENERATED_SOURCE_DIR) { - GENERATED_SOURCE_DIR = "../src-generated/main"; - } - if (!NATIVE_GENERATED_SOURCE_DIR) { - NATIVE_GENERATED_SOURCE_DIR = GENERATED_SOURCE_DIR "/native"; - } - - nativeBridge = "CallbackFunctionPointerBridge"; - bridgePrefix = "bridged__"; - - indent = " "; - - ind_cmdTopics = 0; - ind_cmdStructs = 0; - insideCmdStruct = 0; -} - - -# Checks if a field is available and is no comment -function isFieldUsable(f) { - - valid = 0; - - if (f && !match(f, /.*\/\/.*/)) { - valid = 1; - } - - return valid; -} - - - - -function saveMember(ind_mem, member) { - - name = extractParamName(member); - type_c = extractCType(member); - - cmdsMembers_name[ind_cmdStructs, ind_mem] = name; - cmdsMembers_type_c[ind_cmdStructs, ind_mem] = type_c; -} - - -function doWrapp(ind_cmdStructs_dw) { - - doWrp_dw = 1; - - if (match(cmdsName[ind_cmdStructs_dw], /SharedMemArea/)) { - doWrp_dw = 0; - } else if (match(cmdsName[ind_cmdStructs_dw], /^SCallLuaRulesCommand$/)) { - doWrp_dw = 0; - } - - return doWrp_dw; -} - - -function createNativeFileName(fileName_fn, isHeader_fn) { - - absFileName_fn = NATIVE_GENERATED_SOURCE_DIR "/" fileName_fn; - if (isHeader_fn) { - absFileName_fn = absFileName_fn ".h"; - } else { - absFileName_fn = absFileName_fn ".c"; - } - - return absFileName_fn; -} - - -function printNativeFP2F() { - - outFile_nh = createNativeFileName(nativeBridge, 1); - outFile_nc = createNativeFileName(nativeBridge, 0); - - print("// END: COMMAND_WRAPPERS") >> outFile_nh; - print("") >> outFile_nh; - print("") >> outFile_nc; - - # print the command wrapping functions - for (cmdIndex=0; cmdIndex < ind_cmdStructs; cmdIndex++) { - topicName = cmdsTopicName[cmdIndex]; - topicValue = cmdsTopicNameValue[topicName]; - name = cmdsName[cmdIndex]; - metaInf = cmdsMetaInfo[cmdIndex]; - fullName = metaInf; - sub(/ .*$/, "", fullName); - sub(/^[^ \t]*/, "", metaInf); - - hasRetType = 0; - retType = "int"; - retParam = ""; - paramList = "int _skirmishAIId"; - firstMember = 0; - if (cmdsNumMembers[cmdIndex] > 0) { - for (m=firstMember; m < cmdsNumMembers[cmdIndex]; m++) { - memName = cmdsMembers_name[cmdIndex, m]; - memType_c = cmdsMembers_type_c[cmdIndex, m]; - - if (match(memName, /^ret_/) && !match(memType_c, /\*/)) { - retParam = memName; - retType = memType_c; - hasRetType = 1; - # rewrite the meta info - sub(memName, "RETURN", metaInf); - } else { - paramList = paramList ", " memType_c " " memName; - } - } - } - if (!hasRetType) { - metaInf = metaInf " error-return:0=OK"; - } - paramListNoTypes = removeParamTypes(paramList); - metaInf = trim(metaInf); - - - if (doWrapp(cmdIndex)) { - # print function declaration to *.h - - # Add the member comments to the main comment as @param attributes - numCmdDocLines = cmdsDocComment[cmdIndex, "*"]; - for (m=firstMember; m < cmdsNumMembers[cmdIndex]; m++) { - numLines = cmdMbrsDocComments[cmdIndex*1000 + m, "*"]; - if (numLines > 0) { - cmdsDocComment[cmdIndex, numCmdDocLines] = "@param " cmdsMembers_name[cmdIndex, m]; - } - for (l=0; l < numLines; l++) { - if (l == 0) { - _preDocLine = "@param " cmdsMembers_name[cmdIndex, m] " "; - } else { - _preDocLine = " " lengtAsSpaces(cmdsMembers_name[cmdIndex, m]) " "; - } - cmdsDocComment[cmdIndex, numCmdDocLines] = _preDocLine cmdMbrsDocComments[cmdIndex*1000 + m, l]; - numCmdDocLines++; - } - } - cmdsDocComment[cmdIndex, "*"] = numCmdDocLines; - - outName = bridgePrefix fullName; - - commentEol = ""; - if (metaInf != "") { - commentEol = " // " metaInf; - } - - if (match(fullName, /^Unit_/)) { - # To make this fit in smoothly with the OO structure, - # we want to present each unit command once for the unit class - # and once for the Group class. - - # An other thing we do, is move the common UnitAICommand params - # to the end of the params list, so we can supply default values - # for them more easily later on. - paramList_commonEnd = paramList; - sub(/, short options, int timeOut/, "", paramList_commonEnd); - paramList_commonEnd = paramList_commonEnd ", short options, int timeOut"; - - # Unit version: - paramList_unit = paramList_commonEnd; - sub(/int groupId, /, "", paramList_unit); - printFunctionComment_Common(outFile_nh, cmdsDocComment, cmdIndex, ""); - print("EXPORT(" retType ") " outName "(" paramList_unit ");" commentEol) >> outFile_nh; - print("") >> outFile_nh; - - # Group version: - paramList_group = paramList_commonEnd; - sub(/int unitId, /, "", paramList_group); - outName_group = outName; - sub(/Unit_/, "Group_", outName_group); - printFunctionComment_Common(outFile_nh, cmdsDocComment, cmdIndex, ""); - print("EXPORT(" retType ") " outName_group "(" paramList_group ");" commentEol) >> outFile_nh; - } else { - printFunctionComment_Common(outFile_nh, cmdsDocComment, cmdIndex, ""); - print("EXPORT(" retType ") " outName "(" paramList ");" commentEol) >> outFile_nh; - } - print("") >> outFile_nh; - - # print function definition to *.c - print("") >> outFile_nc; - if (match(fullName, /^Unit_/)) { - # inner version: - print("static " retType " _" outName "(" paramList ") {") >> outFile_nc; - } else { - print("EXPORT(" retType ") " outName "(" paramList ") {") >> outFile_nc; - } - print("") >> outFile_nc; - - print("\t" "struct S" name "Command commandData;") >> outFile_nc; - for (m=firstMember; m < cmdsNumMembers[cmdIndex]; m++) { - memName = cmdsMembers_name[cmdIndex, m]; - memType_c = cmdsMembers_type_c[cmdIndex, m]; - - if (memName == retParam) { - # do nothing - } else { - print("\t" "commandData." memName " = " memName ";") >> outFile_nc; - } - } - print("") >> outFile_nc; - - print("\t" "int _ret = id_clb[_skirmishAIId]->Engine_handleCommand(_skirmishAIId, COMMAND_TO_ID_ENGINE, -1, " topicName ", &commandData);") >> outFile_nc; - print("") >> outFile_nc; - - if (hasRetType) { - # this is unused, delete - print("\t" "if (_ret == 0) {") >> outFile_nc; - print("\t\t" "_ret = commandData." retParam ";") >> outFile_nc; - print("\t" "} else {") >> outFile_nc; - print("\t\t" "_ret = 0;") >> outFile_nc; - print("\t" "}") >> outFile_nc; - } - - print("\t" "return _ret;") >> outFile_nc; - print("}") >> outFile_nc; - - if (match(fullName, /^Unit_/)) { - paramListNoTypes = removeParamTypes(paramList); - - # Unit version: - print("") >> outFile_nc; - print("EXPORT(" retType ") " outName "(" paramList_unit ") {" commentEol) >> outFile_nc; - print("") >> outFile_nc; - print("\t" "const int groupId = -1;") >> outFile_nc; - print("\t" "return _" outName "(" paramListNoTypes ");") >> outFile_nc; - print("}") >> outFile_nc; - print("") >> outFile_nc; - - # Group version: - print("EXPORT(" retType ") " outName_group "(" paramList_group ") {" commentEol) >> outFile_nc; - print("") >> outFile_nc; - print("\t" "const int unitId = -1;") >> outFile_nc; - print("\t" "return _" outName "(" paramListNoTypes ");") >> outFile_nc; - print("}") >> outFile_nc; - } - } else { - print("Java-AIInterface: NOTE: native level: Commands: intentionally not wrapped: " fullName); - } - } - - print("// END: COMMAND_WRAPPERS") >> outFile_nh; - print("") >> outFile_nh; - print("#ifdef __cplusplus") >> outFile_nh; - print("} // extern \"C\"") >> outFile_nh; - print("#endif") >> outFile_nh; - print("") >> outFile_nh; - print("#endif // __CALLBACK_FUNCTION_POINTER_BRIDGE_H") >> outFile_nh; - print("") >> outFile_nh; - - print("") >> outFile_nc; - - close(outFile_nh); - close(outFile_nc); -} - - - - - - -# aggare te los command defines in order -/^[ \t]*COMMAND_.*$/ { - - sub(",", "", $4); - cmdsTopicNameValue[$2] = $4; -} - - - -# This function has to return true (1) if a doc comment (eg: /** foo bar */) -# can be deleted. -# If there is no special condition you want to apply, -# it should always return true (1), -# cause there are additional mechanism to prevent accidental deleting. -# see: commonDoc.awk -function canDeleteDocumentation() { - return isInsideCmdStruct != 1; -} - -################################################################################ -### BEGIN: parsing and saving the command structs - -# end of struct S*Command -/^}; \/\/\$ COMMAND_.*$/ { - - cmdsNumMembers[ind_cmdStructs] = ind_cmdMember; - cmdsTopicName[ind_cmdStructs] = $3; - metaInfo = $0; - sub(/^}; \/\/\$ COMMAND_[^ \t]+/, "", metaInfo); - metaInfo = trim(metaInfo); - cmdsMetaInfo[ind_cmdStructs] = metaInfo; - - if (doWrapp(ind_cmdStructs)) { - #printCommandJava(ind_cmdStructs); - } - - ind_cmdStructs++; - isInsideCmdStruct = 0; -} - - -# inside of struct S*Command -{ - if (isInsideCmdStruct == 1) { - size_tmpMembers = split($0, tmpMembers, ";"); - # cause there is an empty part behind the ';' - size_tmpMembers--; - for (i=1; i<=size_tmpMembers; i++) { - tmpMembers[i] = trim(tmpMembers[i]); - if (tmpMembers[i] == "" || match(tmpMembers[i], /^\/\//)) { - break; - } - # This would bork with more then 1000 members in a command, - # or more then 1000 commands - storeDocLines(cmdMbrsDocComments, ind_cmdStructs*1000 + ind_cmdMember); - saveMember(ind_cmdMember, tmpMembers[i]); - ind_cmdMember++; - } - } -} - -# beginning of struct S*Command -/^struct S.*Command( \{)?/ { - - isInsideCmdStruct = 1; - ind_cmdMember = 0; - commandName = $2; - sub(/^S/, "", commandName); - sub(/Command$/, "", commandName); - - isUnitCommand = match(commandName, /.*Unit$/); - - cmdsIsUnitCmd[ind_cmdStructs] = isUnitCommand; - cmdsName[ind_cmdStructs] = commandName; - storeDocLines(cmdsDocComment, ind_cmdStructs); -} - -# find COMMAND_TO_ID_ENGINE id -/COMMAND_TO_ID_ENGINE/ { - - cmdToIdEngine = $3; -} - -### END: parsing and saving the command structs -################################################################################ - - - -END { - # finalize things - - printNativeFP2F() -} diff --git a/AI/Interfaces/Java/data/InterfaceInfo.lua b/AI/Interfaces/Java/data/InterfaceInfo.lua deleted file mode 100644 index 13b586912a8..00000000000 --- a/AI/Interfaces/Java/data/InterfaceInfo.lua +++ /dev/null @@ -1,51 +0,0 @@ --- --- Info Definition Table format --- --- --- These keywords must be lowercase for LuaParser to read them. --- --- key: user defined or one of the AI_INTERFACE_PROPERTY_* defines in --- SAIInterfaceLibrary.h --- value: the value of the property --- desc: the description (could be used as a tooltip) --- --- --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -local infos = { - { - key = 'shortName', - value = 'Java', - desc = 'machine conform name.', - }, - { - key = 'version', - value = '0.1', - }, - { - key = 'name', - value = 'default Java AI Interface', - desc = 'human readable name.', - }, - { - key = 'description', - value = 'This interface is needed for Java AIs', - desc = 'tooltip.', - }, - { - key = 'url', - value = 'https://springrts.com/wiki/AIInterface:Java', - desc = 'URL with more detailed info about the AI', - }, - { - key = 'supportedLanguages', - value = 'Java (possily Groovy, JRuby, ...)', - }, - { - key = 'supportsLookup', - value = 'false', - }, -} - -return infos diff --git a/AI/Interfaces/Java/data/jvm.properties b/AI/Interfaces/Java/data/jvm.properties deleted file mode 100644 index a9642fc78ed..00000000000 --- a/AI/Interfaces/Java/data/jvm.properties +++ /dev/null @@ -1,86 +0,0 @@ -# These options are passed to the JVM instance in which all Java AIs run in. -# CAUTION: Please only change these settings if you know what you are doing!! -# For a list of possible options, please see: -# http://blogs.sun.com/watt/resource/jvm-options-list.html -# and: -# http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp -# If you use do not use the SUN JVM, -# the supported options may differ from this list. -# Each line that does not start with '#' or ';' and contains -# other characters then whitespaces is taken as an option. -# -# NOTE: When specifying relative paths to files (eg. log files), -# be aware that these files will likely end up in springs writable data dir, -# though this is not guaranteed, which means you may have to search for them -# in the CWD of the JVM, which differs between platforms. -# -# NOTE: When specifying paths to files (eg. log files), -# you can make use of ${home-dir}, which will be replaced with something like: -# "{abs-path-to-spring-writable-data-dir}/AI/Interfaces/Java/0.1" -# - -# NOTE: only really useful for debugging -# false: crash and report error when a JVM option was specified, -# that is unknown to the used JVM -# default: true -;jvm.arguments.ignoreUnrecognized=false - -# NOTE: change this if you need fancy stuff only -# specify as hex value, eg. 0x00010004 is JNI_VERSION_1_4 -# see jni.h (look for JNI_VERSION_* defines) -# default: 0x00010004 -;jvm.jni.version=0x00010002 - - -# NOTE: this should not be used, as it is auto-generated -;jvm.option.java.class.path=... -# you may simply store .jar and .class files in the jlib/ folder -# of the Java AI Interface or your Java Skirmish AI. -# the value of this property is appended to the generated java.class.path - -# NOTE: you shall NOT specify the following, as they are auto-generated: -;jvm.option.java.library.path=... -# simply store .dll, .so or .dylib files in the lib/ folder -# of the Java AI Interface or your Java Skirmish AI. -# the value of this property is appended to the generated java.library.path - -# Specify which type of the JVM to use -# possible values: client, server -# default: 32bit: client -# 64bit: server -;jvm.type=server - -# NOTE: do not use these, as the interface will ignore them; -# see jvm.option.java.*.path options above -;jvm.option.x=-Djava.class.path=... -;jvm.option.x=-Djava.library.path=... - -# footprint (memory) related -jvm.option.x=-Xms64M -jvm.option.x=-Xmx512M -jvm.option.x=-Xss512K -jvm.option.x=-Xoss400K - - -# Misc -;jvm.option.x=-XX:+AlwaysRestoreFPU -;jvm.option.x=-Djava.util.logging.config.file=./logging.properties - -# example settings for debugging -# logging related (only recommended when debugging) -;jvm.option.x=-Xcheck:jni -;jvm.option.x=-verbose:jni -;jvm.option.x=-XX:+UnlockDiagnosticVMOptions -;jvm.option.x=-XX:+LogVMOutput -;jvm.option.x=-XX:LogFile=${home-dir}/log/jvm-log.xml - -;jvm.option.x=-Xdebug -;jvm.option.x=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=7777 -# disable JIT (required for debugging under the classical VM) -;jvm.option.x=-Djava.compiler=NONE -# disables old JDB -;jvm.option.x=-Xnoagent - -;jvm.option.x=-XX:ErrorFile=./hs_err_pid.log -;jvm.option.x=-XX:+CheckJNICalls - diff --git a/AI/Interfaces/Java/pom.xml b/AI/Interfaces/Java/pom.xml deleted file mode 100644 index bddb566ede4..00000000000 --- a/AI/Interfaces/Java/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - 4.0.0 - - - - - - 0.1 - - - - com.springrts - common-spring - 1.0 - ../../../rts/build/maven/support/common/spring/pom.xml - - - com.springrts - ai-interface-java - ${my.version} - - jar - - Java AI Interface - Java Artificial Intelligence interface plugin for the Spring RTS engine - https://springrts.com/wiki/AIInterface:Java - 2008 - - - - - scm:git:git://github.com/spring/spring.git - scm:git:git@github.com:spring/spring.git - http://github.com/spring/spring/tree/master/AI/Interfaces/Java/ - - - diff --git a/AI/Interfaces/Java/src/main/java/com/springrts/ai/Util.java b/AI/Interfaces/Java/src/main/java/com/springrts/ai/Util.java deleted file mode 100644 index e3a45ebcc99..00000000000 --- a/AI/Interfaces/Java/src/main/java/com/springrts/ai/Util.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - Copyright (c) 2009 Robin Vobruba - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package com.springrts.ai; - - -import java.awt.Color; - -/** - * Contains utility functions used within all of the Java AI Interface code. - * - * @author hoijui.quaero@gmail.com - * @version 0.1 - */ -public final class Util { - - /** We need no instances of this class */ - private Util() {} - - public static short[] toShort3Array(final Color color) { - - short[] shortArr = new short[3]; - - shortArr[0] = (short) color.getRed(); - shortArr[1] = (short) color.getGreen(); - shortArr[2] = (short) color.getBlue(); - - return shortArr; - } - - public static Color toColor(final short[] shortArr) { - - Color color = new Color( - (int) shortArr[0], - (int) shortArr[1], - (int) shortArr[2] - ); - - return color; - } -} diff --git a/AI/Interfaces/Java/src/main/native/InterfaceDefines.h b/AI/Interfaces/Java/src/main/native/InterfaceDefines.h deleted file mode 100644 index ceb2ebcae59..00000000000 --- a/AI/Interfaces/Java/src/main/native/InterfaceDefines.h +++ /dev/null @@ -1,23 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#ifndef _INTERFACE_DEFINES_H -#define _INTERFACE_DEFINES_H - -#ifdef __cplusplus -extern "C" { -#endif - -#define JAVA_SKIRMISH_AI_PROPERTY_CLASS_NAME "className" - -#define MY_LOG_FILE "interface-log.txt" -#define JAVA_AI_INTERFACE_LIBRARY_FILE_NAME "AIInterface.jar" -#define NATIVE_LIBS_DIR "lib" -#define JRE_LOCATION_FILE "jre-location.txt" - -#include // for NULL - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _INTERFACE_DEFINES_H diff --git a/AI/Interfaces/Java/src/main/native/InterfaceExport.c b/AI/Interfaces/Java/src/main/native/InterfaceExport.c deleted file mode 100644 index d68714c722e..00000000000 --- a/AI/Interfaces/Java/src/main/native/InterfaceExport.c +++ /dev/null @@ -1,157 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#include "InterfaceExport.h" - -#include "InterfaceDefines.h" -#include "JavaBridge.h" - -// generated at build time -#include "CallbackFunctionPointerBridge.h" - -#include "CUtils/Util.h" -#include "CUtils/SimpleLog.h" - -#include "ExternalAI/Interface/SAIInterfaceLibrary.h" -#include "ExternalAI/Interface/SAIInterfaceCallback.h" -#include "ExternalAI/Interface/SSkirmishAICallback.h" -#include "ExternalAI/Interface/SSkirmishAILibrary.h" - -#include // bool, true, false -#include // strlen(), strcat(), strcpy() -#include // malloc(), calloc(), free() - -static int interfaceId = -1; - -static const struct SAIInterfaceCallback* callback = NULL; -static struct SSkirmishAILibrary jvmSSkirmishAILibrary = {NULL, NULL, NULL, NULL}; - - -EXPORT(int) initStatic(int _interfaceId, const struct SAIInterfaceCallback* _callback) -{ - simpleLog_initcallback(_interfaceId, "Java Interface", _callback->Log_logsl, LOG_LEVEL_INFO); - - // initialize C part of the interface - interfaceId = _interfaceId; - callback = _callback; - - const char* const myShortName = callback->AIInterface_Info_getValueByKey(interfaceId, AI_INTERFACE_PROPERTY_SHORT_NAME); - const char* const myVersion = callback->AIInterface_Info_getValueByKey(interfaceId, AI_INTERFACE_PROPERTY_VERSION); - - if (myShortName == NULL || myVersion == NULL) { - simpleLog_logL(LOG_LEVEL_ERROR, "Couldn't fetch AI Name / Version \"%d\"", _interfaceId); - return -1; - } - - simpleLog_log("Initialized %s v%s AI Interface", myShortName, myVersion); - - // initialize Java part of the interface and the JVM - if (java_initStatic(interfaceId, callback)) { - simpleLog_logL(LOG_LEVEL_NOTICE, "Initialization successful."); - return 0; - } - - simpleLog_logL(LOG_LEVEL_ERROR, "Initialization failed."); - return -1; -} - -EXPORT(int) releaseStatic() -{ - // release Java part of the interface - if (java_releaseStatic() && java_unloadJNIEnv()) - return 0; - - return -1; -} - -EXPORT(enum LevelOfSupport) getLevelOfSupportFor(const char* engineVersion, int engineAIInterfaceGeneratedVersion) -{ - return LOS_Unknown; -} - - -// skirmish AI methods -enum LevelOfSupport CALLING_CONV proxy_skirmishAI_getLevelOfSupportFor( - const char* aiShortName, - const char* aiVersion, - const char* engineVersionString, - int engineVersionNumber, - const char* aiInterfaceShortName, - const char* aiInterfaceVersion -) { - return LOS_Unknown; -} - - -int CALLING_CONV proxy_skirmishAI_init(int skirmishAIId, const struct SSkirmishAICallback* aiCallback) -{ - int ret = -1; - - const char* const shortName = aiCallback->SkirmishAI_Info_getValueByKey(skirmishAIId, SKIRMISH_AI_PROPERTY_SHORT_NAME); - const char* const version = aiCallback->SkirmishAI_Info_getValueByKey(skirmishAIId, SKIRMISH_AI_PROPERTY_VERSION); - const char* const className = aiCallback->SkirmishAI_Info_getValueByKey(skirmishAIId, JAVA_SKIRMISH_AI_PROPERTY_CLASS_NAME); - - if (className != NULL) - ret = java_initSkirmishAIClass(shortName, version, className, skirmishAIId) ? 0 : 1; - - if (ret == 0) { - // init OK - funcPntBrdg_addCallback(skirmishAIId, aiCallback); - ret = java_skirmishAI_init(skirmishAIId, aiCallback); - } - - return ret; -} - -int CALLING_CONV proxy_skirmishAI_release(int skirmishAIId) -{ - // does nothing - const int ret = java_skirmishAI_release(skirmishAIId); - funcPntBrdg_removeCallback(skirmishAIId); - return ret; -} - -int CALLING_CONV proxy_skirmishAI_handleEvent(int skirmishAIId, int topicId, const void* data) -{ - return java_skirmishAI_handleEvent(skirmishAIId, topicId, data); -} - - -EXPORT(const struct SSkirmishAILibrary*) loadSkirmishAILibrary( - const char* const shortName, - const char* const version -) { - // all Java AI's run inside a single JVM proxied by this lib - if (jvmSSkirmishAILibrary.init == NULL) { - jvmSSkirmishAILibrary.getLevelOfSupportFor = &proxy_skirmishAI_getLevelOfSupportFor; -/* - jvmSSkirmishAILibrary.getInfo = proxy_skirmishAI_getInfo; - jvmSSkirmishAILibrary.getOptions = proxy_skirmishAI_getOptions; -*/ - jvmSSkirmishAILibrary.init = &proxy_skirmishAI_init; - jvmSSkirmishAILibrary.release = &proxy_skirmishAI_release; - jvmSSkirmishAILibrary.handleEvent = &proxy_skirmishAI_handleEvent; - } - - return &jvmSSkirmishAILibrary; -} - -EXPORT(int) unloadSkirmishAILibrary( - const char* const shortName, - const char* const version -) { - const char* const className = callback->SkirmishAI_Info_getValueByKey(interfaceId, shortName, version, JAVA_SKIRMISH_AI_PROPERTY_CLASS_NAME); - - if (java_releaseSkirmishAIClass(className)) - return 0; - - return -1; -} - -EXPORT(int) unloadAllSkirmishAILibraries() -{ - if (java_releaseAllSkirmishAIClasses()) - return 0; - - return -1; -} - diff --git a/AI/Interfaces/Java/src/main/native/InterfaceExport.h b/AI/Interfaces/Java/src/main/native/InterfaceExport.h deleted file mode 100644 index 20920ccb563..00000000000 --- a/AI/Interfaces/Java/src/main/native/InterfaceExport.h +++ /dev/null @@ -1,61 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#ifndef _INTERFACE_EXPORT_H -#define _INTERFACE_EXPORT_H - -// check if the correct defines are set by the build system -#if !defined BUILDING_AI_INTERFACE -# error BUILDING_AI_INTERFACE should be defined when building AI Interfaces -#endif -#if !defined BUILDING_AI -# error BUILDING_AI should be defined when building AI Interfaces -#endif -#if defined BUILDING_SKIRMISH_AI -# error BUILDING_SKIRMISH_AI should not be defined when building AI Interfaces -#endif -#if defined SYNCIFY -# error SYNCIFY should not be defined when building AI Interfaces -#endif - - -#include "ExternalAI/Interface/aidefines.h" - -#ifdef __cplusplus -extern "C" { -#endif - -//#include "ExternalAI/Interface/ELevelOfSupport.h" - -struct SSkirmishAILibrary; -struct SAIInterfaceCallback; - -// for a list of the functions that have to be exported, -// see struct SAIInterfaceLibrary in: -// "rts/ExternalAI/Interface/SAIInterfaceLibrary.h" - - -// static AI interface library functions - -EXPORT(int) initStatic(int interfaceId, - const struct SAIInterfaceCallback* callback); -EXPORT(int) releaseStatic(); -//EXPORT(enum LevelOfSupport) getLevelOfSupportFor( -// const char* engineVersion, int engineAIInterfaceGeneratedVersion); - - -// skirmish AI related methods - -EXPORT(const struct SSkirmishAILibrary*) loadSkirmishAILibrary( - const char* const shortName, - const char* const version); -EXPORT(int) unloadSkirmishAILibrary( - const char* const shortName, - const char* const version); -EXPORT(int) unloadAllSkirmishAILibraries(); - - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _INTERFACE_EXPORT_H diff --git a/AI/Interfaces/Java/src/main/native/JavaBridge.c b/AI/Interfaces/Java/src/main/native/JavaBridge.c deleted file mode 100644 index 1827f63c806..00000000000 --- a/AI/Interfaces/Java/src/main/native/JavaBridge.c +++ /dev/null @@ -1,1219 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#include "JavaBridge.h" - -#include "InterfaceDefines.h" -#include "JvmLocater.h" -#include "JniUtil.h" -#include "EventsJNIBridge.h" -#include "CUtils/Util.h" -#include "CUtils/SimpleLog.h" -#include "CUtils/SharedLibrary.h" - -#include "ExternalAI/Interface/aidefines.h" -#include "ExternalAI/Interface/SAIInterfaceLibrary.h" -#include "ExternalAI/Interface/SAIInterfaceCallback.h" -#include "ExternalAI/Interface/SSkirmishAILibrary.h" -#include "ExternalAI/Interface/SSkirmishAICallback.h" -#include "System/SafeCStrings.h" -#include "lib/streflop/streflopC.h" - -#include - -#include // strlen(), strcat(), strcpy() -#include // malloc(), calloc(), free() -#include - -struct Properties { - size_t size; - const char** keys; - const char** values; -}; - -static int interfaceId = -1; - -static const struct SAIInterfaceCallback* callback = NULL; -static struct Properties jvmCfgProps = {0, NULL, NULL}; - -static size_t numSkirmishAIs = 0; -// static const size_t maxSkirmishAIs = 255; // MAX_AIS -#define maxSkirmishAIs 255 - -static size_t skirmishAIId_skirmishAiImpl[maxSkirmishAIs] = {999999}; - -static char* jAIClassNames[maxSkirmishAIs] = {NULL}; - -static jobject jAIInstances[maxSkirmishAIs] = {NULL}; -static jobject jAIClassLoaders[maxSkirmishAIs] = {NULL}; -static jobject jAICallBacks[maxSkirmishAIs] = {NULL}; - -// vars used to integrate the JVM -// it is loaded at runtime, not at loadtime -static sharedLib_t jvmSharedLib = NULL; - -typedef jint (JNICALL JNI_GetDefaultJavaVMInitArgs_t)(void* vmArgs); -typedef jint (JNICALL JNI_CreateJavaVM_t)(JavaVM** vm, void** jniEnv, void* vmArgs); -typedef jint (JNICALL JNI_GetCreatedJavaVMs_t)(JavaVM** vms, jsize vms_sizeMax, jsize* vms_size); - -static JNI_GetDefaultJavaVMInitArgs_t* JNI_GetDefaultJavaVMInitArgs_f; -static JNI_CreateJavaVM_t* JNI_CreateJavaVM_f; -static JNI_GetCreatedJavaVMs_t* JNI_GetCreatedJavaVMs_f; - - -// ### JNI global vars ### - -/// Java VM instance reference -static JavaVM* g_jvm = NULL; - -/// AI Callback class -static jclass g_cls_aiCallback = NULL; -/// AI Callback Constructor: AICallback(int skirmishAIId) -static jmethodID g_m_aiCallback_ctor_I = NULL; - -/// Basic AI interface. -static jclass g_cls_ai_int = NULL; - - - -// ### General helper functions following ### - -/// Sets the FPU state to how spring likes it -static inline void java_establishSpringEnv() { - // only detach in java_unloadJNIEnv - // (*g_jvm)->DetachCurrentThread(g_jvm); - streflop_init_Simple(); -} - -/// The JVM sets the environment it wants automatically, so this is a no-op -static inline void java_establishJavaEnv() {} - - -static inline size_t minSize(size_t size1, size_t size2) { - return (size1 < size2) ? size1 : size2; -} - -static const char* java_getValueByKey(const struct Properties* props, const char* key) { - return util_map_getValueByKey(props->size, props->keys, props->values, key); -} - - -// /** -// * Called when the JVM loads this native library. -// */ -// JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { -// return JNI_VERSION_1_6; -// } -// -// /** -// * Called when the JVM unloads this native library. -// */ -// JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {} - - - - -// ### JNI helper functions following ### - -/** - * Creates the AI Interface global Java class path. - * - * It will consist of the following: - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/AIInterface.jar - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/(j)?config/ - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/(j)?config/[*].jar - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/(j)?resources/ - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/(j)?resources/[*].jar - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/(j)?script/ - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/(j)?script/[*].jar - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/jlib/ - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/jlib/[*].jar - * TODO: {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/common/jlib/ - * TODO: {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/common/jlib/[*].jar - */ -static bool java_createClassPath(char* classPathStr, const size_t classPathStr_sizeMax) -{ - // the dirs and .jar files in the following array - // will be concatenated with intermediate path separators - // to form the classPathStr - static const size_t classPath_sizeMax = 128; - char** classPath = (char**) calloc(classPath_sizeMax, sizeof(char*)); - size_t classPath_size = 0; - - // the Java AI Interfaces java library file path (.../AIInterface.jar) - // We need to search for this jar, instead of looking only where - // the AIInterface.so/InterfaceInfo.lua is, because on some systems - // (eg. Debian), the .so is in /usr/lib, and the .jar's are in /usr/shared. - char mainJarPath[2048]; - const bool located = callback->DataDirs_locatePath(interfaceId, - mainJarPath, sizeof(mainJarPath), - JAVA_AI_INTERFACE_LIBRARY_FILE_NAME, - false, false, false, false); - - if (!located) { - simpleLog_logL(LOG_LEVEL_ERROR, "Couldn't find %s", JAVA_AI_INTERFACE_LIBRARY_FILE_NAME); - return false; - } - - classPath[classPath_size++] = util_allocStrCpy(mainJarPath); - - if (!util_getParentDir(mainJarPath)) { - simpleLog_logL(LOG_LEVEL_ERROR, "Retrieving the parent dir of the path to AIInterface.jar (%s) failed.", mainJarPath); - return false; - } - - char* jarsDataDir = mainJarPath; - - // the directories in the following list will be searched for .jar files - // which will then be added to the classPathStr, plus the dirs will be added - // to the classPathStr directly, so you can keep .class files in there - static const size_t jarDirs_sizeMax = 128; - char** jarDirs = (char**) calloc(jarDirs_sizeMax, sizeof(char*)); - size_t jarDirs_size = 0; - - // add to classpath: - // {spring-data-dir}/Interfaces/Java/0.1/${x}/ - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, jarsDataDir, "jconfig"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, jarsDataDir, "config"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, jarsDataDir, "jresources"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, jarsDataDir, "resources"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, jarsDataDir, "jscript"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, jarsDataDir, "script"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, jarsDataDir, "jlib"); - // "lib" is for native libs only - - // add the jar dirs (for .class files) and all contained .jars recursively - size_t jd, jf; - for (jd = 0; (jd < jarDirs_size) && (classPath_size < classPath_sizeMax); ++jd) { - if (util_fileExists(jarDirs[jd])) { - // add the dir directly - // For this to work properly with URLClassPathHandler, - // we have to ensure there is a '/' at the end, - // for the class-path-part to be recognized as a directory. - classPath[classPath_size++] = util_allocStrCat(2, jarDirs[jd], "/"); - - // add the contained jars recursively - static const size_t jarFiles_sizeMax = 128; - char** jarFiles = (char**) calloc(jarFiles_sizeMax, sizeof(char*)); - const size_t jarFiles_size = util_listFiles(jarDirs[jd], ".jar", jarFiles, true, jarFiles_sizeMax); - - for (jf = 0; (jf < jarFiles_size) && (classPath_size < classPath_sizeMax); ++jf) { - classPath[classPath_size++] = util_allocStrCatFSPath(2, jarDirs[jd], jarFiles[jf]); - FREE(jarFiles[jf]); - } - - FREE(jarFiles); - } - - FREE(jarDirs[jd]); - } - - FREE(jarDirs); - - - // concat the classpath entries - classPathStr[0] = '\0'; - if (classPath[0] != NULL) { - STRCAT_T(classPathStr, classPathStr_sizeMax, classPath[0]); - FREE(classPath[0]); - } - - size_t cp; - - for (cp = 1; cp < classPath_size; ++cp) { - if (classPath[cp] == NULL) - continue; - - STRCAT_T(classPathStr, classPathStr_sizeMax, ENTRY_DELIM); - STRCAT_T(classPathStr, classPathStr_sizeMax, classPath[cp]); - FREE(classPath[cp]); - } - - FREE(classPath); - return true; -} - -/** - * Creates a Skirmish AI local Java class path. - * - * It will consist of the following: - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/{ai-version}/SkirmishAI.jar - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/{ai-version}/(j)?config/ - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/{ai-version}/(j)?config/[*].jar - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/{ai-version}/(j)?resources/ - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/{ai-version}/(j)?resources/[*].jar - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/{ai-version}/(j)?script/ - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/{ai-version}/(j)?script/[*].jar - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/{ai-version}/jlib/ - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/{ai-version}/jlib/[*].jar - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/common/(j)?config/ - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/common/(j)?config/[*].jar - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/common/(j)?resources/ - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/common/(j)?resources/[*].jar - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/common/(j)?script/ - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/common/(j)?script/[*].jar - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/common/jlib/ - * {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/common/jlib/[*].jar - */ -static size_t java_createAIClassPath( - const char* shortName, - const char* version, - char** classPathParts, - const size_t classPathParts_sizeMax -) { - size_t classPathParts_size = 0; - - // the .jar files in the following list will be added to the classpath - const size_t jarFiles_sizeMax = classPathParts_sizeMax; - char** jarFiles = (char**) calloc(jarFiles_sizeMax, sizeof(char*)); - size_t jarFiles_size = 0; - - const char* const skirmDD = - callback->SkirmishAI_Info_getValueByKey(interfaceId, - shortName, version, - SKIRMISH_AI_PROPERTY_DATA_DIR); - if (skirmDD == NULL) { - simpleLog_logL(LOG_LEVEL_ERROR, - "Retrieving the data-dir of Skirmish AI %s-%s failed.", - shortName, version); - } - // {spring-data-dir}/{SKIRMISH_AI_DATA_DIR}/{ai-name}/{ai-version}/SkirmishAI.jar - jarFiles[jarFiles_size++] = util_allocStrCatFSPath(2, - skirmDD, "SkirmishAI.jar"); - - // the directories in the following list will be searched for .jar files - // which then will be added to the classpath, plus they will be added - // to the classpath directly, so you can keep .class files in there - const size_t jarDirs_sizeMax = classPathParts_sizeMax; - char** jarDirs = (char**) calloc(jarDirs_sizeMax, sizeof(char*)); - size_t jarDirs_size = 0; - - // add to classpath ... - - // {spring-data-dir}/Skirmish/MyJavaAI/0.1/SkirmishAI/ - // this can be useful for AI devs while testing, - // if they do not want to put everything into a jar all the time - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDD, "SkirmishAI"); - - // add to classpath: - // {spring-data-dir}/Skirmish/MyJavaAI/0.1/${x}/ - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDD, "jconfig"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDD, "config"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDD, "jresources"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDD, "resources"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDD, "jscript"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDD, "script"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDD, "jlib"); - // "lib" is for native libs only - - // add the dir common for all versions of the Skirmish AI, - // if it is specified and exists - const char* const skirmDDCommon = - callback->SkirmishAI_Info_getValueByKey(interfaceId, - shortName, version, - SKIRMISH_AI_PROPERTY_DATA_DIR_COMMON); - - if (skirmDDCommon != NULL) { - // add to classpath: - // {spring-data-dir}/Skirmish/MyJavaAI/common/${x}/ - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDDCommon, "jconfig"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDDCommon, "config"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDDCommon, "jresources"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDDCommon, "resources"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDDCommon, "jscript"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDDCommon, "script"); - jarDirs[jarDirs_size++] = util_allocStrCatFSPath(2, skirmDDCommon, "jlib"); - // "lib" is for native libs only - } - - // add the directly specified .jar files - size_t jf; - for (jf = 0; (jf < jarFiles_size) && (classPathParts_size < classPathParts_sizeMax); ++jf) { - classPathParts[classPathParts_size++] = util_allocStrCpy(jarFiles[jf]); - FREE(jarFiles[jf]); - } - - // add the dirs and the contained .jar files - size_t jd, sjf; - for (jd = 0; (jd < jarDirs_size) && (classPathParts_size < classPathParts_sizeMax); ++jd) { - if (jarDirs[jd] != NULL && util_fileExists(jarDirs[jd])) { - // add the jar dir (for .class files) - // For this to work properly with URLClassPathHandler, - // we have to ensure there is a '/' at the end, - // for the class-path-part to be recognized as a directory. - classPathParts[classPathParts_size++] = util_allocStrCat(2, jarDirs[jd], "/"); - - // add the jars in the dir - const size_t subJarFiles_sizeMax = classPathParts_sizeMax - classPathParts_size; - char** subJarFiles = (char**) calloc(subJarFiles_sizeMax, sizeof(char*)); - const size_t subJarFiles_size = util_listFiles(jarDirs[jd], ".jar", subJarFiles, true, subJarFiles_sizeMax); - - for (sjf = 0; (sjf < subJarFiles_size) && (classPathParts_size < classPathParts_sizeMax); ++sjf) { - // .../[*].jar - classPathParts[classPathParts_size++] = util_allocStrCatFSPath(2, jarDirs[jd], subJarFiles[sjf]); - FREE(subJarFiles[sjf]); - } - - FREE(subJarFiles); - } - - FREE(jarDirs[jd]); - } - - FREE(jarDirs); - FREE(jarFiles); - - return classPathParts_size; -} - -static jobject java_createAIClassLoader(JNIEnv* env, const char* shortName, const char* version) -{ - static const size_t classPathParts_sizeMax = 512; - char** classPathParts = (char**) calloc(classPathParts_sizeMax, sizeof(char*)); - const size_t classPathParts_size = java_createAIClassPath(shortName, version, classPathParts, classPathParts_sizeMax); - - jobject o_jClsLoader = NULL; - jobjectArray o_cppURLs = jniUtil_createURLArray(env, classPathParts_size); - - if (o_cppURLs != NULL) { -#ifdef _WIN32 - static const char* FILE_URL_PREFIX = "file:///"; -#else // _WIN32 - static const char* FILE_URL_PREFIX = "file://"; -#endif // _WIN32 - size_t cpp; - for (cpp = 0; cpp < classPathParts_size; ++cpp) { - #ifdef _WIN32 - // we can not use windows path separators in file URLs - util_strReplaceChar(classPathParts[cpp], '\\', '/'); - #endif - - char* str_fileUrl = util_allocStrCat(2, FILE_URL_PREFIX, classPathParts[cpp]); - // TODO: check/test if this is allowed/ok - FREE(classPathParts[cpp]); - simpleLog_logL(LOG_LEVEL_INFO, - "Skirmish AI %s %s class-path part %i: \"%s\"", - shortName, version, cpp, str_fileUrl); - jobject jurl_fileUrl = jniUtil_createURLObject(env, str_fileUrl); - FREE(str_fileUrl); - if (jurl_fileUrl == NULL) { - simpleLog_logL(LOG_LEVEL_ERROR, - "Skirmish AI %s %s class-path part %i (\"%s\"): failed to create a URL", - shortName, version, cpp, str_fileUrl); - o_cppURLs = NULL; - break; - } - const bool inserted = jniUtil_insertURLIntoArray(env, o_cppURLs, cpp, jurl_fileUrl); - if (!inserted) { - simpleLog_logL(LOG_LEVEL_ERROR, - "Skirmish AI %s %s class-path part %i (\"%s\"): failed to insert", - shortName, version, cpp, str_fileUrl); - o_cppURLs = NULL; - break; - } - } - } - - if (o_cppURLs != NULL) { - if ((o_jClsLoader = jniUtil_createURLClassLoader(env, o_cppURLs)) != NULL) - o_jClsLoader = jniUtil_makeGlobalRef(env, o_jClsLoader, "Skirmish AI class-loader"); - } - - FREE(classPathParts); - return o_jClsLoader; -} - -/** - * Load the interfaces JVM properties file. - */ -static bool java_readJvmCfgFile(struct Properties* props) -{ - bool read = false; - - const size_t props_sizeMax = 256; - - props->size = 0; - props->keys = (const char**) calloc(props_sizeMax, sizeof(char*)); - props->values = (const char**) calloc(props_sizeMax, sizeof(char*)); - - // ### read JVM options config file ### - char jvmPropFile[2048]; - bool located = callback->DataDirs_locatePath(interfaceId, jvmPropFile, sizeof(jvmPropFile), JVM_PROPERTIES_FILE, false, false, false, false); - - // if the version specific file does not exist, - // try to get the common one - if (!located) - located = callback->DataDirs_locatePath(interfaceId, jvmPropFile, sizeof(jvmPropFile), JVM_PROPERTIES_FILE, false, false, false, true); - - if (located) { - props->size = util_parsePropertiesFile(jvmPropFile, props->keys, props->values, props_sizeMax); - read = true; - simpleLog_logL(LOG_LEVEL_INFO, "JVM: arguments loaded from: %s", jvmPropFile); - } else { - props->size = 0; - read = false; - simpleLog_logL(LOG_LEVEL_INFO, "JVM: arguments NOT loaded"); - } - - return read; -} - -/** - * Creates the Java library path. - * -> where native shared libraries are searched - * - * It will consist of the following: - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/ - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/lib/ - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/common/ - * {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/common/lib/ - */ -static bool java_createNativeLibsPath(char* libraryPath, const size_t libraryPath_sizeMax) -{ - // {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/ - const char* const dd_r = callback->AIInterface_Info_getValueByKey(interfaceId, AI_INTERFACE_PROPERTY_DATA_DIR); - - if (dd_r == NULL) { - simpleLog_logL(LOG_LEVEL_ERROR, "Unable to find read-only data-dir."); - return false; - } - - STRCPY_T(libraryPath, libraryPath_sizeMax, dd_r); - - // {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/{version}/lib/ - char dd_lib_r[2048]; - const bool located = callback->DataDirs_locatePath(interfaceId, dd_lib_r, sizeof(dd_lib_r), NATIVE_LIBS_DIR, false, false, true, false); - - if (!located) { - simpleLog_logL(LOG_LEVEL_NOTICE, "Unable to find read-only native libs data-dir (optional): %s", NATIVE_LIBS_DIR); - } else { - STRCAT_T(libraryPath, libraryPath_sizeMax, ENTRY_DELIM); - STRCAT_T(libraryPath, libraryPath_sizeMax, dd_lib_r); - } - - - // {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/common/ - const char* const dd_r_common = callback->AIInterface_Info_getValueByKey(interfaceId, AI_INTERFACE_PROPERTY_DATA_DIR_COMMON); - - if (dd_r_common == NULL || !util_fileExists(dd_r_common)) { - simpleLog_logL(LOG_LEVEL_NOTICE, "Unable to find common read-only data-dir (optional)."); - } else { - STRCAT_T(libraryPath, libraryPath_sizeMax, ENTRY_DELIM); - STRCAT_T(libraryPath, libraryPath_sizeMax, dd_r_common); - } - - - // {spring-data-dir}/{AI_INTERFACES_DATA_DIR}/Java/common/lib/ - if (dd_r_common != NULL) { - char dd_lib_r_common[2048]; - const bool located = callback->DataDirs_locatePath(interfaceId, dd_lib_r_common, sizeof(dd_lib_r_common), NATIVE_LIBS_DIR, false, false, true, true); - - if (!located || !util_fileExists(dd_lib_r_common)) { - simpleLog_logL(LOG_LEVEL_NOTICE, "Unable to find common read-only native libs data-dir (optional)."); - } else { - STRCAT_T(libraryPath, libraryPath_sizeMax, ENTRY_DELIM); - STRCAT_T(libraryPath, libraryPath_sizeMax, dd_lib_r_common); - } - } - - return true; -} - - -static bool java_createJavaVMInitArgs(struct JavaVMInitArgs* vm_args, const struct Properties* jvmProps) -{ - // jint jniVersion = JNI_VERSION_1_1; - // jint jniVersion = JNI_VERSION_1_2; - jint jniVersion = JNI_VERSION_1_4; - // jint jniVersion = JNI_VERSION_1_6; - - char classPath [8 * 1024] = {0}; - char classPathOpt[8 * 1024] = {0}; - char libraryPath [4 * 1024] = {0}; - char libraryPathOpt[4 * 1024] = {0}; - - bool ignoreUnrecognized = true; - - if (jvmProps != NULL) { - const char* jniVersionFromCfg = java_getValueByKey(jvmProps, "jvm.jni.version"); - const char* ignoreUnrecognizedFromCfg = java_getValueByKey(jvmProps, "jvm.arguments.ignoreUnrecognized"); - - // ### evaluate JNI version to use ### - if (jniVersionFromCfg != NULL) { - const unsigned long int jniVersion_tmp = strtoul(jniVersionFromCfg, NULL, 16); - - if (jniVersion_tmp != 0 /*&& jniVersion_tmp != ULONG_MAX*/) - jniVersion = (jint) jniVersion_tmp; - } - - // ### check if unrecognized JVM options should be ignored ### - // if false, the JVM creation will fail if an - // unknown or invalid option was specified - if (ignoreUnrecognizedFromCfg != NULL && !util_strToBool(ignoreUnrecognizedFromCfg)) - ignoreUnrecognized = false; - } - - simpleLog_logL(LOG_LEVEL_INFO, "JVM: JNI version: %#x", jniVersion); - vm_args->version = jniVersion; - - - if (ignoreUnrecognized) { - simpleLog_logL(LOG_LEVEL_INFO, "JVM: ignoring unrecognized options"); - vm_args->ignoreUnrecognized = JNI_TRUE; - } else { - simpleLog_logL(LOG_LEVEL_INFO, "JVM: NOT ignoring unrecognized options"); - vm_args->ignoreUnrecognized = JNI_FALSE; - } - - - // ### create the Java class-path option ### - // autogenerate the class path - if (!java_createClassPath(classPath, sizeof(classPath))) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed creating Java class-path."); - return false; - } - if (jvmProps != NULL) { - // ..., and append the part from the jvm options properties file, - // if it is specified there - const char* clsPathFromCfg = java_getValueByKey(jvmProps, "jvm.option.java.class.path"); - - if (clsPathFromCfg != NULL) { - STRCAT_T(classPath, sizeof(classPath), ENTRY_DELIM); - STRCAT_T(classPath, sizeof(classPath), clsPathFromCfg); - } - } - - - // create the java.class.path option - STRCPY_T(classPathOpt, sizeof(classPathOpt), "-Djava.class.path="); - STRCAT_T(classPathOpt, sizeof(classPathOpt), classPath); - - - // ### create the Java library-path option ### - // autogenerate the java library path - if (!java_createNativeLibsPath(libraryPath, sizeof(libraryPath))) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed creating Java library-path."); - return false; - } - if (jvmProps != NULL) { - // ..., and append the part from the jvm options properties file, - // if it is specified there - const char* libPathFromCfg = java_getValueByKey(jvmProps, "jvm.option.java.library.path"); - - if (libPathFromCfg != NULL) { - STRCAT_T(libraryPath, sizeof(libraryPath), ENTRY_DELIM); - STRCAT_T(libraryPath, sizeof(libraryPath), libPathFromCfg); - } - } - - // create the java.library.path option ... - // autogenerate it, and append the part from the jvm options file, - // if it is specified there - STRCPY_T(libraryPathOpt, sizeof(libraryPathOpt), "-Djava.library.path="); - STRCAT_T(libraryPathOpt, sizeof(libraryPathOpt), libraryPath); - - - // ### create and set all JVM options ### - const char* strOptions[64]; - size_t numOpts = 0; - - strOptions[numOpts++] = classPathOpt; - strOptions[numOpts++] = libraryPathOpt; - - static const char* const JCPVAL = "-Djava.class.path="; - static const char* const JLPVAL = "-Djava.library.path="; - const size_t JCPVAL_size = strlen(JCPVAL); - const size_t JLPVAL_size = strlen(JCPVAL); - - if (jvmProps != NULL) { - // ### add string options from the JVM config file with property name "jvm.option.x" ### - int i; - for (i = 0; i < jvmProps->size; ++i) { - if (strcmp(jvmProps->keys[i], "jvm.option.x") != 0) - continue; - - const char* const val = jvmProps->values[i]; - const size_t val_size = strlen(val); - // ignore "-Djava.class.path=..." - // and "-Djava.library.path=..." options - if (strncmp(val, JCPVAL, minSize(val_size, JCPVAL_size)) != 0 && - strncmp(val, JLPVAL, minSize(val_size, JLPVAL_size)) != 0) { - strOptions[numOpts++] = val; - } - } - } else { - // ### ... or set default ones, if the JVM config file was not found ### - simpleLog_logL(LOG_LEVEL_WARNING, "JVM: properties file ("JVM_PROPERTIES_FILE") not found; using default options."); - - strOptions[numOpts++] = "-Xms64M"; - strOptions[numOpts++] = "-Xmx512M"; - strOptions[numOpts++] = "-Xss512K"; - strOptions[numOpts++] = "-Xoss400K"; - - #if defined DEBUG - strOptions[numOpts++] = "-Xcheck:jni"; - strOptions[numOpts++] = "-verbose:jni"; - strOptions[numOpts++] = "-XX:+UnlockDiagnosticVMOptions"; - strOptions[numOpts++] = "-XX:+LogVMOutput"; - - strOptions[numOpts++] = "-Xdebug"; - strOptions[numOpts++] = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=7777"; - // disable JIT (required for debugging under the classical VM) - strOptions[numOpts++] = "-Djava.compiler=NONE"; - // disable old JDB - strOptions[numOpts++] = "-Xnoagent"; - #endif // defined DEBUG - } - - vm_args->options = (struct JavaVMOption*) calloc(numOpts, sizeof(struct JavaVMOption)); - vm_args->nOptions = 0; - - // fill strOptions into the JVM options - simpleLog_logL(LOG_LEVEL_INFO, "JVM: options:", numOpts); - char dd_rw[2048] = {'\0'}; - callback->DataDirs_locatePath(interfaceId, dd_rw, sizeof(dd_rw), "", true, true, true, false); - size_t i; - - for (i = 0; i < numOpts; ++i) { - char* tmpOptionString = util_allocStrReplaceStr(strOptions[i], "${home-dir}", dd_rw); - - // do not add empty options - if (tmpOptionString == NULL) - continue; - - if (strlen(tmpOptionString) == 0) { - free(tmpOptionString); - tmpOptionString = NULL; - continue; - } - - vm_args->options[vm_args->nOptions ].optionString = tmpOptionString; - vm_args->options[vm_args->nOptions++].extraInfo = NULL; - - simpleLog_logL(LOG_LEVEL_INFO, "JVM option %ul: %s", vm_args->nOptions - 1, tmpOptionString); - } - - simpleLog_logL(LOG_LEVEL_INFO, ""); - return true; -} - - - -static JNIEnv* java_reattachCurrentThread(JavaVM* jvm) -{ - simpleLog_logL(LOG_LEVEL_DEBUG, "Reattaching current thread..."); - - JNIEnv* env = NULL; - - // const jint res = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void**) &env, NULL); - const jint res = (*jvm)->AttachCurrentThread(jvm, (void**) &env, NULL); - - if (res != 0) { - env = NULL; - simpleLog_logL(LOG_LEVEL_ERROR, "Failed attaching JVM to current thread(2): %i - %s", res, jniUtil_getJniRetValDescription(res)); - } - - return env; -} - -static JNIEnv* java_getJNIEnv(bool preload) -{ - if (g_jvm != NULL) - return java_reattachCurrentThread(g_jvm); - - assert(preload); - simpleLog_logL(LOG_LEVEL_INFO, "Creating the JVM."); - - JNIEnv* ret = NULL; - JNIEnv* env = NULL; - JavaVM* jvm = NULL; - - struct JavaVMInitArgs vm_args; - memset(&vm_args, 0, sizeof(vm_args)); - - jint res = 0; - - if (!java_createJavaVMInitArgs(&vm_args, &jvmCfgProps)) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed initializing JVM init-arguments."); - goto end; - } - - // Looking for existing JVMs might be problematic: they could - // have been initialized with other JVM-arguments then we need. - // But as we can not use DestroyJavaVM (it makes creating a new - // one for the same process impossible, a (SUN?) JVM limitation) - // we have to do this anyway to support /aireload and /aicontrol - // for Java Skirmish AIs. - // - simpleLog_logL(LOG_LEVEL_INFO, "looking for existing JVMs ..."); - jsize numJVMsFound = 0; - - // grab the first Java VM that has been created - if ((res = JNI_GetCreatedJavaVMs_f(&jvm, 1, &numJVMsFound)) != 0) { - jvm = NULL; - simpleLog_logL(LOG_LEVEL_ERROR, "Failed fetching list of running JVMs: %i - %s", res, jniUtil_getJniRetValDescription(res)); - goto end; - } - - simpleLog_logL(LOG_LEVEL_INFO, "number of existing JVMs: %i", numJVMsFound); - - if (numJVMsFound > 0) { - simpleLog_logL(LOG_LEVEL_INFO, "using an already running JVM."); - } else { - simpleLog_logL(LOG_LEVEL_INFO, "creating JVM..."); - - if ((res = JNI_CreateJavaVM_f(&jvm, (void**) &env, &vm_args)) != 0 || (*env)->ExceptionCheck(env)) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed to create Java VM: %i - %s", res, jniUtil_getJniRetValDescription(res)); - goto end; - } - } - - // free the JavaVMInitArgs content - jint i; - for (i = 0; i < vm_args.nOptions; ++i) { - FREE(vm_args.options[i].optionString); - } - FREE(vm_args.options); - - //res = (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void**) &env, NULL); - res = (*jvm)->AttachCurrentThread(jvm, (void**) &env, NULL); - - if (res < 0 || (*env)->ExceptionCheck(env)) { - if ((*env)->ExceptionCheck(env)) - (*env)->ExceptionDescribe(env); - - simpleLog_logL(LOG_LEVEL_ERROR, "Failed to attach JVM to current thread: %i - %s", res, jniUtil_getJniRetValDescription(res)); - goto end; - } - -end: - if (env == NULL || jvm == NULL || (*env)->ExceptionCheck(env) || res != 0) { - simpleLog_logL(LOG_LEVEL_ERROR, "JVM: Failed creating."); - - if (env != NULL && (*env)->ExceptionCheck(env)) - (*env)->ExceptionDescribe(env); - - if (jvm != NULL) { - // never destroy the JVM, doing so prevents it from being created again for the same thread - // res = (*jvm)->DestroyJavaVM(jvm); - } - - g_jvm = NULL; - ret = NULL; - } else { - g_jvm = jvm; - ret = env; - } - - return ret; -} - - -bool java_unloadJNIEnv() -{ - if (g_jvm == NULL) - return true; - - simpleLog_logL(LOG_LEVEL_INFO, "JVM: Unloading ..."); - - #if 0 - // JNIEnv* jniEnv = java_getJNIEnv(false); - - // We have to be the ONLY running thread (native and Java) - // this may not help, but will not hurt either - // - // // jint res = (*g_jvm)->AttachCurrentThreadAsDaemon(g_jvm, (void**) &g_jniEnv, NULL); - // jint res = jvm->AttachCurrentThread((void**) &jniEnv, NULL); - #endif - - #if 0 - if (res < 0 || (*g_jniEnv)->ExceptionCheck(g_jniEnv)) { - if ((*g_jniEnv)->ExceptionCheck(g_jniEnv)) - (*g_jniEnv)->ExceptionDescribe(g_jniEnv); - - simpleLog_logL(LOG_LEVEL_ERROR, "JVM: Can not Attach to the current thread: %i - %s", res, jniUtil_getJniRetValDescription(res)); - return false; - } - #endif - - - const jint res = (*g_jvm)->DetachCurrentThread(g_jvm); - - if (res != 0) { - simpleLog_logL(LOG_LEVEL_ERROR, "JVM: Failed detaching current thread: %i - %s", res, jniUtil_getJniRetValDescription(res)); - return false; - } - - // never destroy the JVM because then it can not be created again - // in the same thread; will always fail with return value -1 (see - // java_getJNIEnv) - #if 0 - if ((res = (*g_jvm)->DestroyJavaVM(g_jvm)) != 0) { - simpleLog_logL(LOG_LEVEL_ERROR, "JVM: Failed destroying: %i - %s", res, jniUtil_getJniRetValDescription(res)); - return false; - } - - simpleLog_logL(LOG_LEVEL_NOTICE, "JVM: Successfully destroyed"); - g_jvm = NULL; - #endif - - java_establishSpringEnv(); - return true; -} - - -bool java_initStatic(int _interfaceId, const struct SAIInterfaceCallback* _callback) -{ - interfaceId = _interfaceId; - callback = _callback; - - // Read the jvm properties config file - java_readJvmCfgFile(&jvmCfgProps); - - size_t t; - for (t = 0; t < maxSkirmishAIs; ++t) { - skirmishAIId_skirmishAiImpl[t] = 999999; - } - - size_t sai; - for (sai = 0; sai < maxSkirmishAIs; ++sai) { - jAIClassNames[sai] = NULL; - jAIInstances[sai] = NULL; - jAIClassLoaders[sai] = NULL; - } - - // dynamically load the JVM - char jreLocationFile[2048]; - const bool located = callback->DataDirs_locatePath(interfaceId, jreLocationFile, sizeof(jreLocationFile), JRE_LOCATION_FILE, false, false, false, false); - char jrePath[1024]; - char jvmLibPath[1024]; - - if (!GetJREPath(jrePath, sizeof(jrePath), located ? jreLocationFile : NULL, NULL)) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed locating a JRE installation, you may specify the JAVA_HOME env var."); - return false; - } - - simpleLog_logL(LOG_LEVEL_NOTICE, "Using JRE (can be changed with JAVA_HOME): %s", jrePath); - -#if defined __arch64__ - static const char* defJvmType = "server"; -#else - static const char* defJvmType = "client"; -#endif - const char* jvmType = java_getValueByKey(&jvmCfgProps, "jvm.type"); - - if (jvmType == NULL) - jvmType = defJvmType; - - if (!GetJVMPath(jrePath, jvmType, jvmLibPath, sizeof(jvmLibPath), NULL)) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed locating the %s version of the JVM, please contact spring devs.", jvmType); - return false; - } - - if (!sharedLib_isLoaded(jvmSharedLib = sharedLib_load(jvmLibPath))) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed to load the JVM at \"%s\".", jvmLibPath); - return false; - } - - simpleLog_logL(LOG_LEVEL_NOTICE, "Successfully loaded the JVM shared library at \"%s\".", jvmLibPath); - - if ((JNI_GetDefaultJavaVMInitArgs_f = (JNI_GetDefaultJavaVMInitArgs_t*) sharedLib_findAddress(jvmSharedLib, "JNI_GetDefaultJavaVMInitArgs")) == NULL) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed to load the JVM, function \"%s\" not exported.", "JNI_GetDefaultJavaVMInitArgs"); - return false; - } - - if ((JNI_CreateJavaVM_f = (JNI_CreateJavaVM_t*) sharedLib_findAddress(jvmSharedLib, "JNI_CreateJavaVM")) == NULL) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed to load the JVM, function \"%s\" not exported.", "JNI_CreateJavaVM"); - return false; - } - - if ((JNI_GetCreatedJavaVMs_f = (JNI_GetCreatedJavaVMs_t*) sharedLib_findAddress(jvmSharedLib, "JNI_GetCreatedJavaVMs")) == NULL) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed to load the JVM, function \"%s\" not exported.", "JNI_GetCreatedJavaVMs"); - return false; - } - - java_establishJavaEnv(); - JNIEnv* env = java_getJNIEnv(true); - const bool loaded = (env != NULL); - const bool inited = (loaded && eventsJniBridge_initStatic(env, maxSkirmishAIs) == 0); - java_establishSpringEnv(); - - return inited; -} - -bool java_releaseStatic() -{ - sharedLib_unload(jvmSharedLib); - jvmSharedLib = NULL; - - FREE(jvmCfgProps.keys); - FREE(jvmCfgProps.values); - return true; -} - - - -static jobject java_createAICallback(JNIEnv* env, const struct SSkirmishAICallback* aiCallback, int skirmishAIId) { - (void) aiCallback; - - // initialize the AI Callback class, if not yet done - if (g_cls_aiCallback == NULL) { - // get the AI Callback class - if ((g_cls_aiCallback = jniUtil_findClass(env, CLS_AI_CALLBACK)) == NULL) - return NULL; - if ((g_cls_aiCallback = jniUtil_makeGlobalRef(env, g_cls_aiCallback, CLS_AI_CALLBACK)) == NULL) - return NULL; - // get (int skirmishAIId) constructor - if ((g_m_aiCallback_ctor_I = jniUtil_getMethodID(env, g_cls_aiCallback, "", "(I)V")) == NULL) - return NULL; - } - - #if 1 - // reuse callback if reloading - // this should be safe since the callback objects created in java_skirmishAI_init are not - // broken by java_skirmishAI_handleEvent, which also calls java_reattachCurrentThread and - // mightget a different env-pointer back - if (jAICallBacks[skirmishAIId] != NULL) - return jAICallBacks[skirmishAIId]; - #endif - - jobject o_clb = (*env)->NewObject(env, g_cls_aiCallback, g_m_aiCallback_ctor_I, skirmishAIId); - - if (jniUtil_checkException(env, "Failed creating Java AI Callback instance")) - return NULL; - - // return (jAICallBacks[skirmishAIId] = jniUtil_makeGlobalRef(env, o_clb, "AI callback instance")); - return (jAICallBacks[skirmishAIId] = o_clb); -} - - - -static bool java_loadSkirmishAI( - JNIEnv* env, - const char* shortName, - const char* version, - const char* className, - jobject* o_ai, - jobject* o_aiClassLoader -) { - #if 0 - // convert className from "com.myai.AI" to "com/myai/AI" - const size_t classNameP_sizeMax = strlen(className) + 1; - char classNameP[classNameP_sizeMax]; - STRCPY_T(classNameP, classNameP_sizeMax, className); - util_strReplaceChar(classNameP, '.', '/'); - #endif - - // get the AIs private class-loader - jobject o_global_aiClassLoader = java_createAIClassLoader(env, shortName, version); - - if (o_global_aiClassLoader == NULL) - return false; - - *o_aiClassLoader = o_global_aiClassLoader; - - // get the AI interface (from AIInterface.jar) - if (g_cls_ai_int == NULL) { - if ((g_cls_ai_int = jniUtil_findClass(env, INT_AI)) == NULL) - return false; - if ((g_cls_ai_int = jniUtil_makeGlobalRef(env, g_cls_ai_int, "AI interface class")) == NULL) - return false; - } - - // get the AI implementation class (from SkirmishAI.jar) - jclass cls_ai = jniUtil_findClassThroughLoader(env, o_global_aiClassLoader, className); - - if (cls_ai == NULL) - return false; - - const bool implementsAIInt = (bool) (*env)->IsAssignableFrom(env, cls_ai, g_cls_ai_int); - - if (!implementsAIInt || (*env)->ExceptionCheck(env)) { - simpleLog_logL(LOG_LEVEL_ERROR, "AI class not assignable from interface "INT_AI": %s", className); - simpleLog_logL(LOG_LEVEL_ERROR, "possible reasons (this list could be incomplete):"); - simpleLog_logL(LOG_LEVEL_ERROR, "* "INT_AI" interface not implemented"); - simpleLog_logL(LOG_LEVEL_ERROR, "* The AI is not compiled for the Java AI Interface version in use"); - - if (implementsAIInt) - (*env)->ExceptionDescribe(env); - - return false; - } - - - // get factory no-arg ctor - jmethodID m_ai_ctor = jniUtil_getMethodID(env, cls_ai, "", "()V"); - - if (m_ai_ctor == NULL) - return false; - - - // get AI instance - jobject o_local_ai = (*env)->NewObject(env, cls_ai, m_ai_ctor); - - if (o_local_ai == NULL || (*env)->ExceptionCheck(env)) { - simpleLog_logL(LOG_LEVEL_ERROR, "Failed fetching AI instance for class: %s", className); - - if (o_local_ai != NULL) - (*env)->ExceptionDescribe(env); - - return false; - } - - // make the AI a global reference, so it will not be garbage collected even after this method returns - *o_ai = jniUtil_makeGlobalRef(env, o_local_ai, "AI instance"); - return true; -} - - -bool java_initSkirmishAIClass( - const char* const shortName, - const char* const version, - const char* const className, - int skirmishAIId -) { - bool success = false; - - // see if an AI for className is instantiated already - size_t sai; - size_t firstFree = numSkirmishAIs; - - for (sai = 0; sai < numSkirmishAIs; ++sai) { - if (jAIClassNames[sai] == NULL) { - firstFree = sai; - break; - } - } - - // sai is now either the instantiated one, or a free one - // instantiate AI (if not already instantiated) - assert(sai < maxSkirmishAIs); - - if (jAIClassNames[sai] == NULL) { - sai = firstFree; - java_establishJavaEnv(); - JNIEnv* env = java_getJNIEnv(false); - - jobject instance = NULL; - jobject classLoader = NULL; - - success = java_loadSkirmishAI(env, shortName, version, className, &instance, &classLoader); - java_establishSpringEnv(); - - if (success) { - jAIInstances[sai] = instance; - jAIClassLoaders[sai] = classLoader; - jAIClassNames[sai] = util_allocStrCpy(className); - - numSkirmishAIs += (firstFree == numSkirmishAIs); - } else { - simpleLog_logL(LOG_LEVEL_ERROR, "Class loading failed for class: %s", className); - } - } else { - success = true; - } - - if (success) - skirmishAIId_skirmishAiImpl[skirmishAIId] = sai; - - return success; -} - -bool java_releaseSkirmishAIClass(const char* className) -{ - // see if an AI for className is instantiated - size_t sai; - for (sai = 0; sai < numSkirmishAIs; ++sai) { - if (jAIClassNames[sai] == NULL) - continue; - if (strcmp(jAIClassNames[sai], className) == 0) - break; - } - - // sai is now either the instantiated one, or a free one - // release AI (if its instance was found) - assert(sai < maxSkirmishAIs); - - if (jAIClassNames[sai] == NULL) - return false; - - java_establishJavaEnv(); - JNIEnv* env = java_getJNIEnv(false); - - - // delete the AI class-loader global reference, - // so it will be garbage collected - bool successPart = jniUtil_deleteGlobalRef(env, jAIClassLoaders[sai], "AI class-loader"); - bool success = successPart; - - // delete the AI global reference, - // so it will be garbage collected - successPart = jniUtil_deleteGlobalRef(env, jAIInstances[sai], "AI instance"); - success = success && successPart; - - java_establishSpringEnv(); - - if (success) { - jAIClassLoaders[sai] = NULL; - jAIInstances[sai] = NULL; - - FREE(jAIClassNames[sai]); - - // if it is the last implementation - numSkirmishAIs -= ((sai + 1) == numSkirmishAIs); - } - - return success; -} - -bool java_releaseAllSkirmishAIClasses() -{ - bool success = true; - - const char* className = ""; - size_t sai; - - for (sai = 0; sai < numSkirmishAIs; ++sai) { - if ((className = jAIClassNames[sai]) == NULL) - continue; - - success = success && java_releaseSkirmishAIClass(className); - } - - return success; -} - - - -int java_skirmishAI_init(int skirmishAIId, const struct SSkirmishAICallback* aiCallback) -{ - int res = -1; - - java_establishJavaEnv(); - - JNIEnv* env = java_getJNIEnv(false); - jobject global_javaAICallback = java_createAICallback(env, aiCallback, skirmishAIId); - - if (global_javaAICallback != NULL) - res = eventsJniBridge_initAI(env, skirmishAIId, global_javaAICallback); - - java_establishSpringEnv(); - return res; -} - -int java_skirmishAI_release(int skirmishAIId) -{ - return 0; -} - -int java_skirmishAI_handleEvent(int skirmishAIId, int topic, const void* data) -{ - java_establishJavaEnv(); - - JNIEnv* env = java_getJNIEnv(false); - const size_t sai = skirmishAIId_skirmishAiImpl[skirmishAIId]; - jobject aiInstance = jAIInstances[sai]; - const int res = eventsJniBridge_handleEvent(env, aiInstance, skirmishAIId, topic, data); - - java_establishSpringEnv(); - return res; -} diff --git a/AI/Interfaces/Java/src/main/native/JavaBridge.h b/AI/Interfaces/Java/src/main/native/JavaBridge.h deleted file mode 100644 index d742f47ef38..00000000000 --- a/AI/Interfaces/Java/src/main/native/JavaBridge.h +++ /dev/null @@ -1,73 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#ifndef _JAVA_BRIDGE_H -#define _JAVA_BRIDGE_H - -#define JVM_PROPERTIES_FILE "jvm.properties" - -#define PKG_AI "com/springrts/ai/" -#define INT_AI PKG_AI"AI" -#define CLS_AI_CALLBACK PKG_AI"JniAICallback" - -// define path entry delimiter, used eg for the java class-path -#ifdef _WIN32 -#define ENTRY_DELIM ";" -#else -#define ENTRY_DELIM ":" -#endif -#define PATH_DELIM "/" - -#ifdef __cplusplus -extern "C" { -#endif - -#include // bool, true, false - -struct SAIInterfaceCallback; -struct SSkirmishAICallback; - -bool java_unloadJNIEnv(); - -bool java_initStatic(int interfaceId, const struct SAIInterfaceCallback* callback); -bool java_releaseStatic(); - -/** - * Instantiates an instance of the specified className. - * - * @param shortName further specifies the the AI to load - * @param version further specifies the the AI to load - * @param className fully qualified name of a Java class that implements - * interface com.springrts.ai.AI, eg: - * "com.myai.AI" - * @param teamId The team that will be using this AI. - * Multiple teams may use the same AI implementation. - * @return true, if the AI implementation is now loaded - */ -bool java_initSkirmishAIClass( - const char* const shortName, - const char* const version, - const char* const className, - int teamId -); - -/** - * Release the loaded AI specified through a class name. - * - * @param className fully qualified name of a Java class that implements - * interface com.springrts.ai.AI, eg: - * "com.myai.AI" - * @return true, if the AI implementation was loaded and is now - * successfully unloaded - */ -bool java_releaseSkirmishAIClass(const char* className); -bool java_releaseAllSkirmishAIClasses(); - -int java_skirmishAI_init(int teamId, const struct SSkirmishAICallback* callback); -int java_skirmishAI_release(int teamId); -int java_skirmishAI_handleEvent(int teamId, int topic, const void* data); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _JAVA_BRIDGE_H diff --git a/AI/Interfaces/Java/src/main/native/JniUtil.c b/AI/Interfaces/Java/src/main/native/JniUtil.c deleted file mode 100644 index 909afc70bd3..00000000000 --- a/AI/Interfaces/Java/src/main/native/JniUtil.c +++ /dev/null @@ -1,288 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#include "JniUtil.h" - -#include "CUtils/SimpleLog.h" - - -// JNI global vars -static jclass g_cls_url = NULL; -static jmethodID g_m_url_ctor = NULL; - -static jclass g_cls_urlClassLoader = NULL; -static jmethodID g_m_urlClassLoader_ctor = NULL; -static jmethodID g_m_urlClassLoader_findClass = NULL; - - -const char* jniUtil_getJniRetValDescription(const jint retVal) { - - switch (retVal) { - case JNI_OK: { return "JNI_OK - success"; break; } - case JNI_ERR: { return "JNI_ERR - unknown error"; break; } - case JNI_EDETACHED: { return "JNI_EDETACHED - thread detached from the VM"; break; } - case JNI_EVERSION: { return "JNI_EVERSION - JNI version error"; break; } -#ifdef JNI_ENOMEM - case JNI_ENOMEM: { return "JNI_ENOMEM - not enough (contiguous) memory"; break; } -#endif // JNI_ENOMEM -#ifdef JNI_EEXIST - case JNI_EEXIST: { return "JNI_EEXIST - VM already created"; break; } -#endif // JNI_EEXIST -#ifdef JNI_EINVAL - case JNI_EINVAL: { return "JNI_EINVAL - invalid arguments"; break; } -#endif // JNI_EINVAL - default: { return "UNKNOWN - unknown/invalid JNI return value"; break; } - } -} - -bool jniUtil_checkException(JNIEnv* env, const char* const errorMsg) { - - if ((*env)->ExceptionCheck(env)) { - simpleLog_logL(LOG_LEVEL_ERROR, errorMsg); - (*env)->ExceptionDescribe(env); - return true; - } - - return false; -} - -jclass jniUtil_findClass(JNIEnv* env, const char* const className) { - - jclass res = NULL; - - res = (*env)->FindClass(env, className); - const bool hasException = (*env)->ExceptionCheck(env); - if (res == NULL || hasException) { - simpleLog_logL(LOG_LEVEL_ERROR, "Class not found: \"%s\"", className); - if (hasException) { - (*env)->ExceptionDescribe(env); - } - res = NULL; - } - - return res; -} - -jobject jniUtil_makeGlobalRef(JNIEnv* env, jobject localObject, const char* objDesc) { - - jobject res = NULL; - - // Make the local class a global reference, - // so it will not be garbage collected, - // even after this method returned, - // but only if explicitly deleted with DeleteGlobalRef - res = (*env)->NewGlobalRef(env, localObject); - if ((*env)->ExceptionCheck(env)) { - simpleLog_logL(LOG_LEVEL_ERROR, - "Failed to make %s a global reference.", - ((objDesc == NULL) ? "" : objDesc)); - (*env)->ExceptionDescribe(env); - res = NULL; - } - - return res; -} - -bool jniUtil_deleteGlobalRef(JNIEnv* env, jobject globalObject, - const char* objDesc) { - - // delete the AI class-loader global reference, - // so it will be garbage collected - (*env)->DeleteGlobalRef(env, globalObject); - if ((*env)->ExceptionCheck(env)) { - simpleLog_logL(LOG_LEVEL_ERROR, - "Failed to delete global reference %s.", - ((objDesc == NULL) ? "" : objDesc)); - (*env)->ExceptionDescribe(env); - return false; - } - - return true; -} - -jmethodID jniUtil_getMethodID(JNIEnv* env, jclass cls, - const char* const name, const char* const signature) { - - jmethodID res = NULL; - - res = (*env)->GetMethodID(env, cls, name, signature); - const bool hasException = (*env)->ExceptionCheck(env); - if (res == NULL || hasException) { - simpleLog_logL(LOG_LEVEL_ERROR, "Method not found: %s(%s)", - name, signature); - if (hasException) { - (*env)->ExceptionDescribe(env); - } - res = NULL; - } - - return res; -} - -jmethodID jniUtil_getStaticMethodID(JNIEnv* env, jclass cls, - const char* const name, const char* const signature) { - - jmethodID res = NULL; - - res = (*env)->GetStaticMethodID(env, cls, name, signature); - const bool hasException = (*env)->ExceptionCheck(env); - if (res == NULL || hasException) { - simpleLog_logL(LOG_LEVEL_ERROR, "Method not found: %s(%s)", - name, signature); - if (hasException) { - (*env)->ExceptionDescribe(env); - } - res = NULL; - } - - return res; -} - - -static bool jniUtil_initURLClass(JNIEnv* env) { - - if (g_m_url_ctor == NULL) { - // get the URL class - static const char* const fcCls = "java/net/URL"; - - g_cls_url = jniUtil_findClass(env, fcCls); - if (g_cls_url == NULL) return false; - - g_cls_url = jniUtil_makeGlobalRef(env, g_cls_url, fcCls); - if (g_cls_url == NULL) return false; - - // get (String) constructor - g_m_url_ctor = jniUtil_getMethodID(env, g_cls_url, - "", "(Ljava/lang/String;)V"); - if (g_m_url_ctor == NULL) return false; - } - - return true; -} -jobject jniUtil_createURLObject(JNIEnv* env, const char* const url) { - - jobject jurl = NULL; - - bool ok = true; - if (g_cls_url == NULL) { - ok = jniUtil_initURLClass(env); - } - - if (ok) { - jstring jstrUrl = (*env)->NewStringUTF(env, url); - if (jniUtil_checkException(env, "Failed creating Java String.")) { jstrUrl = NULL; } - if (jstrUrl != NULL) { - jurl = (*env)->NewObject(env, g_cls_url, g_m_url_ctor, jstrUrl); - if (jniUtil_checkException(env, "Failed creating Java URL.")) { jurl = NULL; } - } - } else { - simpleLog_logL(LOG_LEVEL_ERROR, - "Failed creating Java URL; URL class not initialized."); - } - - return jurl; -} -jobjectArray jniUtil_createURLArray(JNIEnv* env, size_t size) { - - jobjectArray jurlArr = NULL; - - bool ok = true; - if (g_cls_url == NULL) { - ok = jniUtil_initURLClass(env); - } - - if (ok) { - jurlArr = (*env)->NewObjectArray(env, size, g_cls_url, NULL); - if (jniUtil_checkException(env, "Failed creating URL[].")) { jurlArr = NULL; } - } else { - simpleLog_logL(LOG_LEVEL_ERROR, - "Failed creating Java URL[]; URL class not initialized."); - } - - return jurlArr; -} -bool jniUtil_insertURLIntoArray(JNIEnv* env, jobjectArray arr, size_t index, jobject url) { - - bool ok = true; - - (*env)->SetObjectArrayElement(env, arr, index, url); - if (jniUtil_checkException(env, "Failed inserting Java URL into array.")) { ok = false; } - - return ok; -} - -static bool jniUtil_initURLClassLoaderClass(JNIEnv* env) { - - if (g_m_urlClassLoader_findClass == NULL) { - // get the URLClassLoader class - static const char* const fcCls = "java/net/URLClassLoader"; - - g_cls_urlClassLoader = jniUtil_findClass(env, fcCls); - if (g_cls_urlClassLoader == NULL) return false; - - g_cls_urlClassLoader = - jniUtil_makeGlobalRef(env, g_cls_urlClassLoader, fcCls); - if (g_cls_urlClassLoader == NULL) return false; - - // get (URL[]) constructor - g_m_urlClassLoader_ctor = jniUtil_getMethodID(env, - g_cls_urlClassLoader, "", "([Ljava/net/URL;)V"); - if (g_m_urlClassLoader_ctor == NULL) return false; - - // get the findClass(String) method - g_m_urlClassLoader_findClass = jniUtil_getMethodID(env, - g_cls_urlClassLoader, "findClass", - "(Ljava/lang/String;)Ljava/lang/Class;"); - if (g_m_urlClassLoader_findClass == NULL) return false; - } - - return true; -} -jobject jniUtil_createURLClassLoader(JNIEnv* env, jobject urlArray) { - - jobject classLoader = NULL; - - bool ok = true; - if (g_m_urlClassLoader_ctor == NULL) { - ok = jniUtil_initURLClassLoaderClass(env); - } - - if (ok) { - classLoader = (*env)->NewObject(env, g_cls_urlClassLoader, g_m_urlClassLoader_ctor, urlArray); - if (jniUtil_checkException(env, "Failed creating class-loader.")) { return NULL; } - } else { - simpleLog_logL(LOG_LEVEL_ERROR, - "Failed creating class-loader; class-loader class not initialized."); - } - - return classLoader; -} -jclass jniUtil_findClassThroughLoader(JNIEnv* env, jobject classLoader, const char* const className) { - - jclass cls = NULL; - - bool ok = true; - if (g_m_urlClassLoader_findClass == NULL) { - ok = jniUtil_initURLClassLoaderClass(env); - } - - if (ok) { - //cls = (*env)->FindClass(env, classNameP); - jstring jstr_className = (*env)->NewStringUTF(env, className); - cls = (*env)->CallObjectMethod(env, classLoader, g_m_urlClassLoader_findClass, jstr_className); - const bool hasException = (*env)->ExceptionCheck(env); - if (cls == NULL || hasException) { - simpleLog_logL(LOG_LEVEL_ERROR, - "Class not found \"%s\"", className); - if (hasException) { - (*env)->ExceptionDescribe(env); - } - cls = NULL; - } - } else { - simpleLog_logL(LOG_LEVEL_ERROR, - "Failed finding class; class-loader class not initialized."); - } - - return cls; -} - diff --git a/AI/Interfaces/Java/src/main/native/JniUtil.h b/AI/Interfaces/Java/src/main/native/JniUtil.h deleted file mode 100644 index 336f2fc4f1e..00000000000 --- a/AI/Interfaces/Java/src/main/native/JniUtil.h +++ /dev/null @@ -1,80 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#ifndef _JNI_UTIL_H -#define _JNI_UTIL_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include // bool, true, false - -#include - -/** - * Takes a JNI function return value, and returns a short description for it. - * This can be used for getting a human readable string describing the - * return value of functions like AttachCurrentThread() and CreateJavaVM(). - * - * @return a short description for a JNI function return value - */ -const char* jniUtil_getJniRetValDescription(const jint retVal); - -/** - * Handles a possible JNI/Java exception. - * In case an exception is present, the supplied error message is written to - * the log file, and the whole exception info is written to stderr. - * - * @return true, if there was an exception - */ -bool jniUtil_checkException(JNIEnv* env, const char* const errorMsg); - -/** - * Tries to find a Java class. - * - * @return a local reference to the class if found, NULL otherwise - */ -jclass jniUtil_findClass(JNIEnv* env, const char* const className); - -/** - * Converts a local to a global reference. - * As jclass inherits from jobject, this can be used for jclass too. - * - * @return the global reference to localObject on success, NULL otherwise - */ -jobject jniUtil_makeGlobalRef(JNIEnv* env, jobject localObject, const char* objDesc); - -/** - * Deletes a global reference to an object. - * As jclass inherits from jobject, this can be used for jclass too. - * - * @return true on success, false if an error occurred - */ -bool jniUtil_deleteGlobalRef(JNIEnv* env, jobject globalObject, const char* objDesc); - -/** - * Retrieves a reference ID for calling a Java object method. - * - * @return the method ID on success, NULL otherwise - */ -jmethodID jniUtil_getMethodID(JNIEnv* env, jclass cls, const char* const name, const char* const signature); - -/** - * Retrieves a reference ID for calling a Java static method. - * - * @return the method ID on success, NULL otherwise - */ -jmethodID jniUtil_getStaticMethodID(JNIEnv* env, jclass cls, const char* const name, const char* const signature); - -jobject jniUtil_createURLObject(JNIEnv* env, const char* const url); -jobjectArray jniUtil_createURLArray(JNIEnv* env, size_t size); -bool jniUtil_insertURLIntoArray(JNIEnv* env, jobjectArray arr, size_t index, jobject url); - -jobject jniUtil_createURLClassLoader(JNIEnv* env, jobject urlArray); -jclass jniUtil_findClassThroughLoader(JNIEnv* env, jobject classLoader, const char* const className); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _JNI_UTIL_H diff --git a/AI/Interfaces/Java/src/main/native/JvmLocater.h b/AI/Interfaces/Java/src/main/native/JvmLocater.h deleted file mode 100644 index 5032991d6a0..00000000000 --- a/AI/Interfaces/Java/src/main/native/JvmLocater.h +++ /dev/null @@ -1,53 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#ifndef _JVM_LOCATER_H -#define _JVM_LOCATER_H - -#if !defined bool -#include -#endif -#if !defined size_t -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Returns the arch dir name, used eg here: - * .../jre/lib/${arch}/client/libjvm.so - * - * @return sparc, sparcv9, i386, amd64 or ia64 (windows only) - */ -const char* GetArchPath(); - -/* - * Given a JRE location and a JVM type, construct what the name the - * JVM shared library will be. - * - * @param jvmType "/", "\\", "client", "server" - * @param arch see GetArchPath(), use NULL for the default value - * @return true, if the JVM library was found, false otherwise. - */ -bool GetJVMPath(const char* jrePath, const char* jvmType, - char* jvmPath, size_t jvmPathSize, const char* arch); - -/** - * Find the path to a JRE install dir, using platform dependent means. - * - * @param path path of the JRE installation - * @param pathSize size of the path parameter - * @param configFile path to a simple text file containing only a path to the - * JRE installation to use, or NULL - * @param arch see GetArchPath(), use NULL for the default value - * @return true, if a JRE was found, false otherwise. - */ -bool GetJREPath(char* path, size_t pathSize, const char* configFile, - const char* arch); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _JVM_LOCATER_H diff --git a/AI/Interfaces/Java/src/main/native/JvmLocater_common.c b/AI/Interfaces/Java/src/main/native/JvmLocater_common.c deleted file mode 100644 index a85f9d87a95..00000000000 --- a/AI/Interfaces/Java/src/main/native/JvmLocater_common.c +++ /dev/null @@ -1,188 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#include -#include -#include -#include -#include - -#include "CUtils/Util.h" - -#include "JvmLocater.h" - -#define MAXPATHLEN 2048 -#define JRE_PATH_PROPERTY "jre.path" - -#include // for CHAR_BIT -#define CURRENT_DATA_MODEL (CHAR_BIT * sizeof(void*)) - -#include "CUtils/SimpleLog.h" -#include "System/MainDefines.h" -#include "System/SafeCStrings.h" - -// implemented in the OS specific files -const char* GetArchPath(); -bool GetJREPathFromBase(char* path, size_t pathSize, const char* basePath, - const char* arch); -bool GetJREPathOSSpecific(char* path, size_t pathSize, const char* arch); - -bool FileExists(const char* filePath) -{ - struct stat s; - return (stat(filePath, &s) == 0); -} - -//#define LOC_PROP_FILE -bool GetJREPathFromConfig(char* path, size_t pathSize, const char* configFile) -{ -#if defined LOC_PROP_FILE - // assume the config file is in properties file format - // and that the property JRE_PATH_PROPERTY contains - // the absolute path to the JRE to use - static const size_t props_sizeMax = 64; - - const char* props_keys[props_sizeMax]; - const char* props_values[props_sizeMax]; - - const size_t props_size = util_parsePropertiesFile(configFile, props_keys, - props_values, props_sizeMax); - - const char* jvmLocation = util_map_getValueByKey( - props_size, props_keys, props_values, - JRE_PATH_PROPERTY); - - if (jvmLocation == NULL) { - simpleLog_logL(LOG_LEVEL_DEBUG, "JRE not found in config file!"); - return false; - } else { - simpleLog_logL(LOG_LEVEL_NOTICE, "JRE found in config file!"); - STRCPY_T(path, pathSize, jvmLocation); - return true; - } -#else // defined LOC_PROP_FILE - // assume the config file is a plain text file containing nothing but - // the absolute path to the JRE to use in the first line - - bool found = false; - - FILE* cfp = fopen(configFile, "r"); - if (cfp == NULL) { - return found; - } - - // parse results - static const size_t line_sizeMax = 1024; - char line[line_sizeMax]; - if (fgets(line, line_sizeMax, cfp)) { - size_t line_size = strlen(line); - if (*(line+line_size-1) == '\n') { - // remove trailing '\n' - *(line+line_size-1) = '\0'; - line_size--; - } - - simpleLog_logL(LOG_LEVEL_NOTICE, - "Fetched JRE location from \"%s\"!", configFile); - - if (line_size > 0 && *line == '/') { - *(line+line_size-1) = '\0'; // remove trailing '/' - } - STRCPY_T(path, pathSize, line); - found = true; - } - fclose(cfp); - - return found; -#endif // defined LOC_PROP_FILE -} - - -bool GetJREPathFromEnvVars(char* path, size_t pathSize, const char* arch) -{ - bool found = false; - - static const size_t possLoc_sizeMax = 32; - char* possLoc[possLoc_sizeMax]; - size_t possLoc_i = 0; - - possLoc[possLoc_i++] = util_allocStrCpy("JAVA_HOME"); - possLoc[possLoc_i++] = util_allocStrCpy("JDK_HOME"); - possLoc[possLoc_i++] = util_allocStrCpy("JRE_HOME"); - - size_t l; - for (l=0; l < possLoc_i; ++l) { - const char* envPath = getenv(possLoc[l]); - if (envPath != NULL) { - found = GetJREPathFromBase(path, pathSize, envPath, arch); - if (found) { - simpleLog_logL(LOG_LEVEL_NOTICE, "JRE found in env var \"%s\"!", possLoc[l]); - goto locSearchEnd; - } else { - simpleLog_logL(LOG_LEVEL_WARNING, "Unusable JRE from env var \"%s\"=\"%s\"!", possLoc[l], envPath); - } - } - } - locSearchEnd: - - // cleanup - for (l=0; l < possLoc_i; ++l) { - free(possLoc[l]); - possLoc[l] = NULL; - } - - return found; -} - - - -bool GetJREPath(char* path, size_t pathSize, const char* configFile, - const char* arch) -{ - bool found = false; - - if (arch == NULL) { - arch = GetArchPath(); - } - - // check if a JRE location is specified in the config file - if (!found && configFile != NULL) { - found = GetJREPathFromConfig(path, pathSize, configFile); - } - - // check if a JRE is specified in an ENV var (eg. JAVA_HOME) - if (!found) { - found = GetJREPathFromEnvVars(path, pathSize, arch); - } - - // check if a JRE is located in a common location - if (!found) { - found = GetJREPathOSSpecific(path, pathSize, arch); - } - - return found; -} - -int main(int argc, const char* argv[]) { - - //simpleLog_init(NULL, false, LOG_LEVEL_DEBUG, false); - - static const size_t path_sizeMax = 1024; - char path[path_sizeMax]; - bool found = GetJREPath(path, path_sizeMax, NULL, NULL); - if (found) { - printf("JRE found: %s\n", path); - - static const size_t jvmPath_sizeMax = 1024; - char jvmPath[jvmPath_sizeMax]; - bool jvmFound = GetJVMPath(path, "client", jvmPath, jvmPath_sizeMax, NULL); - if (jvmFound) { - printf("JVM found: %s\n", jvmPath); - } else { - printf("JVM not found.\n"); - } - } else { - printf("JRE not found.\n"); - } - - return 0; -} diff --git a/AI/Interfaces/Java/src/main/native/JvmLocater_common.h b/AI/Interfaces/Java/src/main/native/JvmLocater_common.h deleted file mode 100644 index 45d81c2b9ac..00000000000 --- a/AI/Interfaces/Java/src/main/native/JvmLocater_common.h +++ /dev/null @@ -1,38 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#ifndef _JVM_LOCATER_COMMON_H -#define _JVM_LOCATER_COMMON_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "JvmLocater.h" - -#include -#include -#include -#include -#include -#include // for CHAR_BIT - -#include "CUtils/Util.h" -#include "CUtils/SimpleLog.h" - -#define MAXPATHLEN 2048 -#define JRE_PATH_PROPERTY "jre.path" -#define CURRENT_DATA_MODEL (CHAR_BIT * sizeof(void*)) - - -bool FileExists(const char* filePath); - -bool GetJREPathFromEnvVars(char* path, size_t pathSize, const char* arch); - -bool GetJREPath(char* path, size_t pathSize, const char* configFile, - const char* arch); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // _JVM_LOCATER_COMMON_H diff --git a/AI/Interfaces/Java/src/main/native/JvmLocater_linux.c b/AI/Interfaces/Java/src/main/native/JvmLocater_linux.c deleted file mode 100644 index ef1896be34b..00000000000 --- a/AI/Interfaces/Java/src/main/native/JvmLocater_linux.c +++ /dev/null @@ -1,266 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#if !defined _WIN32 - -#include "JvmLocater_common.h" - -#include "System/MainDefines.h" -#include "System/SafeCStrings.h" - -#include - - -#if defined APPLE -#define JVM_LIB "libjvm.dylib" -#else -#define JVM_LIB "libjvm.so" -#endif - -#if defined APPLE -#define JAVA_LIB "libjava.dylib" -#else -#define JAVA_LIB "libjava.so" -#endif - -#ifdef SPARC -# define LIBARCH32NAME "sparc" -# define LIBARCH64NAME "sparcv9" -#else // not SPARC -> LINUX or APPLE -# define LIBARCH32NAME "i386" -# define LIBARCH64NAME "amd64" -#endif // SPARC -#if defined __arch64__ -# define LIBARCHNAME LIBARCH64NAME -#else // defined __arch64__ -# define LIBARCHNAME LIBARCH32NAME -#endif // defined __arch64__ -//LIBARCH32 solaris only: sparc or i386 -//LIBARCH64 solaris only: sparcv9 or amd64 -//LIBARCH sparc, sparcv9, i386, amd64, or ia64 // last one is windows only - - -const char* GetArchPath() -{ - switch(CURRENT_DATA_MODEL) { -#ifdef DUAL_MODE - case 32: - return LIBARCH32NAME; - case 64: - return LIBARCH64NAME; -#endif // DUAL_MODE - default: - return LIBARCHNAME; - } -} - -/* - * On Solaris VM choosing is done by the launcher (java.c). - */ -bool GetJVMPath(const char* jrePath, const char* jvmType, - char* jvmPath, size_t jvmPathSize, const char* arch) -{ - if (arch == NULL) { - arch = GetArchPath(); - } - - if (*jvmType == '/') { - SNPRINTF(jvmPath, jvmPathSize, "%s/"JVM_LIB, jvmType); - } else { - SNPRINTF(jvmPath, jvmPathSize, "%s/lib/%s/%s/"JVM_LIB, jrePath, arch, - jvmType); - } - - return FileExists(jvmPath); -} - - -static bool CheckIfJREPath(const char* path, const char* arch) -{ - bool found = false; - - if (path != NULL) { - char libJava[MAXPATHLEN]; - - // Is path a JRE path? - SNPRINTF(libJava, MAXPATHLEN, "%s/lib/%s/"JAVA_LIB, path, arch); - if (access(libJava, F_OK) == 0) { - found = true; - } - } - - return found; -} - -bool GetJREPathFromBase(char* path, size_t pathSize, const char* basePath, - const char* arch) -{ - bool found = false; - - if (basePath != NULL) { - char jrePath[MAXPATHLEN]; - - // Is basePath a JRE path? - STRCPY_T(jrePath, MAXPATHLEN, basePath); - if (CheckIfJREPath(jrePath, arch)) { - STRCPY_T(path, pathSize, basePath); - found = true; - } - - // Is basePath/jre a JRE path? - STRCAT_T(jrePath, MAXPATHLEN, "/jre"); - if (CheckIfJREPath(jrePath, arch)) { - STRCPY_T(path, pathSize, jrePath); - found = true; - } - } - - return found; -} - -static size_t ExecFileSystemGlob(char** pathHits, size_t pathHits_sizeMax, - const char* globPattern) -{ - size_t pathHits_size = 0; - - // assemble the command - static const size_t cmd_sizeMax = 512; - char cmd[cmd_sizeMax]; - SNPRINTF(cmd, cmd_sizeMax, "find %s/ -maxdepth 0 2> /dev/null", globPattern); - - // execute - FILE* cmd_fp = popen(cmd, "r"); - if (cmd_fp == NULL) { - return pathHits_size; - } - - // parse results - static const size_t line_sizeMax = 512; - char line[line_sizeMax]; - while (fgets(line, line_sizeMax, cmd_fp) && (pathHits_size < pathHits_sizeMax)) { - size_t line_size = strlen(line); - if (*(line+line_size-1) == '\n') { - // remove trailing '\n' - *(line+line_size-1) = '\0'; - line_size--; - } - - simpleLog_logL(LOG_LEVEL_DEBUG, - "glob-hit \"%s\"!", line); - - if (line_size > 0 && *line == '/') { - *(line+line_size-1) = '\0'; // remove trailing '/' - pathHits[pathHits_size++] = util_allocStrCpy(line); - } - } - pclose(cmd_fp); - - return pathHits_size; -} -static bool GetJREPathInCommonLocations(char* path, size_t pathSize, const char* arch) -{ - bool found = false; - - static const size_t possLoc_sizeMax = 32; - char* possLoc[possLoc_sizeMax]; - size_t possLoc_i = 0; - - possLoc[possLoc_i++] = util_allocStrCpy("/usr/local/jdk*"); - possLoc[possLoc_i++] = util_allocStrCpy("/usr/lib/jvm/default-java"); - possLoc[possLoc_i++] = util_allocStrCpy("/usr/lib/jvm/java-?-sun"); - possLoc[possLoc_i++] = util_allocStrCpy("/usr/lib/jvm/java-?-*"); - possLoc[possLoc_i++] = util_allocStrCpy("~/jdk*"); - possLoc[possLoc_i++] = util_allocStrCpy("~/bin/jdk*"); - possLoc[possLoc_i++] = util_allocStrCpy("~/jre*"); - possLoc[possLoc_i++] = util_allocStrCpy("~/bin/jre*"); - - static const size_t globHits_sizeMax = 32; - char* globHits[globHits_sizeMax]; - size_t l, g; - for (l=0; l < possLoc_i; ++l) { - const size_t globHits_size = ExecFileSystemGlob(globHits, globHits_sizeMax, possLoc[l]); - for (g=0; g < globHits_size; ++g) { - found = GetJREPathFromBase(path, pathSize, globHits[g], arch); - if (found) { - simpleLog_logL(LOG_LEVEL_NOTICE, - "JRE found common location env var \"%s\"!", - possLoc[l]); - goto locSearchEnd; - } - } - } - locSearchEnd: - - // cleanup - for (l=0; l < possLoc_i; ++l) { - free(possLoc[l]); - possLoc[l] = NULL; - } - - return found; -} - -static bool GetJREPathWhichJava(char* path, size_t pathSize, const char* arch) -{ - static const char* suf = "/bin/java"; - const size_t suf_size = strlen(suf); - - bool found = false; - - // execute - FILE* cmd_fp = popen("which java | sed 's/[\\n\\r]/K/g'", "r"); - if (cmd_fp == NULL) { - return found; - } - - // parse results - static const size_t line_sizeMax = 512; - char line[line_sizeMax]; - if (fgets(line, line_sizeMax, cmd_fp)) { - if (*line == '/') { // -> absolute path - size_t line_size = strlen(line); - if (*(line+line_size-1) == '\n') { - // remove trailing '\n' - *(line+line_size-1) = '\0'; - line_size--; - } - - simpleLog_logL(LOG_LEVEL_DEBUG, - "which line \"%s\"!", line); - - if (line_size > suf_size - && strcmp(line+(line_size-suf_size), suf) == 0) { - // line ends with suf - simpleLog_logL(LOG_LEVEL_NOTICE, - "JRE found with `which java`!"); - // remove suf - *(line+(line_size-suf_size)) = '\0'; - found = GetJREPathFromBase(path, pathSize, line, arch); - } - } - } - pclose(cmd_fp); - - return found; -} - -/* - * Find path to JRE based on .exe's location or registry settings. - */ -bool GetJREPathOSSpecific(char* path, size_t pathSize, const char* arch) -{ - bool found = false; - - // check if a JRE is located in a common location - if (!found) { - found = GetJREPathInCommonLocations(path, pathSize, arch); - } - - // check if a JRE is located in a common location - if (!found) { - found = GetJREPathWhichJava(path, pathSize, arch); - } - - return found; -} - -#endif // !defined _WIN32 diff --git a/AI/Interfaces/Java/src/main/native/JvmLocater_windows.c b/AI/Interfaces/Java/src/main/native/JvmLocater_windows.c deleted file mode 100644 index 6bb5367f2b6..00000000000 --- a/AI/Interfaces/Java/src/main/native/JvmLocater_windows.c +++ /dev/null @@ -1,181 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -#if defined _WIN32 - -#include "JvmLocater_common.h" - -#include "System/MainDefines.h" -#include "System/SafeCStrings.h" - -#include -#include -#include - - -#define JVM_LIB "jvm.dll" - -#define JAVA_LIB "java.dll" - -/* - * Returns the arch path, to get the current arch use the - * macro GetArch, nbits here is ignored for now. - */ -const char* GetArchPath() -{ -#ifdef _M_AMD64 - return "amd64"; -#elif defined(_M_IA64) - return "ia64"; -#else - return "i386"; -#endif -} - -/* - * Given a JRE location and a JVM type, construct what the name the - * JVM shared library will be. Return true, if such a library - * exists, false otherwise. - */ -bool GetJVMPath(const char* jrePath, const char* jvmType, - char* jvmPath, size_t jvmPathSize, const char* arch) -{ - if (arch == NULL) { - arch = GetArchPath(); - } - - if ((*jvmType == '/') || (*jvmType == '\\')) { - SNPRINTF(jvmPath, jvmPathSize, "%s\\"JVM_LIB, jvmType); - } else { - SNPRINTF(jvmPath, jvmPathSize, "%s\\bin\\%s\\"JVM_LIB, jrePath, - jvmType); - } - - return FileExists(jvmPath); -} - -static bool CheckIfJREPath(const char* path, const char* arch) -{ - bool found = false; - - if (path != NULL) { - char libJava[MAXPATHLEN]; - - // Is path a JRE path? - SNPRINTF(libJava, MAXPATHLEN, "%s\\bin\\"JAVA_LIB, path); - if (FileExists(libJava)) { - found = true; - } - } - - return found; -} - -bool GetJREPathFromBase(char* path, size_t pathSize, const char* basePath, - const char* arch) -{ - bool found = false; - - if (basePath != NULL) { - //if (GetApplicationHome(path, pathSize)) { - char jrePath[MAXPATHLEN]; - - // Is basePath a JRE path? - STRCPY_T(jrePath, MAXPATHLEN, basePath); - if (CheckIfJREPath(jrePath, arch)) { - STRCPY_T(path, pathSize, basePath); - found = true; - } - - // Is basePath/jre a JRE path? - STRCAT_T(jrePath, MAXPATHLEN, "\\jre"); - if (CheckIfJREPath(jrePath, arch)) { - STRCPY_T(path, pathSize, jrePath); - found = true; - } - } - - return found; -} - -/* - * Helpers to look in the registry for a public JRE. - */ - -/* Same for 1.5.0, 1.5.1, 1.5.2 etc. */ -#define JRE_REG_KEY "Software\\JavaSoft\\Java Runtime Environment" - -static bool GetStringFromRegistry(HKEY key, const char* name, char* buf, - size_t bufSize) -{ - DWORD type, size; - - if (RegQueryValueEx(key, name, 0, &type, 0, &size) == 0 - && type == REG_SZ - && (size < bufSize)) { - if (RegQueryValueEx(key, name, 0, 0, (LPBYTE)buf, &size) == 0) { - return true; - } - } - return false; -} - -static bool GetJREPathFromRegistry(char* path, size_t pathSize, const char* arch) -{ - HKEY key, subkey; - char version[MAXPATHLEN]; - - /* - * Note: There is a very similar implementation of the following - * registry reading code in the Windows java control panel (javacp.cpl). - * If there are bugs here, a similar bug probably exists there. Hence, - * changes here require inspection there. - */ - - // Find the current version of the JRE - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, JRE_REG_KEY, 0, KEY_READ, &key) != 0) { - return false; - } - - if (!GetStringFromRegistry(key, "CurrentVersion", - version, sizeof(version))) { - RegCloseKey(key); - return false; - } - - /*if (strcmp(version, wantedVersion) != 0) { - RegCloseKey(key); - return false; - }*/ - - // Find directory where the current version is installed. - if (RegOpenKeyEx(key, version, 0, KEY_READ, &subkey) != 0) { - RegCloseKey(key); - return false; - } - - if (!GetStringFromRegistry(subkey, "JavaHome", path, pathSize)) { - RegCloseKey(key); - RegCloseKey(subkey); - return false; - } - - RegCloseKey(key); - RegCloseKey(subkey); - - simpleLog_logL(LOG_LEVEL_NOTICE, "JRE found in registry!"); - return true; -} - -bool GetJREPathOSSpecific(char* path, size_t pathSize, const char* arch) -{ - bool found = false; - - // check if a JRE is specified in the registry - if (!found) { - found = GetJREPathFromRegistry(path, pathSize, arch); - } - - return found; -} - -#endif // defined _WIN32 diff --git a/AI/Skirmish/BARb b/AI/Skirmish/BARb index 0ef36267633..e6b33037dfb 160000 --- a/AI/Skirmish/BARb +++ b/AI/Skirmish/BARb @@ -1 +1 @@ -Subproject commit 0ef36267633d6c1b2f6408a8d8a59fff38745dc3 +Subproject commit e6b33037dfbe81f2b0eb289f884ed90a1718eec0 diff --git a/AI/Skirmish/NullJavaAI/CMakeLists.txt b/AI/Skirmish/NullJavaAI/CMakeLists.txt deleted file mode 100644 index ffac49c05b4..00000000000 --- a/AI/Skirmish/NullJavaAI/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -### Generic Java Skirmish AI config -# - -configure_java_skirmish_ai("") diff --git a/AI/Skirmish/NullJavaAI/VERSION b/AI/Skirmish/NullJavaAI/VERSION deleted file mode 100644 index ceab6e11ece..00000000000 --- a/AI/Skirmish/NullJavaAI/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.1 \ No newline at end of file diff --git a/AI/Skirmish/NullJavaAI/bin/ant.dependent.properties b/AI/Skirmish/NullJavaAI/bin/ant.dependent.properties deleted file mode 100644 index 559cacc4e54..00000000000 --- a/AI/Skirmish/NullJavaAI/bin/ant.dependent.properties +++ /dev/null @@ -1,36 +0,0 @@ -# properties file for spring Java Skirmish AIs -# This is used when you do not use the spring repository for compiling. -# -# Paths are relative to the project home (which is ../ from this file). -# All values are optional, and listed here with their defaults. -# -# The default assumes that you have the spring source, -# and do a spring in-source build as it is the default with CMake and SCons, -# and as the buildbot does it. -# -# build-dir: ${spring.home}/AI/Skirmish/${ai.name}/ -# install-dir: ${spring.home}/dist/AI/Skirmish/${ai.name}/${ai.version}/ -# -# This file is loaded from within build.xml, -# but only if no "jlibs-interface/" dir is present. -# - -;spring.home=../../.. - -# This is used only in the next property -;build.home=${spring.home} -# Where jar files will be built -;build.dir=${build.home}/AI/Skirmish/${skirmish.ai.name} - -# This is used only in the next property -;dist.home=${spring.home}/dist -# Where jar files will be installed to -;dist.dir=${dist.home}/AI/Skirmish/${skirmish.ai.name}/${skirmish.ai.version} -# Where HTML JavaDoc files will be generated -;doc.dir=${dist.home}/AI/Skirmish/${skirmish.ai.name}/${skirmish.ai.version}/doc/jdoc - -# The following two are needed for compiling (to create the classpath) -# * Here we look for ./jlib/*.jar recursively -;ai.interface.src.home=${spring.home}/AI/Interfaces/Java -# * Here we look for AIInterface.jar -;ai.interface.build.home=${build.home}/AI/Interfaces/Java diff --git a/AI/Skirmish/NullJavaAI/bin/ant.independent.properties b/AI/Skirmish/NullJavaAI/bin/ant.independent.properties deleted file mode 100644 index 129a1589a21..00000000000 --- a/AI/Skirmish/NullJavaAI/bin/ant.independent.properties +++ /dev/null @@ -1,29 +0,0 @@ -# properties file for spring Java Skirmish AIs -# This is used when you do not use the spring repository for compiling. -# -# Paths are relative to the project home (which is ../ from this file). -# All values are optional, and listed here with their defaults. -# -# For the project, you do not need the spring sources, -# but you will need the "jlibs-interface/" instead, which contains -# the Java AI Interfaces jlibs plus its binary and source jar. -# -# build-dir: build/ -# install-dir: dist/ -# -# This file is loaded from within build.xml, -# but only if a "jlibs-interface/" dir is present. -# - -# This is used only in the next property -;build.home=build -# Where jar files will be built -;build.dir=${build.home} - -# This is used only in the next property. -# You will want to set dist.home to your spring install dir. -;dist.home=dist -# Where jar files will be installed to -;dist.dir=${dist.home}/AI/Skirmish/${skirmish.ai.name}/${skirmish.ai.version} -# Where HTML JavaDoc files will be generated -;doc.dir=${dist.home}/AI/Skirmish/${skirmish.ai.name}/${skirmish.ai.version}/doc/jdoc diff --git a/AI/Skirmish/NullJavaAI/bin/build.xml b/AI/Skirmish/NullJavaAI/bin/build.xml deleted file mode 100644 index 8d3b185b6bf..00000000000 --- a/AI/Skirmish/NullJavaAI/bin/build.xml +++ /dev/null @@ -1,279 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . -File not found: ${ai.interface.jar} -Please make sure to compile the Java AI Interface -before trying to compile this AI, or request -an independent version of this project from -wherever you got this AI from. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Successfully bumped ${skirmish.ai.name} version from ${skirmish.ai.version} to ${skirmish.ai.version.new}. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . -Please specify either home.new or name.new, for example: - ant create-independent -Dhome.new=~/projects/SpringSkirmishAIs/ -or - ant create-independent -Dname.new=MyJavaAI - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Spring source independant Java Skirmish AI project copied to: - ${project.dir.new} - -You may want to change your java source package structure -and the data/AIInto.lua file for the new project. - - - - - - - - - - - - - - - - - diff --git a/AI/Skirmish/NullJavaAI/data/AIInfo.lua b/AI/Skirmish/NullJavaAI/data/AIInfo.lua deleted file mode 100644 index 75f19a8632b..00000000000 --- a/AI/Skirmish/NullJavaAI/data/AIInfo.lua +++ /dev/null @@ -1,53 +0,0 @@ --- --- Info Definition Table format --- --- --- These keywords must be lowercase for LuaParser to read them. --- --- key: user defined or one of the SKIRMISH_AI_PROPERTY_* defines in --- SSkirmishAILibrary.h --- value: the value of the property --- desc: the description (could be used as a tooltip) --- --- --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -local infos = { - { - key = 'shortName', - value = 'NullJavaAI', - desc = 'machine conform name.', - }, - { - key = 'version', - value = '0.1', -- AI version - !This comment is used for parsing! - }, - { - key = 'className', - value = 'nulljavaai.NullJavaAI', - desc = 'fully qualified name of a class that implements interface com.springrts.ai.AI', - }, - { - key = 'name', - value = 'low-level Java test Skirmish AI', - desc = 'human readable name.', - }, - { - key = 'loadSupported', - value = 'no', - desc = 'whether this AI supports loading or not', - }, - { - key = 'interfaceShortName', - value = 'Java', -- AI Interface name - !This comment is used for parsing! - desc = 'the shortName of the AI interface this AI needs', - }, - { - key = 'interfaceVersion', - value = '0.1', -- AI Interface version - !This comment is used for parsing! - desc = 'the minimum version of the AI interface required by this AI', - }, -} - -return infos diff --git a/AI/Skirmish/NullJavaAI/data/AIOptions.lua b/AI/Skirmish/NullJavaAI/data/AIOptions.lua deleted file mode 100644 index d435d62e964..00000000000 --- a/AI/Skirmish/NullJavaAI/data/AIOptions.lua +++ /dev/null @@ -1,15 +0,0 @@ --- --- Custom Options Definition Table format --- --- A detailed example of how this format works can be found --- in the spring source under: --- AI/Skirmish/NullAI/data/AIOptions.lua --- --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -local options = { -} - -return options - diff --git a/AI/Skirmish/NullJavaAI/manifest.mf b/AI/Skirmish/NullJavaAI/manifest.mf deleted file mode 100755 index 59499bce4a2..00000000000 --- a/AI/Skirmish/NullJavaAI/manifest.mf +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/AI/Skirmish/NullJavaAI/src/nulljavaai/NullJavaAI.java b/AI/Skirmish/NullJavaAI/src/nulljavaai/NullJavaAI.java deleted file mode 100644 index 41c42c6c235..00000000000 --- a/AI/Skirmish/NullJavaAI/src/nulljavaai/NullJavaAI.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - Copyright (c) 2008 Robin Vobruba - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package nulljavaai; - - -import com.springrts.ai.*; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.logging.*; - -/** - * Serves as Interface for a Java Skirmish AIs for the Spring engine. - * - * @author hoijui - * @version 0.1 - */ -public class NullJavaAI extends AbstractAI implements AI { - - private int skirmishAIId = -1; - private AICallback clb = null; - private String myLogFile = null; - private Logger log = null; - private int frame = -1; - - private static class MyCustomLogFormatter extends Formatter { - - private DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSS dd.MM.yyyy"); - - public String format(LogRecord record) { - - // Create a StringBuffer to contain the formatted record - // start with the date. - StringBuffer sb = new StringBuffer(); - - // Get the date from the LogRecord and add it to the buffer - Date date = new Date(record.getMillis()); - sb.append(dateFormat.format(date)); - sb.append(" "); - - // Get the level name and add it to the buffer - sb.append(record.getLevel().getName()); - sb.append(": "); - - // Get the formatted message (includes localization - // and substitution of parameters) and add it to the buffer - sb.append(formatMessage(record)); - sb.append("\n"); - - return sb.toString(); - } - } - - public static boolean isDebugging() { - return true; - } - - public NullJavaAI() {} - - - @Override - public int init(int skirmishAIId, AICallback callback) { - - int ret = -1; - - this.clb = callback; - - // initialize the log - try { - int teamId = clb.SkirmishAI_getTeamId(); - myLogFile = callback.DataDirs_locatePath("log-team-" + teamId + "-" + skirmishAIId + ".txt", true, true, false, false); - FileHandler fileLogger = new FileHandler(myLogFile, false); - fileLogger.setFormatter(new MyCustomLogFormatter()); - fileLogger.setLevel(Level.ALL); - log = Logger.getLogger("nulljavaai"); - if (isDebugging()) { - log.setLevel(Level.ALL); - } else { - log.setLevel(Level.INFO); - } - log.addHandler(fileLogger); - } catch (Exception ex) { - System.out.println("NullJavaAI: Failed initializing the logger!"); - ex.printStackTrace(); - ret = -2; - } - - try { - int teamId = clb.SkirmishAI_getTeamId(); - log.info("initializing team " + teamId + ", id " + skirmishAIId); - - int numInfo = callback.SkirmishAI_Info_getSize(); - log.info("info (items: " + numInfo + ") ..."); - for (int i = 0; i < numInfo; i++) { - String key = callback.SkirmishAI_Info_getKey(i); - String value = callback.SkirmishAI_Info_getValue(i); - log.info(key + " = " + value); - } - - int numOptions = callback.SkirmishAI_OptionValues_getSize(); - log.info("options (items: " + numOptions + ") ..."); - for (int i = 0; i < numOptions; i++) { - String key = callback.SkirmishAI_OptionValues_getKey(i); - String value = callback.SkirmishAI_OptionValues_getValue(i); - log.info(key + " = " + value); - } - - ret = 0; - } catch (Exception ex) { - log.log(Level.SEVERE, "Failed initializing", ex); - log.log(Level.SEVERE, "msg: " + ex.getMessage()); - StackTraceElement[] stackTrace = ex.getStackTrace(); - for (int i = 0; i < stackTrace.length; i++) { - log.log(Level.SEVERE, "ste: " + stackTrace[i].toString()); - } - ret = -3; - } - - { - int numOptions = clb.SkirmishAI_OptionValues_getSize(); - log.info("sizeOptions: " + numOptions); - log.info("options:"); -// Pointer[] pKeys = evt.optionKeys.getPointerArray(0L, evt.sizeOptions); -// Pointer[] pValues = evt.optionValues.getPointerArray(0L, evt.sizeOptions); -// Properties options = new Properties(); -// for (int i = 0; i < evt.sizeOptions; i++) { -// options.setProperty(pKeys[i].getString(0L), pValues[i].getString(0L)); -// } - for (int i = 0; i < numOptions; i++) { - String key = clb.SkirmishAI_OptionValues_getKey(i); - String value = clb.SkirmishAI_OptionValues_getValue(i); - log.info(key + " = " + value); - } - log.info("stored"); - } - - return ret; - } - - @Override - public int update(int frame) { - - try { - log.finer("update event ..."); - log.finer("frame: " + frame); - } catch (Exception ex) { - log.log(Level.WARNING, "Failed handling event", ex); - return -1; - } - - return 0; - } - - @Override - public int release(int reason) { - - int ret = -1; - - try { - int teamId = clb.SkirmishAI_getTeamId(); - log.info("releasing team " + teamId + ", id " + skirmishAIId); - - ret = 0; - } catch (Exception ex) { - log.log(Level.WARNING, "Failed releasing", ex); - ret = -2; - } - - return ret; - } -} diff --git a/AI/Skirmish/NullOOJavaAI/CMakeLists.txt b/AI/Skirmish/NullOOJavaAI/CMakeLists.txt deleted file mode 100644 index 195bb8f6313..00000000000 --- a/AI/Skirmish/NullOOJavaAI/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -### Generic Java Skirmish AI config -# - -configure_java_skirmish_ai("JavaOO") diff --git a/AI/Skirmish/NullOOJavaAI/VERSION b/AI/Skirmish/NullOOJavaAI/VERSION deleted file mode 100644 index ceab6e11ece..00000000000 --- a/AI/Skirmish/NullOOJavaAI/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.1 \ No newline at end of file diff --git a/AI/Skirmish/NullOOJavaAI/bin/ant.dependent.properties b/AI/Skirmish/NullOOJavaAI/bin/ant.dependent.properties deleted file mode 100644 index 71674185fe3..00000000000 --- a/AI/Skirmish/NullOOJavaAI/bin/ant.dependent.properties +++ /dev/null @@ -1,40 +0,0 @@ -# properties file for spring Java Skirmish AIs -# This is used when you do not use the spring repository for compiling. -# -# Paths are relative to the project home (which is ../ from this file). -# All values are optional, and listed here with their defaults. -# -# The default assumes that you have the spring source, -# and do a spring in-source build as it is the default with CMake and SCons, -# and as the buildbot does it. -# -# build-dir: ${spring.home}/AI/Skirmish/${ai.name}/ -# install-dir: ${spring.home}/dist/AI/Skirmish/${ai.name}/${ai.version}/ -# -# This file is loaded from within build.xml, -# but only if "Java-AIInterface/" and "JavaOO-AIWrapper/" dirs are not present. -# - -;spring.home=../../.. - -# This is used only in the next property -;build.home=${spring.home} -# Where jar files will be built -;build.dir=${build.home}/AI/Skirmish/${skirmish.ai.name} - -# This is used only in the next property -;dist.home=${spring.home}/dist -# Where jar files will be installed to -;dist.dir=${dist.home}/AI/Skirmish/${skirmish.ai.name}/${skirmish.ai.version} -# Where HTML JavaDoc files will be generated -;doc.dir=${dist.home}/AI/Skirmish/${skirmish.ai.name}/${skirmish.ai.version}/doc/jdoc - -# The following four are needed for compiling (to create the classpath) -# * Here we look for ./data/jlib/*.jar recursively -;ai.interface.src.home=${spring.home}/AI/Interfaces/Java -# * Here we look for AIInterface.jar -;ai.interface.build.home=${build.home}/AI/Interfaces/Java -# * Here we look for ./jlib/*.jar recursively -;ai.wrapper.oo.src.home=${spring.home}/AI/Wrappers/JavaOO -# * Here we look for JavaOO-AIWrapper.jar -;ai.wrapper.oo.build.home=${build.home}/AI/Wrappers/JavaOO diff --git a/AI/Skirmish/NullOOJavaAI/bin/ant.independent.properties b/AI/Skirmish/NullOOJavaAI/bin/ant.independent.properties deleted file mode 100644 index 4f5648f2935..00000000000 --- a/AI/Skirmish/NullOOJavaAI/bin/ant.independent.properties +++ /dev/null @@ -1,30 +0,0 @@ -# properties file for spring Java Skirmish AIs -# This is used when you do not use the spring repository for compiling. -# -# Paths are relative to the project home (which is ../ from this file). -# All values are optional, and listed here with their defaults. -# -# For the project, you do not need the spring sources, -# but you will need the "Java-AIInterface/" and "JavaOO-AIWrapper/" dirs -# instead, which contain the Java AI Interfaces jlibs plus its binary -# and source jar, and the Java OO Wrappers equal counterparts. -# -# build-dir: build/ -# install-dir: dist/ -# -# This file is loaded from within build.xml, -# but only if "Java-AIInterface/" and "JavaOO-AIWrapper/" dirs are present. -# - -# This is used only in the next property -;build.home=build -# Where jar files will be built -;build.dir=${build.home} - -# This is used only in the next property. -# You will want to set dist.home to your spring install dir. -;dist.home=dist -# Where jar files will be installed to -;dist.dir=${dist.home}/AI/Skirmish/${skirmish.ai.name}/${skirmish.ai.version} -# Where HTML JavaDoc files will be generated -;doc.dir=${dist.home}/AI/Skirmish/${skirmish.ai.name}/${skirmish.ai.version}/doc/jdoc diff --git a/AI/Skirmish/NullOOJavaAI/bin/build.xml b/AI/Skirmish/NullOOJavaAI/bin/build.xml deleted file mode 100644 index 32b0b4783eb..00000000000 --- a/AI/Skirmish/NullOOJavaAI/bin/build.xml +++ /dev/null @@ -1,342 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . -File not found: ${ai.interface.build.home}/AIInterface.jar -Please make sure to compile the Java AI Interface -before trying to compile this AI, or request -an independent version of this project from -wherever you got this AI. - - - - . -File not found: ${ai.wrapper.oo.build.home}/JavaOO-AIWrapper.jar -Please make sure to compile the Java OO AI Wrapper -before trying to compile this AI, or request -an independent version of this project from -wherever you got this AI from. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Successfully bumped ${skirmish.ai.name} version from ${skirmish.ai.version} to ${skirmish.ai.version.new}. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - . -Please specify either home.new or name.new, for example: - ant create-independent -Dhome.new=~/projects/SpringSkirmishAIs/ -or - ant create-independent -Dname.new=MyJavaAI - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Spring source independant Java Skirmish AI project copied to: - ${project.dir.new} - -You may want to change your java source package structure -and the data/AIInto.lua file for the new project. - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AI/Skirmish/NullOOJavaAI/data/AIInfo.lua b/AI/Skirmish/NullOOJavaAI/data/AIInfo.lua deleted file mode 100644 index 91420c5e704..00000000000 --- a/AI/Skirmish/NullOOJavaAI/data/AIInfo.lua +++ /dev/null @@ -1,53 +0,0 @@ --- --- Info Definition Table format --- --- --- These keywords must be lowercase for LuaParser to read them. --- --- key: user defined or one of the SKIRMISH_AI_PROPERTY_* defines in --- SSkirmishAILibrary.h --- value: the value of the property --- desc: the description (could be used as a tooltip) --- --- --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -local infos = { - { - key = 'shortName', - value = 'NullOOJavaAI', - desc = 'machine conform name.', - }, - { - key = 'version', - value = '0.1', -- AI version - !This comment is used for parsing! - }, - { - key = 'className', - value = 'nulloojavaai.NullOOJavaAI', - desc = 'fully qualified name of a class that implements interface com.springrts.ai.AI', - }, - { - key = 'name', - value = 'high-level Java stub Skirmish AI', - desc = 'human readable name.', - }, - { - key = 'loadSupported', - value = 'no', - desc = 'whether this AI supports loading or not', - }, - { - key = 'interfaceShortName', - value = 'Java', -- AI Interface name - !This comment is used for parsing! - desc = 'the shortName of the AI interface this AI needs', - }, - { - key = 'interfaceVersion', - value = '0.1', -- AI Interface version - !This comment is used for parsing! - desc = 'the minimum version of the AI interface required by this AI', - }, -} - -return infos diff --git a/AI/Skirmish/NullOOJavaAI/data/AIOptions.lua b/AI/Skirmish/NullOOJavaAI/data/AIOptions.lua deleted file mode 100644 index d435d62e964..00000000000 --- a/AI/Skirmish/NullOOJavaAI/data/AIOptions.lua +++ /dev/null @@ -1,15 +0,0 @@ --- --- Custom Options Definition Table format --- --- A detailed example of how this format works can be found --- in the spring source under: --- AI/Skirmish/NullAI/data/AIOptions.lua --- --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -local options = { -} - -return options - diff --git a/AI/Skirmish/NullOOJavaAI/manifest.mf b/AI/Skirmish/NullOOJavaAI/manifest.mf deleted file mode 100755 index 59499bce4a2..00000000000 --- a/AI/Skirmish/NullOOJavaAI/manifest.mf +++ /dev/null @@ -1,2 +0,0 @@ -Manifest-Version: 1.0 - diff --git a/AI/Skirmish/NullOOJavaAI/src/main/java/nulloojavaai/NullOOJavaAI.java b/AI/Skirmish/NullOOJavaAI/src/main/java/nulloojavaai/NullOOJavaAI.java deleted file mode 100644 index 9ffabe1eae6..00000000000 --- a/AI/Skirmish/NullOOJavaAI/src/main/java/nulloojavaai/NullOOJavaAI.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - Copyright (c) 2008 Robin Vobruba - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package nulloojavaai; - - -import com.springrts.ai.AI; -import com.springrts.ai.oo.AIFloat3; -import com.springrts.ai.oo.OOAI; -import com.springrts.ai.oo.clb.*; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Properties; -import java.util.List; -import java.util.logging.*; - -/** - * Serves as Interface for a Java Skirmish AIs for the Spring engine. - * - * @author hoijui - */ -public class NullOOJavaAI extends OOAI implements AI { - - private static class MyCustomLogFormatter extends Formatter { - - private DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss:SSS dd.MM.yyyy"); - - public String format(LogRecord record) { - - // Create a StringBuffer to contain the formatted record - // start with the date. - StringBuffer sb = new StringBuffer(); - - // Get the date from the LogRecord and add it to the buffer - Date date = new Date(record.getMillis()); - sb.append(dateFormat.format(date)); - sb.append(" "); - - // Get the level name and add it to the buffer - sb.append(record.getLevel().getName()); - sb.append(": "); - - // Get the formatted message (includes localization - // and substitution of parameters) and add it to the buffer - sb.append(formatMessage(record)); - sb.append("\n"); - - return sb.toString(); - } - } - - private static void logProperties(Logger log, Level level, Properties props) { - - log.log(level, "properties (items: " + props.size() + "):"); - for (String key : props.stringPropertyNames()) { - log.log(level, key + " = " + props.getProperty(key)); - } - } - - private int skirmishAIId = -1; - private int teamId = -1; - private Properties info = null; - private Properties optionValues = null; - private OOAICallback clb = null; - private String myLogFile = null; - private Logger log = null; - - private static final int DEFAULT_ZONE = 0; - - - public NullOOJavaAI() {} - - private int sendTextMsg(String msg) { - - try { - clb.getGame().sendTextMessage("/say " + msg, DEFAULT_ZONE); - } catch (Exception ex) { - ex.printStackTrace(); - return 1; - } - - return 0; - } - public boolean isDebugging() { - return true; - } - - @Override - public int init(int skirmishAIId, OOAICallback callback) { - - int ret = -1; - - this.skirmishAIId = skirmishAIId; - this.clb = callback; - - this.teamId = clb.getSkirmishAI().getTeamId(); - - info = new Properties(); - Info inf = clb.getSkirmishAI().getInfo(); - int numInfo = inf.getSize(); - for (int i = 0; i < numInfo; i++) { - String key = inf.getKey(i); - String value = inf.getValue(i); - info.setProperty(key, value); - } - - optionValues = new Properties(); - OptionValues opVals = clb.getSkirmishAI().getOptionValues(); - int numOpVals = opVals.getSize(); - for (int i = 0; i < numOpVals; i++) { - String key = opVals.getKey(i); - String value = opVals.getValue(i); - optionValues.setProperty(key, value); - } - - // initialize the log - try { - myLogFile = callback.getDataDirs().locatePath("log-team-" + teamId + ".txt", true, true, false, false); - FileHandler fileLogger = new FileHandler(myLogFile, false); - fileLogger.setFormatter(new MyCustomLogFormatter()); - fileLogger.setLevel(Level.ALL); - log = Logger.getLogger("nulloojavaai"); - log.addHandler(fileLogger); - if (isDebugging()) { - log.setLevel(Level.ALL); - } else { - log.setLevel(Level.INFO); - } - } catch (Exception ex) { - System.out.println("NullOOJavaAI: Failed initializing the logger!"); - ex.printStackTrace(); - ret = -2; - } - - try { - log.info("initializing team " + teamId); - - log.log(Level.FINE, "info:"); - logProperties(log, Level.FINE, info); - - log.log(Level.FINE, "options:"); - logProperties(log, Level.FINE, optionValues); - - ret = 0; - } catch (Exception ex) { - log.log(Level.SEVERE, "Failed initializing", ex); - ret = -3; - } - - return ret; - } - - @Override - public int release(int reason) { - return 0; // signaling: OK - } - - @Override - public int update(int frame) { - - if (frame % 300 == 0) { - sendTextMsg("listing resources ..."); - List resources = clb.getResources(); - for (Resource resource : resources) { - sendTextMsg("Resource " + resource.getName() + " optimum: " - + resource.getOptimum()); - sendTextMsg("Resource " + resource.getName() + " current: " - + clb.getEconomy().getCurrent(resource)); - sendTextMsg("Resource " + resource.getName() + " income: " - + clb.getEconomy().getIncome(resource)); - sendTextMsg("Resource " + resource.getName() + " storage: " - + clb.getEconomy().getStorage(resource)); - sendTextMsg("Resource " + resource.getName() + " usage: " - + clb.getEconomy().getUsage(resource)); - } - } - - return 0; // signaling: OK - } - - @Override - public int message(int player, String message) { - return 0; // signaling: OK - } - - @Override - public int unitCreated(Unit unit, Unit builder) { - - int ret = sendTextMsg("unitCreated: " + unit.toString()); - sendTextMsg("unitCreated def: " + unit.getDef().getName()); - List buildOptions = unit.getDef().getBuildOptions(); - for (UnitDef unitDef : buildOptions) { - sendTextMsg("\tbuildOption x: " + unitDef.getName() + "\t" + unitDef.getHumanName() + "\t" + unitDef.toString() + "\t" + unitDef.hashCode()); - } - - return ret; - } - - @Override - public int unitFinished(Unit unit) { - return 0; // signaling: OK - } - - @Override - public int unitIdle(Unit unit) { - return 0; // signaling: OK - } - - @Override - public int unitMoveFailed(Unit unit) { - return 0; // signaling: OK - } - - @Override - public int unitDamaged(Unit unit, Unit attacker, float damage, AIFloat3 dir, WeaponDef weaponDef, boolean paralyzed) { - return 0; // signaling: OK - } - - @Override - public int unitDestroyed(Unit unit, Unit attacker) { - return 0; // signaling: OK - } - - @Override - public int unitGiven(Unit unit, int oldTeamId, int newTeamId) { - return 0; // signaling: OK - } - - @Override - public int unitCaptured(Unit unit, int oldTeamId, int newTeamId) { - return 0; // signaling: OK - } - - @Override - public int enemyEnterLOS(Unit enemy) { - return 0; // signaling: OK - } - - @Override - public int enemyLeaveLOS(Unit enemy) { - return 0; // signaling: OK - } - - @Override - public int enemyEnterRadar(Unit enemy) { - return 0; // signaling: OK - } - - @Override - public int enemyLeaveRadar(Unit enemy) { - return 0; // signaling: OK - } - - @Override - public int enemyDamaged(Unit enemy, Unit attacker, float damage, AIFloat3 dir, WeaponDef weaponDef, boolean paralyzed) { - return 0; // signaling: OK - } - - @Override - public int enemyDestroyed(Unit enemy, Unit attacker) { - return 0; // signaling: OK - } - - @Override - public int weaponFired(Unit unit, WeaponDef weaponDef) { - return 0; // signaling: OK - } - - @Override - public int playerCommand(java.util.List units, int commandTopicId, int playerId) { - return 0; // signaling: OK - } - - @Override - public int commandFinished(Unit unit, int commandId, int commandTopicId) { - return 0; // signaling: OK - } - - @Override - public int seismicPing(AIFloat3 pos, float strength) { - return 0; // signaling: OK - } - - @Override - public int load(String file) { - return 0; // signaling: OK - } - - @Override - public int save(String file) { - return 0; // signaling: OK - } - - @Override - public int enemyCreated(Unit enemy) { - return 0; // signaling: OK - } - - @Override - public int enemyFinished(Unit enemy) { - return 0; // signaling: OK - } - -} diff --git a/AI/Wrappers/CUtils/Util.c b/AI/Wrappers/CUtils/Util.c index c33d2e418e8..4764a619900 100644 --- a/AI/Wrappers/CUtils/Util.c +++ b/AI/Wrappers/CUtils/Util.c @@ -487,11 +487,7 @@ static void util_initFileSelector(const char* suffix) { fileSelectorSuffix = suffix; } -#if defined(__APPLE__) -static int util_fileSelector(struct dirent* fileDesc) { -#else static int util_fileSelector(const struct dirent* fileDesc) { -#endif return util_endsWith(fileDesc->d_name, fileSelectorSuffix); } diff --git a/AI/Wrappers/Cpp/src/AIFloat3.cpp b/AI/Wrappers/Cpp/src/AIFloat3.cpp index 83ffa7c2ed4..03c903c1841 100644 --- a/AI/Wrappers/Cpp/src/AIFloat3.cpp +++ b/AI/Wrappers/Cpp/src/AIFloat3.cpp @@ -16,10 +16,6 @@ springai::AIFloat3::AIFloat3(float* xyz) : float3(xyz) { } -springai::AIFloat3::AIFloat3(const springai::AIFloat3& other) - : float3(other) -{ -} springai::AIFloat3::AIFloat3(const float3& f3) : float3(f3) { diff --git a/AI/Wrappers/Cpp/src/AIFloat3.h b/AI/Wrappers/Cpp/src/AIFloat3.h index 6e00536a234..1c3ba587e3b 100644 --- a/AI/Wrappers/Cpp/src/AIFloat3.h +++ b/AI/Wrappers/Cpp/src/AIFloat3.h @@ -18,7 +18,8 @@ class AIFloat3 : public float3 { AIFloat3(); AIFloat3(float x, float y, float z); AIFloat3(float* xyz); - AIFloat3(const AIFloat3& other); + // must be trivial - same as float3, otherwise it ruins bindings + AIFloat3(const AIFloat3& other) = default; AIFloat3(const float3& f3); void LoadInto(float* xyz) const; diff --git a/AI/Wrappers/JavaOO/CMakeLists.txt b/AI/Wrappers/JavaOO/CMakeLists.txt deleted file mode 100644 index da45a64c6e6..00000000000 --- a/AI/Wrappers/JavaOO/CMakeLists.txt +++ /dev/null @@ -1,379 +0,0 @@ -### Java OO AI Wrapper -# -# Global variables set in this file: -# * BUILD_JavaOO_AIWRAPPER -# - -#enable_language(Java) - -# includes rts/build/cmake/UtilJava.cmake -include(UtilJava) - - -set(myName "JavaOO") - - -# Check if the user wants to compile the wrapper -if ("${AI_TYPES}" STREQUAL "ALL" OR "${AI_TYPES}" STREQUAL "JAVA") - set(AIWRAPPERS_JAVA TRUE) -else ("${AI_TYPES}" STREQUAL "ALL" OR "${AI_TYPES}" STREQUAL "JAVA") - set(AIWRAPPERS_JAVA FALSE) -endif ("${AI_TYPES}" STREQUAL "ALL" OR "${AI_TYPES}" STREQUAL "JAVA") - - -# Check dependencies of the wrapper are met -if (AIWRAPPERS_JAVA AND BUILD_Java_AIINTERFACE AND AWK_FOUND AND NOT myName MATCHES "${AI_EXCLUDE_REGEX}") - set_global(BUILD_${myName}_AIWRAPPER TRUE) -else (AIWRAPPERS_JAVA AND BUILD_Java_AIINTERFACE AND AWK_FOUND AND NOT myName MATCHES "${AI_EXCLUDE_REGEX}") - set_global(BUILD_${myName}_AIWRAPPER FALSE) - message("warning: Java OO AI Wrapper will not be built!") -endif (AIWRAPPERS_JAVA AND BUILD_Java_AIINTERFACE AND AWK_FOUND AND NOT myName MATCHES "${AI_EXCLUDE_REGEX}") - - -# Build -if (BUILD_${myName}_AIWRAPPER) - set(myDir "${CMAKE_CURRENT_SOURCE_DIR}") - get_last_path_part(dirName ${myDir}) - set(myName "${dirName}") - set(myTarget "${myName}-AIWrapper") - set(myGenTarget "${myTarget}-generateSources") - set(myJavaTarget "${myTarget}-java") - set(myPomTarget "${myTarget}-pom") - set(mySourceDirRel "src/main/java") - make_absolute(mySourceDir "${myDir}" "${mySourceDirRel}") - - ai_wrapper_message(STATUS "Found AI Wrapper: ${myName}") - - - # Build library - set(myPkgFirstPart "com") - set(myParentPkg "${myPkgFirstPart}/springrts/ai") - set(myPkg "${myParentPkg}/oo") - set(myBinDir "${myDir}/bin") - set(commonAwkScriptsDir "${CMAKE_SOURCE_DIR}/AI/Wrappers/CUtils/bin") - set(myBuildDir "${CMAKE_CURRENT_BINARY_DIR}") - set(myGeneratedSourceDir "${myBuildDir}/src-generated/main/java") - set(myJavaBuildDir "${myBuildDir}/classes") - set(myJarFile "${myName}-AIWrapper") - set(myBinJarFile "${myJarFile}.jar") - set(mySrcJarFile "${myJarFile}-src.jar") - set(myJLibDir "${myDir}/jlib") - find_java_lib(vecmath_jar "vecmath" "${myJLibDir}") - set(myJLibs "${vecmath_jar}") - set(jAiIntJavaSourceDir "${JAVA_SRC_DIR_Java_AIINTERFACE}") - set(jAiIntJavaGeneratedSourceDir "${JAVA_GEN_SRC_DIR_Java_AIINTERFACE}") - set(myClassPath "${myJLibs}${PATH_DELIM_H}${CLASSPATH_Java_AIINTERFACE}") - - set_global(${myName}_AIWRAPPER_JAR_BIN "${myBuildDir}/${myBinJarFile}") - set_global(${myName}_AIWRAPPER_JAR_SRC "${myBuildDir}/${mySrcJarFile}") - set_global(${myName}_AIWRAPPER_JAR_CLASSPATH "${myJLibs}${PATH_DELIM_H}${${myName}_AIWRAPPER_JAR_BIN}") - set_global(${myName}_AIWRAPPER_TARGET "${myTarget}") - set_global(SOURCE_ROOT_${myName}_AIWRAPPER "${myDir}") - set_global(BUILD_ROOT_${myName}_AIWRAPPER "${CMAKE_CURRENT_BINARY_DIR}") - - # Locate the manifest file - find_manifest_file("${myDir}" myManifestFile) - if (myManifestFile) - set(myBinJarArgs "cmf" "${myManifestFile}") - else (myManifestFile) - set(myBinJarArgs "cf") - endif (myManifestFile) - - # remove all generated sources from build dir, if it exists - # (required for build dirs of git:master from before 21. September 2010) - file(REMOVE_RECURSE "${myGeneratedSourceDir}/${myPkg}") - - - # Generate sources - # ---------------- - - set(commonAwkScriptArgs - "-v" "INTERFACE_SOURCE_DIR=${jAiIntJavaSourceDir}" - "-v" "INTERFACE_GENERATED_SOURCE_DIR=${jAiIntJavaGeneratedSourceDir}" - "-v" "JAVA_GENERATED_SOURCE_DIR=${myGeneratedSourceDir}" - "-f" "${commonAwkScriptsDir}/common.awk" - "-f" "${commonAwkScriptsDir}/commonDoc.awk" - ) - - set(mySources - "${mySourceDir}/${myPkg}/AIEvent.java" - "${mySourceDir}/${myPkg}/AIFloat3.java" - "${mySourceDir}/${myPkg}/AIException.java" - "${mySourceDir}/${myPkg}/CallbackAIException.java" - "${mySourceDir}/${myPkg}/EventAIException.java" - ) - - set(myGenClbClasses - "Cheats" - "CommandDescription" - "Command" - "Damage" - "DataDirs" - "Debug" - "Drawer" - "Economy" - "Engine" - "FeatureDef" - "Feature" - "Figure" - "FlankingBonus" - "Game" - "GraphDrawer" - "GraphLine" - "Group" - "Info" - "Line" - "Log" - "Lua" - "Map" - "Mod" - "MoveData" - "OOAICallback" - "OptionValues" - "OrderPreview" - "Pathing" - "PathDrawer" - "Point" - "Resource" - "Roots" - "Shield" - "SkirmishAI" - "Team" - "OverlayTexture" - "UnitDef" - "Unit" - "Version" - "WeaponDef" - "WeaponMount" - "Weapon" - ) - set(myGeneratedCallbackSources ) - foreach (className ${myGenClbClasses}) - list(APPEND myGeneratedCallbackSources - "${myGeneratedSourceDir}/${myPkg}/clb/${className}.java" - "${myGeneratedSourceDir}/${myPkg}/clb/Abstract${className}.java" - "${myGeneratedSourceDir}/${myPkg}/clb/Stub${className}.java" - ) - if ("${className}" STREQUAL "CommandDescription") - list(APPEND myGeneratedCallbackSources - "${myGeneratedSourceDir}/${myPkg}/clb/WrappGroupSupportedCommand.java" - "${myGeneratedSourceDir}/${myPkg}/clb/WrappUnitSupportedCommand.java" - ) - elseif ("${className}" STREQUAL "Command") - list(APPEND myGeneratedCallbackSources - "${myGeneratedSourceDir}/${myPkg}/clb/WrappCurrentCommand.java" - ) - else () - list(APPEND myGeneratedCallbackSources - "${myGeneratedSourceDir}/${myPkg}/clb/Wrapp${className}.java" - ) - endif () - endforeach (className) - - set(myGeneratedEventSources - "${myGeneratedSourceDir}/${myPkg}/AbstractOOAI.java" - "${myGeneratedSourceDir}/${myPkg}/IOOAI.java" - "${myGeneratedSourceDir}/${myPkg}/OOAI.java" - "${myGeneratedSourceDir}/${myPkg}/IOOEventAI.java" - "${myGeneratedSourceDir}/${myPkg}/OOEventAI.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitLifeStateAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitTeamChangeAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/EnemyAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/LoadSaveAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/CommandFinishedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/EnemyCreatedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/EnemyDamagedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/EnemyDestroyedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/EnemyFinishedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/EnemyEnterLOSAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/EnemyEnterRadarAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/EnemyLeaveLOSAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/EnemyLeaveRadarAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/InitAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/LoadAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/MessageAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/LuaMessageAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/PlayerCommandAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/ReleaseAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/SaveAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/SeismicPingAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitCapturedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitCreatedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitDamagedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitDestroyedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitFinishedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitGivenAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitIdleAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UnitMoveFailedAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/UpdateAIEvent.java" - "${myGeneratedSourceDir}/${myPkg}/evt/WeaponFiredAIEvent.java" - ) - - set(myGeneratedSources - ${myGeneratedCallbackSources} - ${myGeneratedEventSources} - ) - set_source_files_properties(${myGeneratedSources} PROPERTIES GENERATED TRUE) - - set(jAiIntJavaGeneratedSources - "${jAiIntJavaGeneratedSourceDir}/${myParentPkg}/AI.java" - "${jAiIntJavaGeneratedSourceDir}/${myParentPkg}/AICallback.java" - ) - set_source_files_properties(${jAiIntJavaGeneratedSources} PROPERTIES GENERATED TRUE) - - - # Assemble project generated targets (and their libraries) we depend on - set(myDependTargets "${Java_AIINTERFACE_TARGET}") - set(myDependLibFiles "${Java_AIINTERFACE_JAR_BIN}") - set_source_files_properties(${myDependLibFiles} PROPERTIES GENERATED TRUE) - - add_custom_target(${myJavaTarget} - DEPENDS - "${myBuildDir}/${myBinJarFile}" - "${myBuildDir}/${mySrcJarFile}") - add_dependencies(${myJavaTarget} ${myDependTargets}) - - - # generate sources: callback - add_custom_command( - OUTPUT - ${myGeneratedCallbackSources} - COMMAND "${CMAKE_COMMAND}" - "-E" "remove_directory" "${myGeneratedSourceDir}/${myPkg}/clb" - COMMAND "${CMAKE_COMMAND}" - "-E" "make_directory" "${myGeneratedSourceDir}/${myPkg}/clb" - COMMAND "${AWK_BIN}" - ${commonAwkScriptArgs} - "-f" "${commonAwkScriptsDir}/commonOOCallback.awk" - "-f" "${myBinDir}/wrappCallback.awk" - "${jAiIntJavaGeneratedSourceDir}/${myParentPkg}/AICallback.java" - DEPENDS - "${commonAwkScriptsDir}/common.awk" - "${commonAwkScriptsDir}/commonDoc.awk" - "${commonAwkScriptsDir}/commonOOCallback.awk" - "${myBinDir}/wrappCallback.awk" - # Handled through a target level dependency, see below - #"${jAiIntJavaGeneratedSourceDir}/${myParentPkg}/AICallback.java" - WORKING_DIRECTORY - "${myBinDir}" - COMMENT - " ${myTarget}: Generating callback sources" VERBATIM - ) - - # generate sources: events - add_custom_command( - OUTPUT - ${myGeneratedEventSources} - COMMAND "${CMAKE_COMMAND}" - "-E" "remove_directory" "${myGeneratedSourceDir}/${myPkg}/evt" - COMMAND "${CMAKE_COMMAND}" - "-E" "make_directory" "${myGeneratedSourceDir}/${myPkg}/evt" - COMMAND "${AWK_BIN}" - ${commonAwkScriptArgs} - "-f" "${myBinDir}/wrappEvents.awk" - "${jAiIntJavaGeneratedSourceDir}/${myParentPkg}/AI.java" - DEPENDS - "${commonAwkScriptsDir}/common.awk" - "${commonAwkScriptsDir}/commonDoc.awk" - "${myBinDir}/wrappEvents.awk" - "${jAiIntJavaGeneratedSourceDir}/${myParentPkg}/AI.java" - WORKING_DIRECTORY - "${myBinDir}" - COMMENT - " ${myTarget}: Generating event sources" VERBATIM - ) - - add_custom_target(${myGenTarget} DEPENDS ${myGeneratedSources}) - add_dependencies(${myGenTarget} ${Java_AIINTERFACE_TARGET_GENERATE_SOURCES}) # for AICallback.java - add_dependencies(generateSources ${myGenTarget}) - - - # Build the jars - # -------------- - - # Write list of source files to an arg-file - set(mySrcArgFile "${CMAKE_CURRENT_BINARY_DIR}/sourceFiles.txt") - if (EXISTS "${mySrcArgFile}") - file(REMOVE "${mySrcArgFile}") - endif (EXISTS "${mySrcArgFile}") - foreach (srcFile ${mySources} ${myGeneratedSources}) - file(APPEND "${mySrcArgFile}" "\"${srcFile}\"\n") - endforeach (srcFile) - - # compile and pack library - add_custom_command( - OUTPUT - "${myBuildDir}/${myBinJarFile}" - COMMAND "${CMAKE_COMMAND}" - "-E" "remove_directory" "${myJavaBuildDir}" - COMMAND "${CMAKE_COMMAND}" - "-E" "make_directory" "${myJavaBuildDir}" - COMMAND "${Java_JAVAC_EXECUTABLE}" - "${JAVA_COMPILE_FLAG_CONDITIONAL}" - "-Xlint:deprecation" - "-cp" "${myClassPath}" - "-d" "${myJavaBuildDir}" - "@${mySrcArgFile}" - COMMAND "${Java_JAR_EXECUTABLE}" ARGS - ${myBinJarArgs} "${myBuildDir}/${myBinJarFile}" - "-C" "${myJavaBuildDir}" "${myPkgFirstPart}" - DEPENDS - ${myDependLibFiles} - ${mySources} - ${myGeneratedSources} - WORKING_DIRECTORY - "${myBinDir}" - COMMENT - " ${myTarget}: Compiling sources and packing library ${myBinJarFile}" VERBATIM - ) - - # pack sources - add_custom_command( - OUTPUT - "${myBuildDir}/${mySrcJarFile}" - COMMAND "${Java_JAR_EXECUTABLE}" ARGS - "cf" "${${myName}_AIWRAPPER_JAR_SRC}" - "-C" "${mySourceDir}" "${myPkgFirstPart}" - COMMAND "${Java_JAR_EXECUTABLE}" ARGS - "uf" "${${myName}_AIWRAPPER_JAR_SRC}" - "-C" "${myGeneratedSourceDir}" "${myPkgFirstPart}" - DEPENDS - ${myJavaSources} - ${myJavaGeneratedSources} - WORKING_DIRECTORY - "${myBuildDir}" - COMMENT - " ${myTarget}: Creating sources archive ${mySrcJarFile}" VERBATIM - ) - - # This sets the version in pom.xml - # as we have no separate version for the wrapper, - # we use the one from the interface - set(myMavenProperties "-Dmy.version=${Java_AIINTERFACE_VERS}") - add_custom_command( - OUTPUT - "${myBuildDir}/pom-generated.xml" - COMMAND "${CMAKE_COMMAND}" - "-Dfile.in=${myDir}/pom.xml" - "-Dfile.out=${myBuildDir}/pom-generated.xml" - ${myMavenProperties} - "-P" "${CMAKE_MODULES_SPRING}/ConfigureFile.cmake" - DEPENDS - "${myDir}/pom.xml" - WORKING_DIRECTORY - "${myDir}" - COMMENT - " ${myTarget}: Configure pom.xml" VERBATIM - ) - set_source_files_properties("${myBuildDir}/pom-generated.xml" PROPERTIES GENERATED TRUE) - - add_custom_target(${myPomTarget} - DEPENDS - "${myBuildDir}/pom-generated.xml") - - add_custom_target(${myTarget} ALL) - - add_dependencies(${myTarget} ${myJavaTarget}) - -endif (BUILD_${myName}_AIWRAPPER) diff --git a/AI/Wrappers/JavaOO/bin/ant.properties b/AI/Wrappers/JavaOO/bin/ant.properties deleted file mode 100644 index 0807d79b8a8..00000000000 --- a/AI/Wrappers/JavaOO/bin/ant.properties +++ /dev/null @@ -1,26 +0,0 @@ -# Paths are relative to the project home (which is ../ from this file). -# All values are optional. - -;spring.home=../../.. - -# specify Java-AIInterface dirs -;spring.ai.interface.src.home=${spring.home}/AI/Interfaces/Java -;spring.ai.interface.src.dir=${spring.ai.interface.src.home}/src/main/java -;spring.ai.interface.build.home=${build.home}/AI/Interfaces/Java -;spring.ai.interface.build.dir=${spring.ai.interface.build.home}/src-generated/main/java - -# This is used only in the next property -;build.home=${spring.home}/build -# Where jar files will be built -;build.dir=${build.home}/AI/Wrappers/${wrapper.name} - -# Where generated sources shall be created in -;src.generated=${build.dir}/src-generated/main -;src.generated.java=${src.generated}/java - -# This is used only in the next property -;dist.home=${spring.home}/dist -# Where jar files will be installed to -;dist.dir=${dist.home}/AI/Wrappers/${wrapper.name} -# Where HTML JavaDoc files will be generated -;doc.dir=${dist.home}/AI/Wrappers/${wrapper.name}/doc/jdoc diff --git a/AI/Wrappers/JavaOO/bin/build.xml b/AI/Wrappers/JavaOO/bin/build.xml deleted file mode 100644 index 71e836bbb7a..00000000000 --- a/AI/Wrappers/JavaOO/bin/build.xml +++ /dev/null @@ -1,211 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/AI/Wrappers/JavaOO/bin/wrappCallback.awk b/AI/Wrappers/JavaOO/bin/wrappCallback.awk deleted file mode 100755 index 7824eb30137..00000000000 --- a/AI/Wrappers/JavaOO/bin/wrappCallback.awk +++ /dev/null @@ -1,1241 +0,0 @@ -#!/usr/bin/awk -f -# -# This awk script creates Java classes in OO style to wrap the C style -# JNI based AI Callback wrapper. -# In other words, the output of this file wraps: -# com/springrts/ai/AICallback.java -# which wraps: -# rts/ExternalAI/Interface/SSkirmishAICallback.h -# and -# rts/ExternalAI/Interface/AISCommands.h -# -# This script uses functions from the following files: -# * common.awk -# * commonDoc.awk -# * commonOOCallback.awk -# Variables that can be set on the command-line (with -v): -# * GENERATED_SOURCE_DIR : the generated sources root dir -# * JAVA_GENERATED_SOURCE_DIR : the generated java sources root dir -# * INTERFACE_SOURCE_DIR : the Java AI Interfaces static source files root dir -# * INTERFACE_GENERATED_SOURCE_DIR : the Java AI Interfaces generated source files root dir -# -# usage: -# awk -f thisScript.awk -f common.awk -f commonDoc.awk -f commonOOCallback.awk -# awk -f thisScript.awk -f common.awk -f commonDoc.awk -f commonOOCallback.awk \ -# -v 'GENERATED_SOURCE_DIR=/tmp/build/AI/Interfaces/Java/src-generated' -# - -BEGIN { - # initialize things - - # define the field splitter(-regex) - FS = "(\\()|(\\);)"; - IGNORECASE = 0; - - # Used by other scripts - JAVA_MODE = 1; - - # These vars can be assigned externally, see file header. - # Set the default values if they were not supplied on the command line. - if (!GENERATED_SOURCE_DIR) { - GENERATED_SOURCE_DIR = "../src-generated/main"; - } - if (!JAVA_GENERATED_SOURCE_DIR) { - JAVA_GENERATED_SOURCE_DIR = GENERATED_SOURCE_DIR "/java"; - } - if (!INTERFACE_SOURCE_DIR) { - INTERFACE_SOURCE_DIR = "../../../Interfaces/Java/src/main/java"; - } - if (!INTERFACE_GENERATED_SOURCE_DIR) { - INTERFACE_GENERATED_SOURCE_DIR = "../../../Interfaces/Java/src-generated/main/java"; - } - - myMainPkgA = "com.springrts.ai"; - myParentPkgA = myMainPkgA ".oo"; - myPkgA = myParentPkgA ".clb"; - myPkgD = convertJavaNameFormAToD(myPkgA); - myClassVar = "ooClb"; - myWrapClass = "AICallback"; - myWrapVar = "innerCallback"; - - myBufferedClasses["UnitDef"] = 1; - myBufferedClasses["WeaponDef"] = 1; - myBufferedClasses["FeatureDef"] = 1; - - retParamName = "__retVal"; -} - -function createJavaFileName(clsName_c) { - return JAVA_GENERATED_SOURCE_DIR "/" myPkgD "/" clsName_c ".java"; -} - - -function printHeader(outFile_h, javaPkg_h, javaClassName_h, isInterface_h, - implementsInterface_h, isJniBound_h, isAbstract_h, implementsClass_h) { - - if (isInterface_h) { - classOrInterface_h = "interface"; - } else if (isAbstract_h) { - classOrInterface_h = "abstract class"; - } else { - classOrInterface_h = "class"; - } - - extensionsPart_h = ""; - if (isInterface_h) { - extensionsPart_h = " extends Comparable<" javaClassName_h ">"; - } else if (isAbstract_h) { - extensionsPart_h = " implements " implementsInterface_h ""; - } else { - extensionsPart_h = " extends " implementsClass_h " implements " implementsInterface_h; - } - - printCommentsHeader(outFile_h); - print("") >> outFile_h; - print("package " javaPkg_h ";") >> outFile_h; - print("") >> outFile_h; - print("") >> outFile_h; - print("import " myParentPkgA ".CallbackAIException;") >> outFile_h; - print("import " myParentPkgA ".AIFloat3;") >> outFile_h; - if (isJniBound_h) { - print("import " myMainPkgA ".AICallback;") >> outFile_h; - print("import " myMainPkgA ".Util;") >> outFile_h; - } - print("") >> outFile_h; - print("/**") >> outFile_h; - print(" * @author AWK wrapper script") >> outFile_h; - print(" * @version GENERATED") >> outFile_h; - print(" */") >> outFile_h; - print("public " classOrInterface_h " " javaClassName_h extensionsPart_h " {") >> outFile_h; - print("") >> outFile_h; -} - - -function getNullTypeValue(fRet_ntv) { - - if (fRet_ntv == "void") { - return ""; - } else if (fRet_ntv == "String") { - return "\"\""; - } else if (fRet_ntv == "AIFloat3") { - #return "new AIFloat3(0.0f, 0.0f, 0.0f)"; - return "null"; - } else if (fRet_ntv == "java.awt.Color") { - #return "java.awt.Color.BLACK"; - return "null"; - } else if (startsWithCapital(fRet_ntv)) { - # must be a class - return "null"; - } else if (match(fRet_ntv, /^java.util.List/)) { - return "null"; - } else if (match(fRet_ntv, /^java.util.Map/)) { - return "null"; - } else if (fRet_ntv == "boolean") { - return "false"; - } else { - return "0"; - } -} -function printTripleFunc(fRet_tr, fName_tr, fParams_tr, thrownExceptions_tr, outFile_int_tr, outFile_stb_tr, outFile_jni_tr, printIntAndStb_tr, noOverride_tr, isDeprecated_tr) { - - _funcHdr_tr = "public " fRet_tr " " fName_tr "(" fParams_tr ")"; - if (thrownExceptions_tr != "") { - _funcHdr_tr = _funcHdr_tr " throws " thrownExceptions_tr; - } - - if (printIntAndStb_tr) { - print("\t" _funcHdr_tr ";") >> outFile_int_tr; - print("") >> outFile_int_tr; - - isSimpleGetter_tr = ((fRet_tr != "void") && (fParams_tr == "") && match(fName_tr, /^(get|is)/)); - if ((fName_tr == "isEnabled") && match(outFile_int_tr, /Cheats\.java$/)) { - # an exception, because this has a setter anyway - isSimpleGetter_tr = 0; - } - nullTypeValue_tr = getNullTypeValue(fRet_tr); - if (isSimpleGetter_tr) { - # create an additional private member and setter - propName_tr = fName_tr; - sub(/^get/, "", propName_tr); - propName_tr = lowerize(propName_tr); - fSetterName_tr = fName_tr; - sub(/^(get|is)/, "set", fSetterName_tr); - print("\t" "public void " fSetterName_tr "(" fRet_tr " " propName_tr ")" " {") >> outFile_stb_tr; - print("\t\t" "this." propName_tr " = " propName_tr ";") >> outFile_stb_tr; - print("\t" "}") >> outFile_stb_tr; - print("\t" "private " fRet_tr " " propName_tr " = " nullTypeValue_tr ";") >> outFile_stb_tr; - if (isDeprecated_tr) { - # this prevents javac from outputting a warning - print("\t" "/** @deprecated */") >> outFile_stb_tr; - } - print("\t" "@Override") >> outFile_stb_tr; - print("\t" _funcHdr_tr " {") >> outFile_stb_tr; - print("\t\t" "return " propName_tr ";") >> outFile_stb_tr; - } else { - print("\t" "@Override") >> outFile_stb_tr; - print("\t" _funcHdr_tr " {") >> outFile_stb_tr; - # simply return a null like value - if (fRet_tr == "void") { - # return nothing - } else { - print("\t\t" "return " nullTypeValue_tr ";") >> outFile_stb_tr; - } - } - print("\t" "}") >> outFile_stb_tr; - print("") >> outFile_stb_tr; - } - - if (!noOverride_tr) { - print("\t" "@Override") >> outFile_jni_tr; - } - print("\t" _funcHdr_tr " {") >> outFile_jni_tr; - print("") >> outFile_jni_tr; -} - - -function printClasses() { - - # look for AVAILABLE indicators - for (_memberId in cls_memberId_metaComment) { - if (match(cls_memberId_metaComment[_memberId], /AVAILABLE:/)) { - _availCls = cls_memberId_metaComment[_memberId]; - sub(/^.*AVAILABLE:/, "", _availCls); - sub(/[ \t].*$/, "", _availCls); - clsAvailInd_memberId_cls[_memberId] = _availCls; - } - } - - c_size_cs = cls_id_name["*"]; - for (c=0; c < c_size_cs; c++) { - cls_cs = cls_id_name[c]; - anc_size_cs = cls_name_implIds[cls_cs ",*"]; - - printIntAndStb_cs = 1; - for (a=0; a < anc_size_cs; a++) { - implId_cs = cls_name_implIds[cls_cs "," a]; - printClass(implId_cs, cls_cs, printIntAndStb_cs); - # only print interface and stub when printing the first impl-class - printIntAndStb_cs = 0; - } - } -} - - -function printClass(implId_c, clsName_c, printIntAndStb_c) { - - implCls_c = implId_c; - sub(/^.*,/, "", implCls_c); - - clsName_int_c = clsName_c; - clsName_abs_c = "Abstract" clsName_int_c; - clsName_stb_c = "Stub" clsName_int_c; - _fullClsName = cls_implId_fullClsName[implId_c]; - if (_fullClsName != clsName_c) { - lastAncName_c = implId_c; - sub(/,[^,]*$/, "", lastAncName_c); # remove class name - sub(/^.*,/, "", lastAncName_c); # remove pre last ancestor name - noInterfaceIndices_c = lowerize(lastAncName_c) "Id"; - } else { - noInterfaceIndices_c = 0; - } - clsName_jni_c = "Wrapp" _fullClsName; - - if (printIntAndStb_c) { - outFile_int_c = createJavaFileName(clsName_int_c); - outFile_abs_c = createJavaFileName(clsName_abs_c); - outFile_stb_c = createJavaFileName(clsName_stb_c); - } - outFile_jni_c = createJavaFileName(clsName_jni_c); - - if (printIntAndStb_c) { - printHeader(outFile_int_c, myPkgA, clsName_int_c, 1, 0, 0, 0, 0); - printHeader(outFile_abs_c, myPkgA, clsName_abs_c, 0, clsName_int_c, 0, 1, 0); - printHeader(outFile_stb_c, myPkgA, clsName_stb_c, 0, clsName_int_c, 0, 0, clsName_abs_c); - } - printHeader( outFile_jni_c, myPkgA, clsName_jni_c, 0, clsName_int_c, 1, 0, clsName_abs_c); - - # prepare additional indices names - addInds_size_c = split(cls_implId_indicesArgs[implId_c], addInds_c, ","); - for (ai=1; ai <= addInds_size_c; ai++) { - sub(/int /, "", addInds_c[ai]); - addInds_c[ai] = trim(addInds_c[ai]); - } - - myInnerClb = myClassVar ".getInnerCallback()"; - - - # print private vars - print("\t" "private " myWrapClass " " myWrapVar " = null;") >> outFile_jni_c; - # print additionalVars - for (ai=1; ai <= addInds_size_c; ai++) { - print("\t" "private int " addInds_c[ai] " = -1;") >> outFile_jni_c; - } - print("") >> outFile_jni_c; - - - # print constructor - ctorParams = myWrapClass " " myWrapVar; - addIndPars_c = ""; - for (ai=1; ai <= addInds_size_c; ai++) { - addIndPars_c = addIndPars_c ", int " addInds_c[ai]; - } - ctorParams = ctorParams addIndPars_c; - ctorParamsNoTypes = removeParamTypes(ctorParams); - sub(/^, /, "", addIndPars_c); - addIndParsNoTypes_c = removeParamTypes(addIndPars_c); - condAddIndPars_c = (addIndPars_c == "") ? "" : ", "; - print("\t" "public " clsName_jni_c "(" ctorParams ") {") >> outFile_jni_c; - print("") >> outFile_jni_c; - print("\t\t" "this." myWrapVar " = " myWrapVar ";") >> outFile_jni_c; - # init additionalVars - for (ai=1; ai <= addInds_size_c; ai++) { - addIndName = addInds_c[ai]; - print("\t\t" "this." addIndName " = " addIndName ";") >> outFile_jni_c; - } - print("\t" "}") >> outFile_jni_c; - print("") >> outFile_jni_c; - - - # print additional vars fetchers - for (ai=1; ai <= addInds_size_c; ai++) { - addIndName = addInds_c[ai]; - addIndNameCap = capitalize(addIndName); - - printIntAndStb_tmp_c = printIntAndStb_c; - _noOverride = 0; - if ((noInterfaceIndices_c != 0) && (addIndName == noInterfaceIndices_c)) { - printIntAndStb_tmp_c = 0; - _noOverride = 1; - } - - # Print the direct fetcher function, eg. "int getUnitId()" - _fRet = "int"; - _fName = "get" addIndNameCap; - _fParams = ""; - _fExceps = ""; - _fIsDeprecated = 0; - printTripleFunc(_fRet, _fName, _fParams, _fExceps, outFile_int_c, outFile_stb_c, outFile_jni_c, printIntAndStb_tmp_c, _noOverride, _fIsDeprecated); - print("\t\t" "return " addIndName ";") >> outFile_jni_c; - print("\t" "}") >> outFile_jni_c; - print("") >> outFile_jni_c; - - # Print the OO entity fetcher function if applicable, eg. "Unit getUnit()" - addIndNameCapOO = addIndNameCap; - _hadId = sub(/Id$/, "", addIndNameCapOO); - if (_hadId && (addIndNameCapOO in cls_name_id) && (addIndNameCapOO != clsName_int_c)) { - _refObj = addIndNameCapOO; # example: Unit - _implId = implId_m "," _refObj; - if (_implId in cls_implId_fullClsName) { - _fullClsName = cls_implId_fullClsName[_implId]; - } else if (cls_name_implIds[_refObj ",*"] == 1) { - _fullClsName = cls_name_implIds[_refObj ",0"]; - _fullClsName = cls_implId_fullClsName[_fullClsName]; - } else { - print("ERROR: failed finding the full class name for: " _refObj); - exit(1); - } - - _wrappGetInst_params = myWrapVar; - for (aij=1; aij <= ai; aij++) { - _wrappGetInst_params = _wrappGetInst_params ", " addInds_c[aij]; - } - - _fRet = addIndNameCapOO; - _fName = "get" addIndNameCapOO; - _fParams = ""; - _fExceps = ""; - _fIsDeprecated = 0; - printTripleFunc(_fRet, _fName, _fParams, _fExceps, outFile_int_c, outFile_stb_c, outFile_jni_c, printIntAndStb_tmp_c, _noOverride, _fIsDeprecated); - print("\t\t" "return Wrapp" _fullClsName ".getInstance(" _wrappGetInst_params ");") >> outFile_jni_c; - print("\t" "}") >> outFile_jni_c; - print("") >> outFile_jni_c; - } - } - - # print static instance fetcher method - { - clsIsBuffered_c = isBufferedClass(clsName_c); - _isAvailableMethod = ""; - for (_memId in clsAvailInd_memberId_cls) { - if (clsAvailInd_memberId_cls[_memId] == clsName_c) { - _isAvailableMethod = _memId; - gsub(/,/, "_", _isAvailableMethod); - } - } - - if (clsIsBuffered_c) { - print("\t" "private static java.util.Map _buffer_instances = new java.util.HashMap();") >> outFile_jni_c; - print("") >> outFile_jni_c; - } - print("\t" "public static " clsName_c " getInstance(" ctorParams ") {") >> outFile_jni_c; - print("") >> outFile_jni_c; - lastParamName = ctorParamsNoTypes; - sub(/^.*,[ \t]*/, "", lastParamName); - if (match(lastParamName, /^[^ \t]+Id$/)) { - # id's < 0 are invalid, return null - print("\t\t" "if (" lastParamName " < 0) {") >> outFile_jni_c; - print("\t\t\t" "return null;") >> outFile_jni_c; - print("\t\t" "}") >> outFile_jni_c; - print("") >> outFile_jni_c; - } - print("\t\t" clsName_c " _ret = null;") >> outFile_jni_c; - if (_isAvailableMethod == "") { - print("\t\t" "_ret = new " clsName_jni_c "(" ctorParamsNoTypes ");") >> outFile_jni_c; - } else { - print("\t\t" "boolean isAvailable = " myWrapVar "." _isAvailableMethod "(" addIndParsNoTypes_c ");") >> outFile_jni_c; - print("\t\t" "if (isAvailable) {") >> outFile_jni_c; - print("\t\t\t" "_ret = new " clsName_jni_c "(" ctorParamsNoTypes ");") >> outFile_jni_c; - print("\t\t" "}") >> outFile_jni_c; - } - if (clsIsBuffered_c) { - if (_isAvailableMethod == "") { - print("\t\t" "{") >> outFile_jni_c; - } else { - print("\t\t" "if (_ret != null) {") >> outFile_jni_c; - } - print("\t\t\t" "Integer indexHash = _ret.hashCode();") >> outFile_jni_c; - print("\t\t\t" "if (_buffer_instances.containsKey(indexHash)) {") >> outFile_jni_c; - print("\t\t\t\t" "_ret = _buffer_instances.get(indexHash);") >> outFile_jni_c; - print("\t\t\t" "} else {") >> outFile_jni_c; - print("\t\t\t\t" "_buffer_instances.put(indexHash, _ret);") >> outFile_jni_c; - print("\t\t\t" "}") >> outFile_jni_c; - print("\t\t" "}") >> outFile_jni_c; - } - print("\t\t" "return _ret;") >> outFile_jni_c; - print("\t" "}") >> outFile_jni_c; - print("") >> outFile_jni_c; - } - - - if (printIntAndStb_c) { - # print compareTo(other) method - { - print("\t" "@Override") >> outFile_abs_c; - print("\t" "public int compareTo(" clsName_c " other) {") >> outFile_abs_c; - print("\t\t" "final int BEFORE = -1;") >> outFile_abs_c; - print("\t\t" "final int EQUAL = 0;") >> outFile_abs_c; - print("\t\t" "final int AFTER = 1;") >> outFile_abs_c; - print("") >> outFile_abs_c; - print("\t\t" "if (this == other) return EQUAL;") >> outFile_abs_c; - print("") >> outFile_abs_c; - - if (isClbRootCls) { - print("\t\t" "if (this.skirmishAIId < other.skirmishAIId) return BEFORE;") >> outFile_abs_c; - print("\t\t" "if (this.skirmishAIId > other.skirmishAIId) return AFTER;") >> outFile_abs_c; - print("\t\t" "return EQUAL;") >> outFile_abs_c; - } else { - for (ai=1; ai <= addInds_size_c; ai++) { - addIndName = addInds_c[ai]; - if ((noInterfaceIndices_c == 0) || (addIndName != noInterfaceIndices_c)) { - print("\t\t" "if (this.get" capitalize(addIndName) "() < other.get" capitalize(addIndName) "()) return BEFORE;") >> outFile_abs_c; - print("\t\t" "if (this.get" capitalize(addIndName) "() > other.get" capitalize(addIndName) "()) return AFTER;") >> outFile_abs_c; - } - } - print("\t\t" "return 0;") >> outFile_abs_c; - } - print("\t" "}") >> outFile_abs_c; - print("") >> outFile_abs_c; - } - - - # print equals(other) method - if (!isClbRootCls) { - print("\t" "@Override") >> outFile_abs_c; - print("\t" "public boolean equals(Object otherObject) {") >> outFile_abs_c; - print("") >> outFile_abs_c; - print("\t\t" "if (this == otherObject) return true;") >> outFile_abs_c; - print("\t\t" "if (!(otherObject instanceof " clsName_c ")) return false;") >> outFile_abs_c; - print("\t\t" clsName_c " other = (" clsName_c ") otherObject;") >> outFile_abs_c; - print("") >> outFile_abs_c; - - #if (isClbRootCls) { - # print("\t\t" "if (this.skirmishAIId != other.skirmishAIId) return false;") >> outFile_abs_c; - # print("\t\t" "return true;") >> outFile_abs_c; - #} - #else - { - for (ai=1; ai <= addInds_size_c; ai++) { - addIndName = addInds_c[ai]; - if ((noInterfaceIndices_c == 0) || (addIndName != noInterfaceIndices_c)) { - print("\t\t" "if (this.get" capitalize(addIndName) "() != other.get" capitalize(addIndName) "()) return false;") >> outFile_abs_c; - } - } - print("\t\t" "return true;") >> outFile_abs_c; - } - print("\t" "}") >> outFile_abs_c; - print("") >> outFile_abs_c; - } - - - # print hashCode() method - if (!isClbRootCls) { - print("\t" "@Override") >> outFile_abs_c; - print("\t" "public int hashCode() {") >> outFile_abs_c; - print("") >> outFile_abs_c; - - if (isClbRootCls) { - print("\t\t" "int _res = 0;") >> outFile_abs_c; - print("") >> outFile_abs_c; - print("\t\t" "_res += this.skirmishAIId * 10E8;") >> outFile_abs_c; - } else { - print("\t\t" "int _res = 23;") >> outFile_abs_c; - print("") >> outFile_abs_c; - # NOTE: This could go wrong if we have more then 7 additional indices - # see 10E" (7-ai) below - # the conversion to int is nessesarry, - # as otherwise it would be a double, - # which would be higher then max int, - # and most hashes would end up being max int, - # when converted from double to int - for (ai=1; ai <= addInds_size_c; ai++) { - addIndName = addInds_c[ai]; - if ((noInterfaceIndices_c == 0) || (addIndName != noInterfaceIndices_c)) { - print("\t\t" "_res += this.get" capitalize(addIndName) "() * (int) (10E" (7-ai) ");") >> outFile_abs_c; - } - } - } - print("") >> outFile_abs_c; - print("\t\t" "return _res;") >> outFile_abs_c; - print("\t" "}") >> outFile_abs_c; - print("") >> outFile_abs_c; - } - - - # print toString() method - { - print("\t" "@Override") >> outFile_abs_c; - print("\t" "public String toString() {") >> outFile_abs_c; - print("") >> outFile_abs_c; - print("\t\t" "String _res = this.getClass().toString();") >> outFile_abs_c; - print("") >> outFile_abs_c; - - #if (isClbRootCls) { # NO FOLD - # print("\t\t" "_res = _res + \"(skirmishAIId=\" + this.skirmishAIId + \", \";") >> outFile_abs_c; - #} else { # NO FOLD - # print("\t\t" "_res = _res + \"(clbHash=\" + this." myWrapVar ".hashCode() + \", \";") >> outFile_abs_c; - # print("\t\t" "_res = _res + \"skirmishAIId=\" + this." myWrapVar ".SkirmishAI_getSkirmishAIId() + \", \";") >> outFile_abs_c; - for (ai=1; ai <= addInds_size_c; ai++) { - addIndName = addInds_c[ai]; - if ((noInterfaceIndices_c == 0) || (addIndName != noInterfaceIndices_c)) { - print("\t\t" "_res = _res + \"" addIndName "=\" + this.get" capitalize(addIndName) "() + \", \";") >> outFile_abs_c; - } - } - #} # NO FOLD - print("\t\t" "_res = _res + \")\";") >> outFile_abs_c; - print("") >> outFile_abs_c; - print("\t\t" "return _res;") >> outFile_abs_c; - print("\t" "}") >> outFile_abs_c; - print("") >> outFile_abs_c; - } - } - - # make these available in called functions - implId_c_ = implId_c; - clsName_c_ = clsName_c; - printIntAndStb_c_ = printIntAndStb_c; - - # print member functions - members_size = cls_name_members[clsName_int_c ",*"]; - for (m=0; m < members_size; m++) { - memName_c = cls_name_members[clsName_int_c "," m]; - fullName_c = implId_c "," memName_c; - gsub(/,/, "_", fullName_c); - if (doWrappMember(fullName_c)) { - printMember(fullName_c, memName_c, addInds_size_c); - } else { - print("JavaOO-AIWrapper: NOTE: intentionally not wrapped: " fullName_c); - } - } - - - # finnish up - if (printIntAndStb_c) { - print("}") >> outFile_int_c; - print("") >> outFile_int_c; - close(outFile_int_c); - - print("}") >> outFile_abs_c; - print("") >> outFile_abs_c; - close(outFile_abs_c); - - print("}") >> outFile_stb_c; - print("") >> outFile_stb_c; - close(outFile_stb_c); - } - print("}") >> outFile_jni_c; - print("") >> outFile_jni_c; - close(outFile_jni_c); -} - - -function isRetParamName(paramName_rp) { - return (match(paramName_rp, /_out(_|$)/) || match(paramName_rp, /(^|_)?ret_/)); -} - - -function printMember(fullName_m, memName_m, additionalIndices_m) { - - # use some vars from the printClass function (which called us) - implId_m = implId_c_; - clsName_m = clsName_c_; - printIntAndStb_m = printIntAndStb_c_; - implCls_m = implCls_c; - clsName_int_m = clsName_int_c; - clsName_stb_m = clsName_stb_c; - clsName_jni_m = clsName_jni_c; - outFile_int_m = outFile_int_c; - outFile_stb_m = outFile_stb_c; - outFile_jni_m = outFile_jni_c; - addInds_size_m = addInds_size_c; - for (ai=1; ai <= addInds_size_m; ai++) { - addInds_m[ai] = addInds_c[ai]; - } - - indent_m = "\t"; - memId_m = clsName_m "," memName_m; - retType = cls_memberId_retType[memId_m]; # this may be changed - retType_int = retType; # this is a const var - params = cls_memberId_params[memId_m]; - isFetcher = cls_memberId_isFetcher[memId_m]; - metaComment = cls_memberId_metaComment[memId_m]; - memName = fullName_m; - sub(/^.*_/, "", memName); - functionName_m = fullName_m; - sub(/^[^_]+_/, "", functionName_m); - - if (memId_m in clsAvailInd_memberId_cls) { - return; - } - - isVoid_int_m = (retType_int == "void"); - - retVar_int_m = "_ret_int"; # this is a const var - retVar_out_m = retVar_int_m; # this may be changed - declaredVarsCode = ""; - conversionCode_pre = ""; - conversionCode_post = ""; - thrownExceptions = ""; - ommitMainCall = 0; - - if (!isVoid_int_m) { - declaredVarsCode = "\t\t" retType_int " " retVar_int_m ";" "\n" declaredVarsCode; - } - - - # Rewrite meta comment - if (match(metaComment, /FETCHER:MULTI:IDs:/)) { - # convert this: FETCHER:MULTI:IDs:Group:groupIds - # to this: ARRAY:groupIds->Group - _mc_pre = metaComment; - sub(/FETCHER:MULTI:IDs:.*$/, "", _mc_pre); - _mc_fet = metaComment; - sub(/^.*FETCHER:MULTI:IDs:/, "", _mc_fet); - sub(/[ \t].*$/, "", _mc_fet); - _mc_post = metaComment; - sub(/^.*FETCHER:MULTI:IDs:[^ \t]*/, "", _mc_post); - - _refObj = _mc_fet; - sub(/:.*$/, "", _refObj); - _refPaNa = _mc_fet; - sub(/^.*:/, "", _refPaNa); - - _mc_newArr = "ARRAY:" _refPaNa "->" _refObj; - metaComment = _mc_pre _mc_newArr _mc_post; - } - if (match(metaComment, /FETCHER:MULTI:NUM:/)) { - # convert this: FETCHER:MULTI:NUM:Resource - # to this: ARRAY:RETURN_SIZE->Resource - sub(/FETCHER:MULTI:NUM:/, "ARRAY:RETURN_SIZE->", metaComment); - } - if (match(metaComment, /REF:MULTI:/)) { - # convert this: REF:MULTI:unitDefIds->UnitDef - # to this: ARRAY:unitDefIds->UnitDef - sub(/REF:MULTI:/, "ARRAY:", metaComment); - } - - # remove additional indices from the outer params - for (ai=1; ai <= addInds_size_m; ai++) { - _removed = sub(/[^,]+(, )?/, "", params); - if (!_removed && !part_isStatic(memName_m, metaComment)) { - addIndName = addInds_m[ai]; - print("ERROR: failed removing additional indices " addIndName " from method " memName_m " in class " clsName_int_m); - exit(1); - } - } - - innerParams = removeParamTypes(params); - - # add additional indices fetcher calls to inner params - addInnerParams = ""; - addInds_real_size_m = addInds_size_m; - if (part_isStatic(memName_m, metaComment)) { - addInds_real_size_m--; - } - for (ai=1; ai <= addInds_real_size_m; ai++) { - addIndName = addInds_m[ai]; - _condComma = ""; - if (addInnerParams != "") { - _condComma = ", "; - } - addInnerParams = addInnerParams _condComma "this.get" capitalize(addIndName) "()"; - } - _condComma = ""; - if ((addInnerParams != "") && (innerParams != "")) { - _condComma = ", "; - } - innerParams = addInnerParams _condComma innerParams; - - - # convert param types - paramNames_size = split(innerParams, paramNames, ", "); - for (prm = 1; prm <= paramNames_size; prm++) { - paNa = paramNames[prm]; - if (!isRetParamName(paNa)) { - if (match(paNa, /_posF3/)) { - # convert float[3] to AIFloat3 - paNaNew = paNa; - sub(/_posF3/, "", paNaNew); - sub("float\\[\\] " paNa, "AIFloat3 " paNaNew, params); - conversionCode_pre = conversionCode_pre "\t\t" "float[] " paNa " = " paNaNew ".toFloatArray();" "\n"; - } else if (match(paNa, /_colorS3/)) { - # convert short[3] to java.awt.Color - paNaNew = paNa; - sub(/_colorS3/, "", paNaNew); - sub("short\\[\\] " paNa, "java.awt.Color " paNaNew, params); - conversionCode_pre = conversionCode_pre "\t\t" "short[] " paNa " = Util.toShort3Array(" paNaNew ");" "\n"; - } - } - } - - # convert an error return int value to an Exception - # "error-return:0=OK" - if (part_isErrorReturn(metaComment) && retType == "int") { - errorRetValueOk_m = part_getErrorReturnValueOk(metaComment); - - conversionCode_post = conversionCode_post "\t\t" "if (" retVar_out_m " != " errorRetValueOk_m ") {" "\n"; - conversionCode_post = conversionCode_post "\t\t\t" "throw new CallbackAIException(\"" memName_m "\", " retVar_out_m ");" "\n"; - conversionCode_post = conversionCode_post "\t\t" "}" "\n"; - thrownExceptions = thrownExceptions ", CallbackAIException" thrownExceptions; - - retType = "void"; - } - - # convert out params to return values - paramTypeNames_size = split(params, paramTypeNames, ", "); - hasRetParam = 0; - for (prm = 1; prm <= paramTypeNames_size; prm++) { - paNa = extractParamName(paramTypeNames[prm]); - if (isRetParamName(paNa)) { - if (retType == "void") { - paTy = extractParamType(paramTypeNames[prm]); - hasRetParam = 1; - if (match(paNa, /_posF3/)) { - # convert float[3] to AIFloat3 - retParamType = "AIFloat3"; - retVar_out_m = "_ret"; - conversionCode_pre = conversionCode_pre "\t\t" "float[] " paNa " = new float[3];" "\n"; - conversionCode_post = conversionCode_post "\t\t" retVar_out_m " = new AIFloat3(" paNa "[0], " paNa "[1]," paNa "[2]);" "\n"; - declaredVarsCode = "\t\t" retParamType " " retVar_out_m ";" "\n" declaredVarsCode; - sub("(, )?float\\[\\] " paNa, "", params); - retType = retParamType; - } else if (match(paNa, /_colorS3/)) { - retParamType = "java.awt.Color"; - retVar_out_m = "_ret"; - conversionCode_pre = conversionCode_pre "\t\t" "short[] " paNa " = new short[3];" "\n"; - conversionCode_post = conversionCode_post "\t\t" retVar_out_m " = Util.toColor(" paNa ");" "\n"; - declaredVarsCode = "\t\t" retParamType " " retVar_out_m ";" "\n" declaredVarsCode; - sub("(, )?short\\[\\] " paNa, "", params); - retType = retParamType; - } else if (match(paTy, /StringBuffer/)) { - retParamType = "String"; - retVar_out_m = "_ret"; - conversionCode_pre = conversionCode_pre "\t\t" "StringBuffer " paNa " = new StringBuffer();" "\n"; - conversionCode_post = conversionCode_post "\t\t" retParamType " " retVar_out_m " = " paNa ".toString();" "\n"; - sub("(, )?StringBuffer " paNa, "", params); - retType = retParamType; - } else { - print("FAILED converting return param: " paramTypeNames[prm] " / " fullName_m); - exit(1); - } - } else { - print("FAILED converting return param: return type should be \"void\", but is \"" retType "\""); - exit(1); - } - } - } - - # REF: - refObjs_size_m = split(metaComment, refObjs_m, "REF:"); - for (ro=2; ro <= refObjs_size_m; ro++) { - _ref = refObjs_m[ro]; - sub(/[ \t].*$/, "", _ref); # remove parts after this REF part - _isMulti = match(_ref, /MULTI:/); - _isReturn = match(_ref, /RETURN->/); - - if (!_isMulti && !_isReturn) { - # convert single param reference - _refRel = _ref; - sub(/^.*:/, "", _refRel); - _paNa = _refRel; # example: resourceId - sub(/->.*$/, "", _paNa); - _refObj = _refRel; # example: Resource - sub(/^.*->/, "", _refObj); - _paNaNew = _paNa; - if (!sub(/Id$/, "", _paNaNew)) { - _paNaNew = "oo_" _paNaNew; - } - - if (_refObj == "Team" || _refObj == "FigureGroup" || _refObj == "Path") { - print("note: ignoring meta comment: REF:" _ref); - } else { - _paNa_found = sub("int " _paNa, _refObj " " _paNaNew, params); - # it may not be found if it is an output parameter - if (_paNa_found) { - conversionCode_pre = conversionCode_pre "\t\t" "int " _paNa " = " _paNaNew ".get" _refObj "Id();" "\n"; - } - } - } else if (!_isMulti && _isReturn) { - _refObj = _ref; # example: Resource - sub(/^.*->/, "", _refObj); - - if (_refObj == "Team" || _refObj == "FigureGroup" || _refObj == "Path") { - print("note: ignoring meta comment: REF:" _ref); - continue; - } - - _implId = implId_m "," _refObj; - if (_implId in cls_implId_fullClsName) { - _fullClsName = cls_implId_fullClsName[_implId]; - } else if (cls_name_implIds[_refObj ",*"] == 1) { - _fullClsName = cls_name_implIds[_refObj ",0"]; - _fullClsName = cls_implId_fullClsName[_fullClsName]; - } else { - print("ERROR: failed finding the full class name for: " _refObj); - exit(1); - } - - _retVar_out_new = retVar_out_m "_out"; - _wrappGetInst_params = myWrapVar; - _hasRetInd = 0; - _inPa_size = split(innerParams, _, ","); - if (retType != "void" && (_inPa_size == addInds_size_m || _inPa_size == 0)) { - _hasRetInd = 1; - } - for (ai=1; ai <= (addInds_size_m-_hasRetInd); ai++) { - # Very hacky! too unmotivated for proper fix, sorry. - # proper fix would involve getting the parent of the wrapped - # class and using its additional indices - if ((functionName_m != "UnitDef_WeaponMount_getWeaponDef") && (functionName_m != "Unit_Weapon_getDef")) { - _wrappGetInst_params = _wrappGetInst_params ", " addInds_m[ai]; - } - } - if (retType != "void") { - _wrappGetInst_params = _wrappGetInst_params ", " retVar_out_m; - } else { - ommitMainCall = 1; - } - conversionCode_post = conversionCode_post "\t\t" _retVar_out_new " = Wrapp" _fullClsName ".getInstance(" _wrappGetInst_params ");" "\n"; - declaredVarsCode = "\t\t" _refObj " " _retVar_out_new ";" "\n" declaredVarsCode; - retVar_out_m = _retVar_out_new; - retType = _refObj; - } else { - print("WARNING: unsupported: REF:" _ref); - } - } - - - isMap = part_isMap(fullName_m, metaComment); - if (isMap) { - - _isFetching = 1; - _isRetSize = 0; - _isObj = 0; - _mapVar_size = "_size"; - _mapVar_keys = "keys"; - _mapVar_values = "values"; - _mapType_key = "String"; - _mapType_value = "String"; - _mapType_oo_key = "String"; - _mapType_oo_value = "String"; - _mapVar_oo = "_map"; - _mapType_int = "java.util.Map<" _mapType_oo_key ", " _mapType_oo_value ">"; - _mapType_impl = "java.util.HashMap<" _mapType_oo_key ", " _mapType_oo_value ">"; - - sub("(, )?" _mapType_key "\\[\\] " _mapVar_keys, "", params); - sub("(, )?" _mapType_value "\\[\\] " _mapVar_values, "", params); - sub(/, $/ , "", params); - - declaredVarsCode = "\t\t" "int " _mapVar_size ";" "\n" declaredVarsCode; - if (_isFetching) { - declaredVarsCode = "\t\t" _mapType_int " " _mapVar_oo ";" "\n" declaredVarsCode; - } - if (!_isRetSize) { - declaredVarsCode = "\t\t" _mapType_key "[] " _mapVar_keys ";" "\n" declaredVarsCode; - declaredVarsCode = "\t\t" _mapType_value "[] " _mapVar_values ";" "\n" declaredVarsCode; - if (_isFetching) { - conversionCode_pre = conversionCode_pre "\t\t" _mapVar_keys " = null;" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" _mapVar_values " = null;" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" _mapVar_size " = " myWrapVar "." functionName_m "(" innerParams ");" "\n"; - } else { - #conversionCode_pre = conversionCode_pre "\t\t" _arraySizeVar " = " _arrayListVar ".size();" "\n"; - #conversionCode_pre = conversionCode_pre "\t\t" "int _size = " _arraySizeVar ";" "\n"; - } - } - - if (_isRetSize) { - #conversionCode_post = conversionCode_post "\t\t" _arraySizeVar " = " retVar_out_m ";" "\n"; - #_arraySizeMaxPaNa = _arraySizeVar; - } else { - conversionCode_pre = conversionCode_pre "\t\t" _mapVar_keys " = new " _mapType_key "[" _mapVar_size "];" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" _mapVar_values " = new " _mapType_value "[" _mapVar_size "];" "\n"; - } - - if (_isFetching) { - # convert to a HashMap - conversionCode_post = conversionCode_post "\t\t" _mapVar_oo " = new " _mapType_impl "();" "\n"; - conversionCode_post = conversionCode_post "\t\t" "for (int i=0; i < " _mapVar_size "; i++) {" "\n"; -# if (_isObj) { -# if (_isRetSize) { - conversionCode_post = conversionCode_post "\t\t\t" _mapVar_oo ".put(" _mapVar_keys "[i], " _mapVar_values "[i]);" "\n"; -# } else { - #conversionCode_post = conversionCode_post "\t\t\t" _mapVar_oo ".put(" myPkgA ".Wrapp" _refObj ".getInstance(" myWrapVar _addWrappVars ", " _arrayPaNa "[i]));" "\n"; -# } -# } else if (_isNative) { - #conversionCode_post = conversionCode_post "\t\t\t" _arrayListVar ".add(" _arrayPaNa "[i]);" "\n"; -# } - conversionCode_post = conversionCode_post "\t\t" "}" "\n"; - - retParamType = _mapType_int; - retVar_out_m = _mapVar_oo; - retType = retParamType; - } else { - # convert from a HashMap - } - } - - - isArray = part_isArray(fullName_m, metaComment); - if (isArray) { - _refObj = ""; - _arrayPaNa = metaComment; - _addWrappVars = ""; - sub(/^.*ARRAY:/, "", _arrayPaNa); - sub(/[ \t].*$/, "", _arrayPaNa); - if (match(_arrayPaNa, /->/)) { - _refObj = _arrayPaNa; - sub(/->.*$/, "", _arrayPaNa); - sub(/^.*->/, "", _refObj); - _refObjInt = _refObj; - - if (match(_refObj, /-/)) { - sub(/-.*$/, "", _refObj); - sub(/^.*-/, "", _refObjInt); - } - _implId = implId_m "," _refObj; - if (_implId in cls_implId_fullClsName) { - _fullClsName = cls_implId_fullClsName[_implId]; - } else if ((myRootClass "," _refObj) in cls_implId_fullClsName) { - _implId = myRootClass "," _refObj; - _fullClsName = cls_implId_fullClsName[_implId]; - } else { - print("ERROR: failed to find the full class name for " _refObj " in " fullName_m); - exit(1); - } - _refObj = _fullClsName; - _addWrappVars = cls_implId_indicesArgs[_implId]; - sub(/(,)?[^,]*$/, "", _addWrappVars); # remove last index - _addWrappVars = trim(removeParamTypes(_addWrappVars)); - if (_addWrappVars != "") { - _addWrappVars = ", " _addWrappVars; - } - } - - _isF3 = match(_arrayPaNa, /_AposF3/); - _isObj = (_refObj != ""); - _isNative = (!_refObj && !_isObjc); - - _isRetSize = 0; - if (_isObj) { - _isRetSize = (_arrayPaNa == "RETURN_SIZE"); - } - - _arrayType = params; - sub("\\[\\][ \t]" _arrayPaNa ".*$", "", _arrayType); - sub("^.*[ \t]", "", _arrayType); - _arraySizeMaxPaNa = _arrayPaNa "_sizeMax"; - _arraySizeVar = _arrayPaNa "_size"; - _arraySizeRaw = _arrayPaNa "_raw_size"; - - _arrayListVar = _arrayPaNa "_list"; - if (_isF3) { - _arrListGenType = "AIFloat3"; - } else if (_isObj) { - _arrListGenType = _refObjInt; - } else if (_isNative) { - _arrListGenType = convertJavaBuiltinTypeToClass(_arrayType); - } - _arrListType = "java.util.List<" _arrListGenType ">"; - _arrListImplType = "java.util.ArrayList<" _arrListGenType ">"; - - _isFetching = sub("(, )?int " _arraySizeMaxPaNa, "", params); - if (_isRetSize) { - _isFetching = 1; - } else { - if (!_isFetching && !_isRetSize) { - _isNonFetcher = sub("(, )?int " _arraySizeVar, "", params); - if (!_isNonFetcher) { - print("ERROR: neither propper fetcher nor supplier ARRAY syntax in function: " fullName_m); - exit(1); - } - } - if (_isFetching) { - sub(_arrayType "\\[\\] " _arrayPaNa, "", params); - } else { - sub(_arrayType "\\[\\] " _arrayPaNa, _arrListType " " _arrayListVar, params); - } - sub(/^, /, "", params); - sub(/, $/, "", params); - } - - declaredVarsCode = "\t\t" "int " _arraySizeVar ";" "\n" declaredVarsCode; - if (_isFetching) { - declaredVarsCode = "\t\t" _arrListType " " _arrayListVar ";" "\n" declaredVarsCode; - } - if (!_isRetSize) { - declaredVarsCode = "\t\t" _arrayType "[] " _arrayPaNa ";" "\n" declaredVarsCode; - declaredVarsCode = "\t\t" "int " _arraySizeRaw ";" "\n" declaredVarsCode; - if (_isFetching) { - declaredVarsCode = "\t\t" "int " _arraySizeMaxPaNa ";" "\n" declaredVarsCode; - conversionCode_pre = conversionCode_pre "\t\t" _arraySizeMaxPaNa " = Integer.MAX_VALUE;" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" _arrayPaNa " = null;" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" _arraySizeVar " = " myWrapVar "." functionName_m "(" innerParams ");" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" _arraySizeMaxPaNa " = " _arraySizeVar ";" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" _arraySizeRaw " = " _arraySizeVar ";" "\n"; - if (_isF3) { - conversionCode_pre = conversionCode_pre "\t\t" "if (" _arraySizeVar " % 3 != 0) {" "\n"; - conversionCode_pre = conversionCode_pre "\t\t\t" "throw new RuntimeException(\"returned AIFloat3 array has incorrect size (\" + " _arraySizeVar "+ \"), should be a multiple of 3.\");" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" "}" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" _arraySizeVar " /= 3;" "\n"; - } - } else { - conversionCode_pre = conversionCode_pre "\t\t" _arraySizeVar " = " _arrayListVar ".size();" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" "int _size = " _arraySizeVar ";" "\n"; - if (_isF3) { - conversionCode_pre = conversionCode_pre "\t\t" _arraySizeVar " *= 3;" "\n"; - } - conversionCode_pre = conversionCode_pre "\t\t" _arraySizeRaw " = " _arraySizeVar ";" "\n"; - } - } - - if (_isRetSize) { - conversionCode_post = conversionCode_post "\t\t" _arraySizeVar " = " retVar_out_m ";" "\n"; - _arraySizeMaxPaNa = _arraySizeVar; - } else { - conversionCode_pre = conversionCode_pre "\t\t" _arrayPaNa " = new " _arrayType "[" _arraySizeRaw "];" "\n"; - } - - if (_isFetching) { - # convert to an ArrayList - conversionCode_post = conversionCode_post "\t\t" _arrayListVar " = new " _arrListImplType "(" _arraySizeVar ");" "\n"; - conversionCode_post = conversionCode_post "\t\t" "for (int i=0; i < " _arraySizeMaxPaNa "; i++) {" "\n"; - if (_isF3) { - conversionCode_post = conversionCode_post "\t\t\t" _arrayListVar ".add(new AIFloat3(" _arrayPaNa "[i], " _arrayPaNa "[++i], " _arrayPaNa "[++i]));" "\n"; - } else if (_isObj) { - if (_isRetSize) { - conversionCode_post = conversionCode_post "\t\t\t" _arrayListVar ".add(" myPkgA ".Wrapp" _refObj ".getInstance(" myWrapVar _addWrappVars ", i));" "\n"; - } else { - conversionCode_post = conversionCode_post "\t\t\t" _arrayListVar ".add(" myPkgA ".Wrapp" _refObj ".getInstance(" myWrapVar _addWrappVars ", " _arrayPaNa "[i]));" "\n"; - } - } else if (_isNative) { - conversionCode_post = conversionCode_post "\t\t\t" _arrayListVar ".add(" _arrayPaNa "[i]);" "\n"; - } - conversionCode_post = conversionCode_post "\t\t" "}" "\n"; - - retParamType = _arrListType; - retVar_out_m = _arrayListVar; - retType = retParamType; - } else { - # convert from an ArrayList - conversionCode_pre = conversionCode_pre "\t\t" "for (int i=0; i < _size; i++) {" "\n"; - if (_isF3) { - conversionCode_pre = conversionCode_pre "\t\t\t" "int arrInd = i*3;" "\n"; - conversionCode_pre = conversionCode_pre "\t\t\t" "AIFloat3 aif3 = " _arrayListVar ".get(i);" "\n"; - conversionCode_pre = conversionCode_pre "\t\t\t" _arrayPaNa "[arrInd] = aif3.x;" "\n"; - conversionCode_pre = conversionCode_pre "\t\t\t" _arrayPaNa "[arrInd+1] = aif3.y;" "\n"; - conversionCode_pre = conversionCode_pre "\t\t\t" _arrayPaNa "[arrInd+2] = aif3.z;" "\n"; - } else if (_isObj) { - conversionCode_pre = conversionCode_pre "\t\t\t" _arrayPaNa "[i] = " _arrayListVar ".get(i).get" _refObj "Id();" "\n"; - } else if (_isNative) { - conversionCode_pre = conversionCode_pre "\t\t\t" _arrayPaNa "[i] = " _arrayListVar ".get(i);" "\n"; - } - conversionCode_pre = conversionCode_pre "\t\t" "}" "\n"; - } - } - - firstLineEnd = ";"; - mod_m = ""; - if (!isInterface_m) { - firstLineEnd = " {"; - mod_m = "public "; - } - - sub(/^, /, "", thrownExceptions); - - print("") >> outFile_jni_m; - - isBuffered_m = !isVoid_m && isBufferedFunc(fullName_m) && (params == ""); - if (!isInterface_m && isBuffered_m) { - print(indent_m retType " _buffer_" memName ";") >> outFile_jni_m; - print(indent_m "boolean _buffer_isInitialized_" memName " = false;") >> outFile_jni_m; - } - - # print method doc comment - fullName_doc_m = fullName_m; - sub(/^[^_]*_/, "", fullName_doc_m); # remove OOAICallback_ - if (printIntAndStb_m) { - printFunctionComment_Common(outFile_int_m, funcDocComment, fullName_doc_m, indent_m); - printFunctionComment_Common(outFile_stb_m, funcDocComment, fullName_doc_m, indent_m); - } - printFunctionComment_Common(outFile_jni_m, funcDocComment, fullName_doc_m, indent_m); - - _fNoOverride = 0; - commentText = getFunctionComment_Common(funcDocComment, fullName_doc_m); - _fIsDeprecated = match(commentText, /@deprecated/); - printTripleFunc(retType, memName, params, thrownExceptions, outFile_int_m, outFile_stb_m, outFile_jni_m, printIntAndStb_m, _fNoOverride, _fIsDeprecated); - - isVoid_m = (retType == "void"); - - if (!isInterface_m) { - condRet_int_m = isVoid_int_m ? "" : retVar_int_m " = "; - indent_m = indent_m "\t"; - - if (isBuffered_m) { - print(indent_m "if (!_buffer_isInitialized_" memName ") {") >> outFile_jni_m; - indent_m = indent_m "\t"; - } - if (declaredVarsCode != "") { - print(declaredVarsCode) >> outFile_jni_m; - } - if (conversionCode_pre != "") { - print(conversionCode_pre) >> outFile_jni_m; - } - if (!ommitMainCall) { - print(indent_m condRet_int_m myWrapVar "." functionName_m "(" innerParams ");") >> outFile_jni_m; - } - if (conversionCode_post != "") { - print(conversionCode_post) >> outFile_jni_m; - } - if (isBuffered_m) { - print(indent_m "_buffer_" memName " = " retVar_out_m ";") >> outFile_jni_m; - print(indent_m "_buffer_isInitialized_" memName " = true;") >> outFile_jni_m; - sub(/\t/, "", indent_m); - print(indent_m "}") >> outFile_jni_m; - print("") >> outFile_jni_m; - retVar_out_m = "_buffer_" memName; - } - if (!isVoid_m) { - print(indent_m "return " retVar_out_m ";") >> outFile_jni_m; - } - sub(/\t/, "", indent_m); - print(indent_m "}") >> outFile_jni_m; - } -} - - -function doWrappMember(fullName_dwm) { - - doWrapp_dwm = 1; - - return doWrapp_dwm; -} - -# Used by the common OO AWK script -function doWrappOO(funcFullName_dw, params_dw, metaComment_dw) { - - doWrapp_dw = 1; - - #doWrapp_dw = doWrapp_dw && !match(funcFullName_dw, /Lua_callRules/) && !match(funcFullName_dw, /Lua_callUI/); - - return doWrapp_dw; -} - -function wrappFunctionDef(funcDef, commentEolTot) { - - size_funcParts = split(funcDef, funcParts, "(\\()|(\\);)"); - # because the empty part after ");" would count as part as well - size_funcParts--; - - fullName = funcParts[1]; - fullName = trim(fullName); - sub(/.*[ \t]+/, "", fullName); - - retType = funcParts[1]; - sub(/[ \t]*public/, "", retType); - sub(fullName, "", retType); - retType = trim(retType); - - params = funcParts[2]; - - wrappFunctionPlusMeta(retType, fullName, params, commentEolTot); -} - -# This function has to return true (1) if a doc comment (eg: /** foo bar */) -# can be deleted. -# If there is no special condition you want to apply, -# it should always return true (1), -# cause there are additional mechanism to prevent accidental deleting. -# see: commonDoc.awk -function canDeleteDocumentation() { - return isMultiLineFunc != 1; -} - - -# grab callback functions info -# 2nd, 3rd, ... line of a function definition -{ - if (isMultiLineFunc) { # function is defined on one single line - funcIntermLine = $0; - # separate possible comment at end of line: // fu bar - commentEol = funcIntermLine; - if (sub(/.*\/\//, "", commentEol)) { - commentEolTot = commentEolTot commentEol; - } - sub(/[ \t]*\/\/.*$/, "", funcIntermLine); - funcIntermLine = trim(funcIntermLine); - funcSoFar = funcSoFar " " funcIntermLine; - if (match(funcSoFar, /;$/)) { - # function ends in this line - wrappFunctionDef(funcSoFar, commentEolTot); - isMultiLineFunc = 0; - } - } -} -# 1st line of a function definition -/\tpublic .*\);/ { - - funcStartLine = $0; - # separate possible comment at end of line: // foo bar - commentEolTot = ""; - commentEol = funcStartLine; - if (sub(/.*\/\//, "", commentEol)) { - commentEolTot = commentEolTot commentEol; - } - # remove possible comment at end of line: // foo bar - sub(/\/\/.*$/, "", funcStartLine); - funcStartLine = trim(funcStartLine); - if (match(funcStartLine, /;$/)) { - # function ends in this line - wrappFunctionDef(funcStartLine, commentEolTot); - } else { - funcSoFar = funcStartLine; - isMultiLineFunc = 1; - } -} - - - -END { - # finalize things - store_everything(); - printClasses(); -} diff --git a/AI/Wrappers/JavaOO/bin/wrappEvents.awk b/AI/Wrappers/JavaOO/bin/wrappEvents.awk deleted file mode 100755 index d044b135740..00000000000 --- a/AI/Wrappers/JavaOO/bin/wrappEvents.awk +++ /dev/null @@ -1,627 +0,0 @@ -#!/usr/bin/awk -f -# -# This awk script creates a java class in OO style to wrap the C style -# JNI based AI Events wrapper interface. -# In other words, the output of this file wraps: -# com/springrts/ai/AI.java -# which wraps: -# rts/ExternalAI/Interface/AISEvents.h -# -# This script uses functions from the following files: -# * common.awk -# * commonDoc.awk -# * commonOOCallback.awk -# Variables that can be set on the command-line (with -v): -# * GENERATED_SOURCE_DIR : the generated sources root dir -# * JAVA_GENERATED_SOURCE_DIR : the generated java sources root dir -# * INTERFACE_SOURCE_DIR : the Java AI Interfaces static source files root dir -# * INTERFACE_GENERATED_SOURCE_DIR : the Java AI Interfaces generated source files root dir -# - -BEGIN { - # initialize things - - # define the field splitter(-regex) - FS = "(\\()|(\\);)"; - IGNORECASE = 0; - - # Used by other scripts - JAVA_MODE = 1; - - # These vars can be assigned externally, see file header. - # Set the default values if they were not supplied on the command line. - if (!GENERATED_SOURCE_DIR) { - GENERATED_SOURCE_DIR = "../src-generated/main"; - } - if (!JAVA_GENERATED_SOURCE_DIR) { - JAVA_GENERATED_SOURCE_DIR = GENERATED_SOURCE_DIR "/java"; - } - if (!INTERFACE_SOURCE_DIR) { - INTERFACE_SOURCE_DIR = "../../../Interfaces/Java/src/main/java"; - } - if (!INTERFACE_GENERATED_SOURCE_DIR) { - INTERFACE_GENERATED_SOURCE_DIR = "../../../Interfaces/Java/src-generated/main/java"; - } - - javaSrcRoot = "../src/main/java"; - - myParentPkgA = "com.springrts.ai"; - myMainPkgA = myParentPkgA ".oo"; - myPkgClbA = myMainPkgA ".clb"; - myPkgEvtA = myMainPkgA ".evt"; - myMainPkgD = convertJavaNameFormAToD(myMainPkgA); - myPkgEvtD = convertJavaNameFormAToD(myPkgEvtA); - - aiFloat3Class = "AIFloat3"; - - myOOAIClass = "OOAI"; - myOOAIInterface = "I" myOOAIClass; - myOOAIAbstractClass = "AbstractOOAI"; - myOOEventAIClass = "OOEventAI"; - myOOEventAIInterface = "I" myOOEventAIClass; - myOOAIFile = JAVA_GENERATED_SOURCE_DIR "/" myMainPkgD "/" myOOAIClass ".java"; - myOOAIInterfaceFile = JAVA_GENERATED_SOURCE_DIR "/" myMainPkgD "/" myOOAIInterface ".java"; - myOOAIAbstractFile = JAVA_GENERATED_SOURCE_DIR "/" myMainPkgD "/" myOOAIAbstractClass ".java"; - myOOEventAIFile = JAVA_GENERATED_SOURCE_DIR "/" myMainPkgD "/" myOOEventAIClass ".java"; - myOOEventAIInterfaceFile = JAVA_GENERATED_SOURCE_DIR "/" myMainPkgD "/" myOOEventAIInterface ".java"; - - printOOAIHeader(myOOAIFile, myOOAIClass); - printOOAIHeader(myOOAIInterfaceFile, myOOAIInterface); - printOOAIHeader(myOOAIAbstractFile, myOOAIAbstractClass); - printOOEventAIHeader(myOOEventAIFile); - printOOEventAIHeader(myOOEventAIInterfaceFile); - - ind_evt = 0; -} - - - -function printOOAIHeader(outFile, clsName) { - - printCommentsHeader(outFile); - print("") >> outFile; - print("package " myMainPkgA ";") >> outFile; - print("") >> outFile; - print("") >> outFile; - if (clsName != myOOAIInterface) { - print("import " myParentPkgA ".AI;") >> outFile; - } - if (clsName == myOOAIClass) { - print("import " myParentPkgA ".AICallback;") >> outFile; - print("import " myMainPkgA ".clb.WrappOOAICallback;") >> outFile; - print("import " myMainPkgA ".clb.WrappUnit;") >> outFile; - print("import " myMainPkgA ".clb.WrappWeaponDef;") >> outFile; - } - print("import " myMainPkgA ".AIFloat3;") >> outFile; - print("import " myMainPkgA ".clb.OOAICallback;") >> outFile; - print("import " myMainPkgA ".clb.Unit;") >> outFile; - print("import " myMainPkgA ".clb.WeaponDef;") >> outFile; - print("") >> outFile; - print("/**") >> outFile; - print(" * TODO: Add description here") >> outFile; - print(" *") >> outFile; - print(" * @author hoijui") >> outFile; - print(" * @version GENERATED") >> outFile; - print(" */") >> outFile; - - _type = "abstract class"; - _extends = ""; - _implements = " implements " myOOAIInterface ", AI"; - if (clsName == myOOAIAbstractClass) { - _extends = " extends " myOOAIClass; - } - if (clsName == myOOAIInterface) { - _type = "interface"; - _implements = ""; - } - print("public " _type " " clsName _extends _implements " {") >> outFile; - print("") >> outFile; - - if (clsName == myOOAIClass) { - print("\t" "private AICallback clb = null;") >> outFile; - print("\t" "private OOAICallback clbOO = null;") >> outFile; - print("") >> outFile; - } -} -function printOOAIEnd(outFile) { - - print("}") >> outFile; - print("") >> outFile; -} - -function printOOEventAIHeader(outFile) { - - printCommentsHeader(outFile); - print("") >> outFile; - print("package " myMainPkgA ";") >> outFile; - print("") >> outFile; - print("") >> outFile; - print("import " myParentPkgA ".AI;") >> outFile; - print("import " myMainPkgA "." myOOAIClass ";") >> outFile; - print("import " myPkgEvtA ".*;") >> outFile; - print("import " myPkgClbA ".*;") >> outFile; - print("") >> outFile; - print("/**") >> outFile; - print(" * TODO: Add description here") >> outFile; - print(" *") >> outFile; - print(" * @author hoijui") >> outFile; - print(" * @version GENERATED") >> outFile; - print(" */") >> outFile; - if (outFile == myOOEventAIFile) { - print("public abstract class " myOOEventAIClass " extends " myOOAIClass " implements " myOOEventAIInterface ", AI {") >> outFile; - } else { - print("public interface " myOOEventAIInterface " {") >> outFile; - } - print("") >> outFile; - - print("\t" "/**") >> outFile; - print("\t" " * TODO: Add description here") >> outFile; - print("\t" " *") >> outFile; - print("\t" " * @param event the AI event to handle, sent by the engine") >> outFile; - print("\t" " * @throws AIException") >> outFile; - print("\t" " */") >> outFile; - _modifyer = ""; - if (outFile == myOOEventAIFile) { - _modifyer = " abstract"; - } - print("\t" "public" _modifyer " void handleEvent(AIEvent event) throws EventAIException;") >> outFile; - print("") >> outFile; -} -function printOOEventAIEnd(outFile) { - - print("}") >> outFile; - print("") >> outFile; -} - - -function convertJavaSimpleTypeToOO(paType_sto, paName_sto, - paTypeNew_sto, paNameNew_sto, convPre_sto, convPost_sto) { - - _change = 1; - - # uses this global vars: - # - paramTypeNew - # - paramNameNew - # - conversionCode_pre - # - conversionCode_post - - paramTypeNew = paType_sto; - paramNameNew = paName_sto; - - if (match(paName_sto, /_posF3/)) { - # convert float[3] to AIFloat3 - sub(/_posF3/, "", paramNameNew); - paramTypeNew = "AIFloat3"; - conversionCode_pre = conversionCode_pre "\t\t" paramTypeNew " " paramNameNew " = new " paramTypeNew "(" paName_sto ");" "\n"; - #conversionCode_pre = conversionCode_pre "\t\t" paType_sto " " paName_sto " = " paNameNew_sto ".toFloatArray();" "\n"; - } else if (match(paName_sto, /_colorS3/)) { - # convert short[3] to java.awt.Color - sub(/_colorS3/, "", paramNameNew); - paramTypeNew = "java.awt.Color"; - conversionCode_pre = conversionCode_pre "\t\t" paramTypeNew " " paramNameNew " = Util.toColor(" paName_sto ");" "\n"; - #conversionCode_pre = conversionCode_pre "\t\t" paType_sto " " paName_sto " = Util.toShort3Array(" paNameNew_sto ");" "\n"; - } else if ((paType_sto == "int") && match(paName_sto, /(unit|builder|attacker|enemy)(Id)?$/)) { - # convert int to Unit - sub(/Id$/, "", paramNameNew); - paramNameNew = "oo_" paramNameNew; - paramTypeNew = "Unit"; - conversionCode_pre = conversionCode_pre "\t\t" paramTypeNew " " paramNameNew " = Wrapp" paramTypeNew ".getInstance(this.clb, " paName_sto ");" "\n"; - } else if ((paType_sto == "int[]") && match(paName_sto, /unit(s|Ids)$/)) { - # convert int[] to List - sub(/(Id)?s$/, "s", paramNameNew); - paramNameNew = "oo_" paramNameNew; - paramTypeNew = "java.util.List"; - conversionCode_pre = conversionCode_pre "\t\t" paramTypeNew " " paramNameNew " = new java.util.ArrayList();" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" "for (int u=0; u < " paName_sto ".length; u++) {" "\n"; - conversionCode_pre = conversionCode_pre "\t\t\t" paramNameNew ".add(WrappUnit.getInstance(this.clb, " paName_sto "[u]));" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" "}" "\n"; - } else if ((paType_sto == "int") && match(paName_sto, /(weaponDef)(Id)?$/)) { - # convert int to WeaponDef - sub(/Id$/, "", paramNameNew); - paramNameNew = "oo_" paramNameNew; - paramTypeNew = "WeaponDef"; - conversionCode_pre = conversionCode_pre "\t\t" paramTypeNew " " paramNameNew " = Wrapp" paramTypeNew ".getInstance(this.clb, " paName_sto ");" "\n"; - } else if (paType_sto == "AICallback") { - # convert AICallback to OOAICallback - paramNameNew = "oo_" paramNameNew; - paramTypeNew = "OOAICallback"; - conversionCode_pre = conversionCode_pre "\t\t" "this.clb = " paName_sto ";" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" "this.clbOO = Wrapp" paramTypeNew ".getInstance(" paName_sto ");" "\n"; - conversionCode_pre = conversionCode_pre "\t\t" paramTypeNew " " paramNameNew " = this.clbOO;" "\n"; - } else { - _change = 0; - } - - return _change; -} - - -function printEventsOO() { - - # agarra te los event interfaces - for (e=0; e < evts_size; e++) { - meta_es = evts_meta[e]; - - interfList_size_es = 0; - if (match(meta_es, /INTERFACES:/)) { - interfMeta_es = meta_es; - sub(/^.*INTERFACES:/, "", interfMeta_es); - sub(/[ \t].*$/, "", interfMeta_es); - interfList_size_es = split(interfMeta_es, interfList_es, /\),/); - - for (i=1; i <= interfList_size_es; i++) { - _intName = interfList_es[i]; - sub(/\(.*$/, "", _intName); - _intParams = interfList_es[i]; - sub(/^.*\(/, "", _intParams); - sub(/\)$/, "", _intParams); - - if (!(_intName in int_names)) { - int_names[_intName] = _intParams; - } - evts_intNames[e "," (i-1)] = _intName; - evts_intParams[e "," (i-1)] = _intParams; - } - } - evts_intNames[e ",*"] = interfList_size_es; - } - - # print the event classes - for (e=0; e < evts_size; e++) { - printEventOO(e); - } - - # print the event interfaces - for (intName in int_names) { - printOOEventInterface(intName); - } -} - -function printEventOO(ind_evt_em) { - - retType_em = evts_retType[ind_evt_em]; - name_em = evts_name[ind_evt_em]; - params_em = evts_params[ind_evt_em]; - meta_em = evts_meta[ind_evt_em]; - - paramsList_size_em = split(params_em, paramsList_em, ", "); - - conversionCode_pre = ""; - conversionCode_post = ""; - ooParams_em = ""; - - for (p=1; p <= paramsList_size_em; p++) { - _param = paramsList_em[p]; - _paramType = _param; - sub(/ [^ ]*$/, "", _paramType); - _paramName = _param; - sub(/^[^ ]* /, "", _paramName); - - paramTypeNew = ""; - paramNameNew = ""; - convertJavaSimpleTypeToOO(_paramType, _paramName); - - if (!match(_paramName, /_size$/)) { - ooParams_em = ooParams_em ", " paramTypeNew " " paramNameNew; - } - } - sub(/^, /, "", ooParams_em); - - _equalMethod = (ooParams_em == params_em); - _isVoid = (retType_em == "void"); - if (_isVoid) { - _condRet = ""; - } else { - _condRet = "_ret = "; - } - - print("") >> myOOAIFile; - - if (!_equalMethod) { - ooParamNames_em = removeParamTypes(ooParams_em); - print("\t" "@Override") >> myOOAIFile; - print("\t" "public final " retType_em " " name_em "(" params_em ") {") >> myOOAIFile; - print("") >> myOOAIFile; - - if (!_isVoid) { - print("\t\t" retType_em " _ret;") >> myOOAIFile; - print("") >> myOOAIFile; - } - if (conversionCode_pre != "") { - print(conversionCode_pre) >> myOOAIFile; - } - - print("\t\t" _condRet "this." name_em "(" ooParamNames_em ");") >> myOOAIFile; - - if (conversionCode_post != "") { - print(conversionCode_post) >> myOOAIFile; - } - if (!_isVoid) { - print("") >> myOOAIFile; - print("\t\t" "return _ret;") >> myOOAIFile; - } - - print("\t" "}") >> myOOAIFile; - } - - printFunctionComment_Common(myOOAIFile, evts_docComment, ind_evt_em, "\t"); - print("\t" "@Override") >> myOOAIFile; - if (retType_em == "int") { - print("\t" "public " retType_em " " name_em "(" ooParams_em ") { return 0; }") >> myOOAIFile; - } else { - print("Warning: No default return value given for event return-type " retType_em); - print("\t" "public abstract " retType_em " " name_em "(" ooParams_em ");") >> myOOAIFile; - } - - ooParamsCleaned_em = ooParams_em; - gsub(/ oo_/, " ", ooParamsCleaned_em); - - print("") >> myOOAIInterfaceFile; - printFunctionComment_Common(myOOAIInterfaceFile, evts_docComment, ind_evt_em, "\t"); - print("\t" "public " retType_em " " name_em "(" ooParamsCleaned_em ");") >> myOOAIInterfaceFile; - - print("") >> myOOAIAbstractFile; - printFunctionComment_Common(myOOAIAbstractFile, evts_docComment, ind_evt_em, "\t"); - print("\t" "@Override") >> myOOAIAbstractFile; - print("\t" "public " retType_em " " name_em "(" ooParamsCleaned_em ") {") >> myOOAIAbstractFile; - print("\t\t" "return 0; // signaling: OK") >> myOOAIAbstractFile; - print("\t" "}") >> myOOAIAbstractFile; - - printOOEventWrapper(retType_em, name_em, ooParamsCleaned_em, meta_em, ind_evt_em); -} - -function printOOEventWrapper(retType_ei, mthName_ei, ooParams_ei, meta_ei, ind_evt_ei) { - - outFile = myOOEventAIFile; - ooParamsNoTypes_ei = removeParamTypes(ooParams_ei); - evtName_ei = capitalize(mthName_ei) "AIEvent"; - - print("\t" "@Override") >> outFile; - print("\t" "public final " retType_ei " " mthName_ei "(" ooParams_ei ") {") >> outFile; - print("") >> outFile; - print("\t\t" "AIEvent evt = new " evtName_ei "(" ooParamsNoTypes_ei ");") >> outFile; - print("\t\t" "try {") >> outFile; - print("\t\t\t" "this.handleEvent(evt);") >> outFile; - print("\t\t\t" "return 0; // everything OK") >> outFile; - print("\t\t" "} catch (EventAIException ex) {") >> outFile; - print("\t\t\t" "return ex.getErrorNumber();") >> outFile; - print("\t\t" "}") >> outFile; - print("\t" "}") >> outFile; - print("") >> outFile; - - printOOEventClass(retType_ei, evtName_ei, ooParams_ei, meta_ei, ind_evt_ei); -} - -function printOOEventClass(retType_ec, evtName_ec, ooParams_ec, meta_ec, ind_evt_ec) { - - outFile = JAVA_GENERATED_SOURCE_DIR "/" myPkgEvtD "/" evtName_ec ".java"; - - ooParamsList_size_ec = split(ooParams_ec, ooParamsList_ec, ", "); - int_size_ec = evts_intNames[ind_evt_ec ",*"]; - - # list up interface parameters - # clear arrays - split("", intParams_toPrint_ec); - split("", myIntParams_intParams_ec); - split("", myIntParams_int_ec); - for (_ii=0; _ii < int_size_ec; _ii++) { - _name = evts_intNames[ind_evt_ec "," _ii]; - _myIntParams = evts_intParams[ind_evt_ec "," _ii]; - _intParams = int_names[_name]; - - _myIntParamsList_size = split(_myIntParams, _myIntParamsList, ","); - _intParamsList_size = split(_intParams, _intParamsList, ","); - - for (_p=1; _p <= _intParamsList_size; _p++) { - _myIntParam = _myIntParamsList[_p]; - _intParam = _intParamsList[_p]; - - intParams_toPrint_ec[_intParam] = 1; - myIntParams_int_ec[_myIntParam] = _name; - if (_myIntParam != _intParam) { - myIntParams_intParams_ec[_myIntParam] = _intParam; - } - } - } - - _addIntLst = ""; - for (i=0; i < int_size_ec; i++) { - _addIntLst = _addIntLst ", " evts_intNames[ind_evt_ec "," i] "AIEvent"; - } - - # print comments header - printCommentsHeader(outFile); - print("") >> outFile; - - print("package " myPkgEvtA ";") >> outFile; - print("") >> outFile; - print("") >> outFile; - - # print imports - print("import " myMainPkgA ".AIEvent;") >> outFile; - print("import " myMainPkgA ".AIFloat3;") >> outFile; - print("import " myPkgClbA ".*;") >> outFile; - print("") >> outFile; - - # print class header - printFunctionComment_Common(outFile, evts_docComment, ind_evt_ec, ""); - print("public class " evtName_ec " implements AIEvent" _addIntLst " {") >> outFile; - print("") >> outFile; - - # print member vars - for (_p=1; _p <= ooParamsList_size_ec; _p++) { - print("\t" "private " ooParamsList_ec[_p] ";") >> outFile; - } - print("") >> outFile; - - # print constructor - print("\t" "public " evtName_ec "(" ooParams_ec ") {") >> outFile; - print("") >> outFile; - for (_p=1; _p <= ooParamsList_size_ec; _p++) { - _name = extractParamName(ooParamsList_ec[_p]); - print("\t\t" "this." _name " = " _name ";") >> outFile; - } - print("\t" "}") >> outFile; - print("") >> outFile; - - # print member getters - for (_p=1; _p <= ooParamsList_size_ec; _p++) { - _type = extractParamType(ooParamsList_ec[_p]); - _name = extractParamName(ooParamsList_ec[_p]); - - # save the type for later writing of interfaces, - # in case it is a ninterface param - if (_name in intParams_toPrint_ec) { - _intName = _name; - if (_name in myIntParams_intParams_ec) { - _intName = myIntParams_intParams_ec[_name]; - } - _int = myIntParams_int_ec[_name]; - - int_name_param_type[_int "," _intName] = _type; - # hacky - int_name_param_type[_int "," _name] = _type; - } - - # print out @Override if this is an interface member - if (_name in intParams_toPrint_ec && intParams_toPrint_ec[_name] == 1) { - print("\t" "@Override") >> outFile; - intParams_toPrint_ec[_name] = 0; - } - - print("\t" "public " _type " get" capitalize(_name) "() {") >> outFile; - print("\t\t" "return this." _name ";") >> outFile; - print("\t" "}") >> outFile; - - # print out another getter if the interface param name is different - if (_name in myIntParams_intParams_ec) { - _intName = myIntParams_intParams_ec[_name]; - intParams_toPrint_ec[_intName] = 0; - - print("\t" "@Override") >> outFile; - print("\t" "public " _type " get" capitalize(_intName) "() {") >> outFile; - print("\t\t" "return this.get" capitalize(_name) "();") >> outFile; - print("\t" "}") >> outFile; - } - - print("") >> outFile; - } - - print("}") >> outFile; - - close(outFile); -} - -function printOOEventInterface(int_name_ei) { - - outFile = JAVA_GENERATED_SOURCE_DIR "/" myPkgEvtD "/" int_name_ei "AIEvent.java"; - - int_params_ei = int_names[int_name_ei]; - - int_paramsList_size_ei = split(int_params_ei, int_paramsList_ei, ","); - int_size_ei = evts_intNames[ind_evt_ec ",*"]; - - _addIntLst = ""; - for (i=0; i < int_size_ei; i++) { - _addIntLst = _addIntLst ", " evts_intNames[ind_evt_ec "," i] "AIEvent"; - } - - # print comments header - printCommentsHeader(outFile); - print("") >> outFile; - - print("package " myPkgEvtA ";") >> outFile; - print("") >> outFile; - print("") >> outFile; - - # print imports - print("import " myMainPkgA ".AIEvent;") >> outFile; - print("import " myMainPkgA ".AIFloat3;") >> outFile; - print("import " myPkgClbA ".*;") >> outFile; - print("") >> outFile; - - # print class header - print("public interface " int_name_ei "AIEvent extends AIEvent {") >> outFile; - print("") >> outFile; - - # print getters - for (_p=1; _p <= int_paramsList_size_ei; _p++) { - _name = int_paramsList_ei[_p]; - _type = int_name_param_type[int_name_ei "," _name]; - - print("\t" "public " _type " get" capitalize(_name) "();") >> outFile; - } - print("") >> outFile; - - print("}") >> outFile; -} - - - -# This function has to return true (1) if a doc comment (eg: /** foo bar */) -# can be deleted. -# If there is no special condition you want to apply, -# it should always return true (1), -# cause there are additional mechanism to prevent accidental deleting. -# see: commonDoc.awk -function canDeleteDocumentation() { - return 1; -} - -################################################################################ -### BEGIN: parsing and saving the event methods - -# beginning of struct S*Event -/^\tpublic .+\);/ { - - _head = $1; - _params = $2; - _tail = $3; - - _retType = _head; - sub(/^\tpublic /, "", _retType); # remove pre - sub(/ .*$/, "", _retType); # remove post - - _name = _head; - sub(/^\tpublic [^ ]+ /, "", _name); # remove pre - - _meta = $0; - _hasMeta = sub(/.*\);[ \t]*\/\/[ \t]*/, "", _meta); # remove pre - if (!_hasMeta) { - _meta = ""; - } - - evts_retType[ind_evt] = _retType; - evts_name[ind_evt] = _name; - evts_params[ind_evt] = _params; - evts_meta[ind_evt] = _meta; - storeDocLines(evts_docComment, ind_evt); -#print(_retType " " _name "(" _params ") // " _meta); - ind_evt++; -} - -### END: parsing and saving the event methods -################################################################################ - - - - -END { - # finalize things - - evts_size = ind_evt; - - printEventsOO(); - - printOOAIEnd(myOOAIFile); - printOOAIEnd(myOOAIInterfaceFile); - printOOAIEnd(myOOAIAbstractFile); - printOOEventAIEnd(myOOEventAIFile); - printOOEventAIEnd(myOOEventAIInterfaceFile); - - close(myOOAIFile); - close(myOOAIInterfaceFile); - close(myOOAIAbstractFile); - close(myOOEventAIFile); - close(myOOEventAIInterfaceFile); -} diff --git a/AI/Wrappers/JavaOO/jlib/vecmath-src.jar b/AI/Wrappers/JavaOO/jlib/vecmath-src.jar deleted file mode 100644 index 4f66594635f..00000000000 Binary files a/AI/Wrappers/JavaOO/jlib/vecmath-src.jar and /dev/null differ diff --git a/AI/Wrappers/JavaOO/jlib/vecmath.jar b/AI/Wrappers/JavaOO/jlib/vecmath.jar deleted file mode 100755 index 75b8ac577b0..00000000000 Binary files a/AI/Wrappers/JavaOO/jlib/vecmath.jar and /dev/null differ diff --git a/AI/Wrappers/JavaOO/pom.xml b/AI/Wrappers/JavaOO/pom.xml deleted file mode 100644 index cd552ebf475..00000000000 --- a/AI/Wrappers/JavaOO/pom.xml +++ /dev/null @@ -1,66 +0,0 @@ - - 4.0.0 - - - - - - 0.1 - - - - com.springrts - common-spring - 1.0 - ../../../rts/build/maven/support/common/spring/pom.xml - - - com.springrts - ai-wrapper-javaoo - ${my.version} - - jar - - Java OO AI Wrapper - Java Object Oriented Artificial Intelligence interface wrapper for the Spring RTS engine - https://springrts.com/wiki/AIWrapper:JavaOO - 2008 - - - - - scm:git:git://github.com/spring/spring.git - scm:git:git@github.com:spring/spring.git - http://github.com/spring/spring/tree/master/AI/Wrappers/JavaOO/ - - - - - - - com.springrts - ai-interface-java - ${my.version} - - - - java3d - vecmath - 1.3.1 - - - - - diff --git a/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/AIEvent.java b/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/AIEvent.java deleted file mode 100644 index 796112ef8a1..00000000000 --- a/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/AIEvent.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright (c) 2008 Robin Vobruba - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package com.springrts.ai.oo; - -/** - * An AI event is sent form the engine to Java Skirmish AIs. - * - * @author hoijui - * @version 0.1 - */ -public interface AIEvent { - -} diff --git a/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/AIException.java b/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/AIException.java deleted file mode 100644 index 2c3a9ddb927..00000000000 --- a/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/AIException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - Copyright (c) 2009 Robin Vobruba - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package com.springrts.ai.oo; - -/** - * Common base interface for AI related Exceptions. - * - * @author hoijui - * @version 0.1 - */ -public interface AIException { - - /** - * Returns the error associated with this Exception. - * This is used to send over low-level language interfaces, - * for example C, where exceptions are not supported. - * @return should be != 0, as this value is reserved for the no-error state - */ - public int getErrorNumber(); -} diff --git a/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/AIFloat3.java b/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/AIFloat3.java deleted file mode 100644 index 7865eac461c..00000000000 --- a/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/AIFloat3.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright (c) 2008 Robin Vobruba - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package com.springrts.ai.oo; - - -import javax.vecmath.Tuple3d; -import javax.vecmath.Tuple3f; -import javax.vecmath.Vector3d; -import javax.vecmath.Vector3f; -import java.awt.Color; - -/** - * Represents a position on the map. - * - * @author hoijui.quaero@gmail.com - * @version 0.1 - */ -public class AIFloat3 extends Vector3f { - - public AIFloat3() { - super(0.0f, 0.0f, 0.0f); - } - public AIFloat3(float x, float y, float z) { - super(x, y, z); - } - public AIFloat3(float[] xyz) { - super(xyz); - } - public AIFloat3(AIFloat3 other) { - super(other); - } - public AIFloat3(Tuple3d tub3d) { - super(tub3d); - } - public AIFloat3(Tuple3f tub3f) { - super(tub3f); - } - public AIFloat3(Vector3d vec3d) { - super(vec3d); - } - public AIFloat3(Vector3f vec3f) { - super(vec3f); - } - public AIFloat3(Color color) { - - this.x = color.getRed() / 255.0F; - this.y = color.getGreen() / 255.0F; - this.z = color.getBlue() / 255.0F; - } - - public Color toColor() { - return new Color(x, y, z); - } - @Override - public String toString() { - return "(" + this.x + ", " + this.y + ", " + this.z + ")"; - } - public float[] toFloatArray() { - - float[] floatArr = new float[3]; - loadInto(floatArr); - return floatArr; - } - public void loadInto(float[] xyz) { - - xyz[0] = x; - xyz[1] = y; - xyz[2] = z; - } - - @Override - public int hashCode() { - - final int prime = 31; - int result = super.hashCode(); - result = prime * result + Float.floatToIntBits(x); - result = prime * result + Float.floatToIntBits(y); - result = prime * result + Float.floatToIntBits(z); - return result; - } - @Override - public boolean equals(Object obj) { - - if (this == obj) { - return true; - } else if (!super.equals(obj)) { - return false; - } else if (getClass() != obj.getClass()) { - return false; - } - - AIFloat3 other = (AIFloat3) obj; - if (Float.floatToIntBits(x) != Float.floatToIntBits(other.x)) { - return false; - } else if (Float.floatToIntBits(y) != Float.floatToIntBits(other.y)) { - return false; - } else if (Float.floatToIntBits(z) != Float.floatToIntBits(other.z)) { - return false; - } else { - return true; - } - } -} diff --git a/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/CallbackAIException.java b/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/CallbackAIException.java deleted file mode 100644 index 309663ef976..00000000000 --- a/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/CallbackAIException.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - Copyright (c) 2009 Robin Vobruba - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package com.springrts.ai.oo; - -/** - * An exception of this type may be thrown while from an AI callback method. - * - * @author hoijui - */ -public class CallbackAIException extends RuntimeException implements AIException { - - private String methodName; - private int errorNumber; - - public CallbackAIException(String methodName, int errorNumber) { - super("Error calling method \"" + methodName + "\": " + errorNumber); - - this.methodName = methodName; - this.errorNumber = errorNumber; - } - public CallbackAIException(String methodName, int errorNumber, Throwable cause) { - super("Error calling method \"" + methodName + "\": " + errorNumber, cause); - - this.methodName = methodName; - this.errorNumber = errorNumber; - } - - /** - * Returns the name of the method in which the exception occurred. - */ - public String getMethodName() { - return methodName; - } - - /** - * Returns the error number that will be sent to the engine, - * and consequently appear in the engines main log file. - * @return should be != 0, as this value is reserved for the no-error state - */ - @Override - public int getErrorNumber() { - return errorNumber; - } -} diff --git a/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/EventAIException.java b/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/EventAIException.java deleted file mode 100644 index 8486a33f23f..00000000000 --- a/AI/Wrappers/JavaOO/src/main/java/com/springrts/ai/oo/EventAIException.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright (c) 2009 Robin Vobruba - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -package com.springrts.ai.oo; - -/** - * An exception of this type may be thrown while handling an AI event. - * - * @author hoijui - * @version 0.1 - */ -public class EventAIException extends Exception implements AIException { - - public static final int DEFAULT_ERROR_NUMBER = 10; - - private int errorNumber; - - public EventAIException() { - super(); - - this.errorNumber = DEFAULT_ERROR_NUMBER; - } - public EventAIException(int errorNumber) { - super(); - - this.errorNumber = errorNumber; - } - - public EventAIException(String message) { - super(message); - - this.errorNumber = DEFAULT_ERROR_NUMBER; - } - public EventAIException(String message, int errorNumber) { - super(message); - - this.errorNumber = errorNumber; - } - - public EventAIException(String message, Throwable cause) { - super(message, cause); - - this.errorNumber = DEFAULT_ERROR_NUMBER; - } - public EventAIException(String message, Throwable cause, int errorNumber) { - super(message, cause); - - this.errorNumber = errorNumber; - } - - public EventAIException(Throwable cause) { - super(cause); - - this.errorNumber = DEFAULT_ERROR_NUMBER; - } - public EventAIException(Throwable cause, int errorNumber) { - super(cause); - - this.errorNumber = errorNumber; - } - - /** - * Returns the error number that will be sent to the engine, - * and consequently appear in the engines main log file. - * @return should be != 0, as this value is reserved for the no-error state - */ - @Override - public int getErrorNumber() { - return errorNumber; - } -} diff --git a/CMakeLists.txt b/CMakeLists.txt index d016d27287c..49e7b0acd8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,11 +73,6 @@ else() set(DEBUG_BUILD FALSE) endif() -if(DEBUG_BUILD) - set(JAVA_COMPILE_FLAG_CONDITIONAL "-g:lines,source,vars") -else() - set(JAVA_COMPILE_FLAG_CONDITIONAL "-g:lines,source") -endif() # By default, the libraries that don't explicitly specify SHARED/STATIC are build statically. # See https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html @@ -105,13 +100,12 @@ endif (APPLE) ### Compiler flags and defines based on build type include(TestCXXFlags) -## 32bit or 64bit? +## 64-bit architecture check set(MARCH_FLAG ${MARCH} CACHE STRING "CPU optimization (use `generic` for generic optimization)") -if (CMAKE_SIZEOF_VOID_P EQUAL 8) - set(MARCH_BITS 64 CACHE INTERNAL "" FORCE) -else (CMAKE_SIZEOF_VOID_P EQUAL 8) - message(FATAL_ERROR "RecoilEngine does not support 32 bits." ) -endif (CMAKE_SIZEOF_VOID_P EQUAL 8) +if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8) + message(FATAL_ERROR "RecoilEngine requires a 64-bit build environment.") +endif (NOT CMAKE_SIZEOF_VOID_P EQUAL 8) +set(MARCH_BITS 64 CACHE INTERNAL "" FORCE) # Detect architecture if (CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64|armv8|ARM64|AARCH64") @@ -124,9 +118,8 @@ else() message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}. Only ARM64 and x86-64 are supported.") endif() -message(STATUS "Building Spring on ${MARCH_BITS}bit environment") -set(BUILD_BITS "${MARCH_BITS}" CACHE STRING "Target arch machine type") -message(STATUS "Targetting ${BUILD_BITS}bit") +message(STATUS "Building Spring on a 64-bit environment") +set(BUILD_BITS 64 CACHE INTERNAL "Target architecture bitness" FORCE) ### Install paths (relative to CMAKE_INSTALL_PREFIX) @@ -211,7 +204,7 @@ endif (UNIX AND NOT MINGW) # (next two are relative to CMAKE_INSTALL_PREFIX) set(AI_LIBS_DIR "${DATADIR}" CACHE STRING "Where to install Skirmish AI libraries") set(AI_DATA_DIR "${AI_LIBS_DIR}" CACHE STRING "Where to install Skirmish AI additional files (eg. configuration)") -set(AI_TYPES "NATIVE" CACHE STRING "Which AI Interfaces (and Skirmish AIs using them) to build [ALL|NATIVE|JAVA|NONE]") +set(AI_TYPES "NATIVE" CACHE STRING "Which AI Interfaces (and Skirmish AIs using them) to build [ALL|NATIVE|NONE]") ## DataDirs set(BUILTIN_DATADIRS "") @@ -494,12 +487,7 @@ if (CMAKE_COMPILER_IS_GNUCXX) set(FALLBACK_SSE_FLAGS "${FALLBACK_SSE_FLAGS} -mno-avx -mno-fma -mno-fma4 -mno-xop -mno-lwp") set(FALLBACK_SSE_FLAGS "${FALLBACK_SSE_FLAGS} -mno-avx2") - if (MARCH_BITS EQUAL 64) - set(FALLBACK_MARCH "x86-64") - else (MARCH_BITS EQUAL 64) - set(FALLBACK_MARCH "i686") - endif (MARCH_BITS EQUAL 64) - + set(FALLBACK_MARCH "x86-64") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${FALLBACK_MARCH} -mtune=generic ${FALLBACK_SSE_FLAGS}") endif (NOT MARCH_FLAG STREQUAL "generic") @@ -586,22 +574,10 @@ else (MSVC) #set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ggdb3") #cmake has -g it by default #set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -ggdb3") #cmake has -g it by default - if (MARCH_BITS EQUAL 64 AND BUILD_BITS EQUAL 32) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32") - endif (MARCH_BITS EQUAL 64 AND BUILD_BITS EQUAL 32) if (MINGW) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++ -Wl,--enable-auto-import") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++ -Wl,--enable-auto-import") - if (BUILD_BITS EQUAL 32) - # Increase memory limit from 2GB to 3GB on 32bit Windows and 2GB->4GB on 64bit Windows (assuming spring.exe is 32bit) - # http://msdn.microsoft.com/en-us/library/windows/desktop/aa366778(v=vs.85).aspx - message(STATUS "Enable IMAGE_FILE_LARGE_ADDRESS_AWARE (>2GB memory limit)") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--large-address-aware") - else (BUILD_BITS EQUAL 32) - message(WARNING "The 64bit version of spring on windows is experimental and may not sync with regular builds") - endif (BUILD_BITS EQUAL 32) endif (MINGW) endif (MSVC) @@ -720,8 +696,11 @@ add_subdirectory(rts) # Unit tests # this has to be in root CMakeLists.txt -enable_testing() -add_subdirectory(test) +option(BUILD_TESTING "Build engine tests" ON) +if (BUILD_TESTING) + enable_testing() + add_subdirectory(test) +endif () if (INSTALL_PORTABLE) install(DIRECTORY DESTINATION games) diff --git a/coding-agents/BACKWARDS_COMPATIBILITY.md b/coding-agents/BACKWARDS_COMPATIBILITY.md new file mode 100644 index 00000000000..d36c3775a78 --- /dev/null +++ b/coding-agents/BACKWARDS_COMPATIBILITY.md @@ -0,0 +1,25 @@ +# Backwards compatibility + +Recoil isn't 100% beholden to backwards compatibility, but breaking changes are weighed carefully against their benefit. Backwards compatiblity is a constraint, not a veto. + +## Why the bar is high + +Recoil games aren't short-cycle Unreal/Unity titles — they're lifetime hobby projects. There is no steady stream of new games picking up the latest engine; the games we have are the games we have. They fall into two camps, and neither absorbs churn well: + +- **Mature games** need stability above all else. +- **Games still in active development** have the flexibility, but rarely the volunteer bandwidth to chase significant engine breakage. + +## How to weigh a change + +- Quantify the benefit (perf, correctness, maintainability) concretely, not in the abstract. +- Identify which games or content would break, and how mechanical the fix is on their side. "Rename a call site" is very different from "rearchitect your gadget." +- Prefer changes whose blast radius is contained or whose adaptation is mechanical. Avoid changes that force games to rethink core logic with no real mitigation path. + +## Precedents + +- **Multi-threaded unit movement & collision** — landed with a large perf win and effectively no game-side impact (ignoring incidentally-fixed bugs). This is the shape of change to look for. +- **Multi-threading `Unit::Update`, `Unit::SlowUpdate`, or projectiles** — don't. The impact on games would be huge and there isn't much that can be done to mitigate it. Not a path worth proposing. + +## The upshot + +Backwards compatiblity constraints don't close the door on performance work — they just point it at the areas where the blast radius is small. Plenty of wins are still on the table; pick the ones games don't have to pay for. diff --git a/coding-agents/ENGINE_PERFORMANCE.md b/coding-agents/ENGINE_PERFORMANCE.md new file mode 100644 index 00000000000..f3bb67f5d77 --- /dev/null +++ b/coding-agents/ENGINE_PERFORMANCE.md @@ -0,0 +1,55 @@ +# Engine performance + +Recoil is an RTS engine built for large-scale games — designed to handle thousands of units at once. + +## Scale target + +- **Target:** ~10k concurrent units, *including buildings*. +- Mobile units tend to be ~40% of that late-game total. +- Largest seen in a real game: ~17.7k units. That's a data point, not a design target. + +## Sim, draw, and update frames + +The main loop is `Update → Draw`, repeating — see the diagram and table below for the per-phase breakdown. Each iteration first drains any queued sim-frame packets (0..N per iteration), then renders one draw frame. `CGame::Update` dispatches `SimFrame()` calls as `NETMSG_NEWFRAME` packets arrive; `CGame::Draw` runs the unsynced update phase and then renders. The sim burst is capped at ~500 ms (`minDrawFPS`) so draw always gets to run, and it's all one thread — sim and rendering are **not concurrent**; parallelism only happens *inside* a phase. + +Conversely, if no sim frames are in the queue the main loop runs `Draw`/`UpdateUnsynced` as fast as possible — many draw iterations can pass between successive sim frames, with visuals interpolating smoothly in between via `globalRendering->timeOffset`. + +``` +main-loop iteration (repeats as fast as possible) +├── CGame::Update (mostly synced) +│ └── SimFrame × 0..N ← processes queued sim frames capped at +| ~500ms per iteration +└── CGame::Draw (unsynced) + ├── UpdateUnsynced ← unsynced update phase + └── render world + screen ← Draw::World + Draw::Screen +``` + +| Phase | Rate | Synced? | Responsibility | +|---|---|---|---| +| **Sim frame** — `CGame::SimFrame` | fixed 30 Hz (`GAME_SPEED`) | mostly yes | advance deterministic state: units, pathing, projectiles, line-of-sight, scripts, Lua `GameFrame` | +| **Draw frame** — `CGame::Draw` | variable | no | update phase (see below) + render world/screen | +| **Update phase** — `CGame::UpdateUnsynced` *(inside draw frame)* | per draw frame | no | timings, interpolation, camera, GUI, sound, world-drawer prep | + +### Profiler buckets + +The engine `CTimeProfiler` (and the `benchmark` tool) report three peer buckets: `Sim` (the whole synced step), `Update` (`CGame::UpdateUnsynced`), and `Draw` (rendering only, *excluding* the Update that runs first). + +`Sim` is **"mostly" synced**: it also bills unsynced work that runs inline during `SimFrame`. +- **Explicit Lua callins** — `GameFrame`/`GameFramePost` run near the start of each sim frame. +- **Event-driven Lua callins** — unsynced widgets can subscribe to synced game events, so their handlers run inline as those events fire during the frame. +- **C++-only unsynced sections** — e.g. the MT projectile visual pass (`Sim::Projectiles::UpdateUnsyncedMT`) and ghosted-building updates (`CUnitDrawer::UpdateGhostedBuildings`). + +### Scheduling and CPU budget + +- Sim has a target rate set by the server; draw is as fast as the hardware allows. The sim target is `30 Hz × speedFactor`; at a speed factor of 1x, in-game time tracks real-world time 1:1, and at 2x speed the server fires twice as many sim frames per real-world second so the world evolves twice as fast. +- **Zero, one, or many** sim frames per draw frame — if the client falls behind, pending sim frames burst in the next iteration to catch up. +- Visuals interpolate between sim frames, so draw rate can exceed sim rate without stutter. +- Sim time is carefully budgeted and scheduled against draw frames (because they run serially) so there's always a minimum fps for the player + +## Multi-threading + +The engine runs one **main thread** plus a pool of **worker threads**, all pinned to distinct cores. We typically aim for 6-8 worker threads. The main thread drives the sim/draw loop; workers pick up parallel work dispatched from the main thread (via `for_mt` and friends in `rts/System/Threading/ThreadPool.h`). The main thread also participates in draining the task queue while it waits. + +Most parallel work in the engine is **homogeneous** — the same operation applied over many items (unit updates, projectile steps, etc.) via `for_mt`. Keeping parallel work homogeneous is a deliberate discipline: it makes determinism easier to reason about and keeps sim output independent of how work happens to land across threads. + +**QTPFS is the one heterogeneous exception.** The quad-tree pathfinder maintains its own per-worker search state (`SearchThreadData`, `SparseData`) independent of engine sim state, which lets it safely run path searches on the worker pool *in the background* via `for_mt_background`. Background tasks yield to higher-priority work by rescheduling themselves when other jobs arrive, so QTPFS soaks up idle worker capacity without preempting foreground parallelism. diff --git a/cont/LuaUI/debug.lua b/cont/LuaUI/debug.lua index 26cb771850d..f938a8d9889 100644 --- a/cont/LuaUI/debug.lua +++ b/cont/LuaUI/debug.lua @@ -313,8 +313,6 @@ function Debug() print("Game.mapSizeZ = " .. Game.mapSizeZ) print("Game.mapName = " .. Game.mapName) print("Game.modName = " .. Game.modName) - print("Game.limitDGun = " .. tostring(Game.limitDGun)) - print("Game.Game.diminishingMetal = " .. tostring(Game.diminishingMetal)) PrintAllyTeamList() PrintTeamList() diff --git a/cont/base/springcontent/CMakeLists.txt b/cont/base/springcontent/CMakeLists.txt index 95e5769f31e..01509b86128 100644 --- a/cont/base/springcontent/CMakeLists.txt +++ b/cont/base/springcontent/CMakeLists.txt @@ -101,7 +101,6 @@ list(APPEND FILES LuaGadgets/Gadgets/share_levels.lua LuaGadgets/Gadgets/cmd_nocost.lua LuaGadgets/Gadgets/game_end.lua - LuaGadgets/Gadgets/share_delayed.lua LuaGadgets/Gadgets/README.txt LuaGadgets/Gadgets/share_no_builders.lua LuaGadgets/Gadgets/object_statusbars_default.lua @@ -109,7 +108,6 @@ list(APPEND FILES LuaGadgets/Gadgets/share_control.lua LuaGadgets/Gadgets/unit_script.lua LuaGadgets/Gadgets/game_spawn.lua - LuaGadgets/Gadgets/unit_limit_dgun.lua LuaGadgets/system.lua LuaGadgets/actions.lua LuaGadgets/README.txt diff --git a/cont/base/springcontent/EngineOptions.lua b/cont/base/springcontent/EngineOptions.lua index f1b0bfc0de8..23dd42ce902 100644 --- a/cont/base/springcontent/EngineOptions.lua +++ b/cont/base/springcontent/EngineOptions.lua @@ -38,15 +38,6 @@ local options = step = 1, -- quantization is aligned to the def value -- (step <= 0) means that there is no quantization }, - - { - key = 'LimitDgun', - name = 'Limit D-Gun range', - desc = "The commander's D-Gun weapon will be usable only close to the player's starting location", - type = 'bool', - def = false, - }, - { key = 'GhostedBuildings', name = 'Ghosted buildings', diff --git a/cont/base/springcontent/LuaGadgets/Gadgets/README.txt b/cont/base/springcontent/LuaGadgets/Gadgets/README.txt index c859ac704bb..1fa49aa2a23 100644 --- a/cont/base/springcontent/LuaGadgets/Gadgets/README.txt +++ b/cont/base/springcontent/LuaGadgets/Gadgets/README.txt @@ -9,7 +9,6 @@ LuaRules * cmd_nocost.lua * share_control.lua -* share_delayed.lua * share_levels.lua * share_no_builders.lua diff --git a/cont/base/springcontent/LuaGadgets/Gadgets/share_delayed.lua b/cont/base/springcontent/LuaGadgets/Gadgets/share_delayed.lua deleted file mode 100644 index f19b841d48f..00000000000 --- a/cont/base/springcontent/LuaGadgets/Gadgets/share_delayed.lua +++ /dev/null @@ -1,402 +0,0 @@ --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- --- --- file: share_delayed.lua --- brief: delay unit sharing --- author: Dave Rodgers --- --- Copyright (C) 2007. --- Licensed under the terms of the GNU GPL, v2 or later. --- --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -function gadget:GetInfo() - return { - name = "SharingDelayed", - desc = "delayed unit sharing", - author = "trepan", - date = "Apr 22, 2007", - license = "GNU GPL, v2 or later", - layer = -4, - enabled = true -- loaded by default? - } -end - --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- --- --- FIXME: (TODO) --- - Delayed resource sharing --- - Visual indicators for units queued to be shared --- - Update unit share times for fallen comrades --- --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - --- Only active in comm-ends games - -local gameMode = Game.gameMode or 4 - -if (gameMode ~= 1) then - return false -end - - --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- --- --- Proposed Command ID Ranges: --- --- all negative: Engine (build commands) --- 0 - 999: Engine --- 1000 - 9999: Group AI --- 10000 - 19999: LuaUI --- 20000 - 29999: LuaCob --- 30000 - 39999: LuaRules --- - -local CMD_CANCEL_SHARE = 33999 - - --------------------------------------------------------------------------------- --- COMMON --------------------------------------------------------------------------------- -if (gadgetHandler:IsSyncedCode()) then --------------------------------------------------------------------------------- --- SYNCED --------------------------------------------------------------------------------- - -local teams = {} -- teamID = { unitID = shareInfo } -local shares = {} -- unitID = oldTeam -local frames = {} -- unitID = framee - -local enabled = true - -local minDelay = 1 -- minimum delay between shares -local costScale = 0.1 -- add extra cycles based on unit costs - -local cancelShareCmdDesc = { - id = CMD_CANCEL_SHARE, - type = CMDTYPE.ICON, - name = '\255\255\100\100NoShare', - tooltip = 'Cancel the unit transfer', - action = 'cancel_share', -} - - --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -local function AllowAction(playerID) - if (playerID ~= 0) then - Spring.SendMessageToPlayer(playerID, "Must be the host player") - return false - end - if (not Spring.IsCheatingEnabled()) then - Spring.SendMessageToPlayer(playerID, "Cheating must be enabled") - return false - end - return true -end - - -local function ChatControl(cmd, line, words, playerID) - if (not AllowAction(playerID)) then - Spring.Echo('delayed sharing is ' .. (enabled and 'enabled' or 'disabled')) - return true - end - if (#words == 0) then - enabled = not enabled - else - enabled = (words[1] == '1') - end - Spring.Echo('delayed sharing is ' .. (enabled and 'enabled' or 'disabled')) - return true -end - - -local function StopShare(cmd, line, words, playerID) - local _,_,_,teamID = Spring.GetPlayerInfo(playerID) - local team = teamID and teams[teamID] or nil - if (team) then - for _,data in pairs(team) do - shares[data.unitID] = nil - frames[data.unitID] = nil - end - teams[teamID] = nil - Spring.Echo('cancelled remaining unit transfers') - else - Spring.Echo('there are no unit transfers to cancel') - end - return true -end - - --------------------------------------------------------------------------------- - -function gadget:Initialize() - gadgetHandler:RegisterCMDID(CMD_CANCEL_SHARE) - _G.shareFrames = frames - - local cmd, help - - cmd = "sharedelay" - help = " [0|1]: delayed unit sharing, useful for comm ends games" - gadgetHandler:AddChatAction(cmd, ChatControl, help) - Script.AddActionFallback(cmd .. ' ', help) - - cmd = "stopshare" - help = ": cancel all queued unit transfers for your team" - gadgetHandler:AddChatAction(cmd, StopShare, help) - Script.AddActionFallback(cmd, help) -end - - -function gadget:Shutdown() - gadgetHandler:RemoveChatAction("sharedelay") - Script.RemoveActionFallback("sharedelay") - - gadgetHandler:RemoveChatAction("stopshare") - Script.RemoveActionFallback("stopshare") - - for _,unitID in ipairs(Spring.GetAllUnits()) do - local cmdDescID = Spring.FindUnitCmdDesc(unitID, CMD_CANCEL_SHARE) - if (cmdDescID) then - Spring.RemoveUnitCmdDesc(unitID, cmdDescID) - end - end -end - - --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -local insert = table.insert -local remove = table.remove - - -local function InsertShare(unitID, oldTeam, newTeam, delay) - if (shares[unitID]) then - return -- already active - end - - local shareInfo = { - unitID = unitID, - oldTeam = oldTeam, - newTeam = newTeam, - delay = delay, - } - local team = teams[oldTeam] - - if (team) then - print(team[#team].frame, delay) - shareInfo.frame = team[#team].frame + delay - else - team = {} - teams[oldTeam] = team - shareInfo.frame = Spring.GetGameFrame() + delay - end - - insert(team, shareInfo) - shares[unitID] = oldTeam - frames[unitID] = shareInfo.frame - - Spring.InsertUnitCmdDesc(unitID, 1, cancelShareCmdDesc) -end - - --------------------------------------------------------------------------------- - -local function RecalcDelays(team) - for i = 2, #team do - team[i].frame = team[i - 1].frame + team[i].delay - frames[team[i].unitID] = team[i].frame - end -end - - -local function RemoveShare(unitID) - local oldTeam = shares[unitID] - shares[unitID] = nil - frames[unitID] = nil - - local cmdDescID = Spring.FindUnitCmdDesc(unitID, CMD_CANCEL_SHARE) - if (cmdDescID) then - Spring.RemoveUnitCmdDesc(unitID, cmdDescID) - end - - local team = teams[oldTeam] - if ((oldTeam == nil) or (team == nil)) then - return -- not active - end - - local index - for i, shareInfo in ipairs(team) do - if (shareInfo.unitID == unitID) then - index = i - break - end - end - if (index == nil) then - return -- something is amiss - end - - local shareInfo = team[index] - remove(team, index) - - if (#team <= 0) then - teams[oldTeam] = nil - else - if (index ~= 1) then - RecalcDelays(team) - else - local nowFrame = Spring.GetGameFrame() - if (shareInfo.frame > nowFrame) then - local front = team[1] - front.frame = nowFrame + front.delay - frames[front.unitID] = front.frame - RecalcDelays(team) - end - end - end -end - - --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -function gadget:AllowUnitTransfer(unitID, unitDefID, oldTeam, newTeam, capture) - if (capture) then - return true - end - if (not enabled) then - return true - end - - local ud = UnitDefs[unitDefID] - if (not ud) then - return true -- something is borked - end - - -- compute the share delay - local cost = ud.metalCost + (ud.energyCost / 60) - local costDelay = math.floor(cost * costScale) - local shareDelay = minDelay + costDelay - - local team = teams[oldTeam] - if ((team == nil) and (shareDelay <= 0)) then - return true -- share the unit immediately - end - - InsertShare(unitID, oldTeam, newTeam, shareDelay) - - return false -end - - --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -function gadget:GameFrame(frameNum) - for _,team in pairs(teams) do - while (team and (#team > 0)) do - local front = team[1] - if (front.frame > frameNum) then - break -- front is not yet ready to be shared - end - - local curTeam = Spring.GetUnitTeam(front.unitID) - if (curTeam and (curTeam == front.oldTeam)) then - -- FIXME: see if newTeam is alive - local tmp = AllowUnitTransfer - AllowUnitTransfer = function() return true end - Spring.TransferUnit(front.unitID, front.newTeam) - AllowUnitTransfer = tmp - end - - RemoveShare(front.unitID) - end - end -end - - --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -function gadget:AllowCommand(unitID, unitDefID, unitTeam, - cmdID, cmdParams, cmdOptions) - if (cmdID == CMD_CANCEL_SHARE) then - RemoveShare(unitID) - return false - end - return true -end - - --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -function gadget:UnitDestroyed(unitID, unitDefID, unitTeam) - RemoveShare(unitID) -end - - --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -function gadget:UnitTaken(unitID) - RemoveShare(unitID) -end - - --------------------------------------------------------------------------------- --- SYNCED --------------------------------------------------------------------------------- -else --------------------------------------------------------------------------------- --- UNSYNCED --------------------------------------------------------------------------------- - -function gadget:Initialize() -end - - -function gadget:Shutdown() -end - - -local GetGameFrame = Spring.GetGameFrame -local GetUnitPosition = Spring.GetUnitPosition -local GetUnitAllyTeam = Spring.GetUnitAllyTeam -local GetLocalAllyTeamID = Spring.GetLocalAllyTeamID -local AddWorldIcon = Spring.AddWorldIcon -local AddWorldText = Spring.AddWorldText - -function gadget:DrawWorld() - local frames = SYNCED.shareFrames - if ((frames == nil) or (snext(frames) == nil)) then - return - end - local nowFrame = GetGameFrame() - local myAllyTeam = GetLocalAllyTeamID() - for unitID, frame in spairs(frames) do - if (GetUnitAllyTeam(unitID) == myAllyTeam) then - local x, y, z = GetUnitPosition(unitID) - if (x) then - local str = string.format('%.1f', (frame - nowFrame) / Game.gameSpeed) - AddWorldIcon(x, y, z, CMD.STOP) - AddWorldText(str, x, y, z) - end - end - end -end - - --------------------------------------------------------------------------------- --- UNSYNCED --------------------------------------------------------------------------------- -end --------------------------------------------------------------------------------- --- COMMON --------------------------------------------------------------------------------- diff --git a/cont/base/springcontent/LuaGadgets/Gadgets/unit_limit_dgun.lua b/cont/base/springcontent/LuaGadgets/Gadgets/unit_limit_dgun.lua deleted file mode 100644 index e5bcf08e67e..00000000000 --- a/cont/base/springcontent/LuaGadgets/Gadgets/unit_limit_dgun.lua +++ /dev/null @@ -1,155 +0,0 @@ --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- --- --- file: unit_dgun_limit.lua --- brief: Re-implements limit dgun in Lua --- author: Andrea Piras --- --- Copyright (C) 2010. --- Licensed under the terms of the GNU GPL, v2 or later. --- --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -function gadget:GetInfo() - return { - name = "Limit Dgun", - desc = "Re-implements limit dgun in Lua", - author = "Andrea Piras", - date = "August, 2010", - license = "GNU GPL, v2 or later", - layer = 0, - enabled = true -- loaded by default? - } -end - --------------------------------------------------------------------------------- --------------------------------------------------------------------------------- - -local enabled = tonumber(Spring.GetModOptions().limitdgun) or 0 -if (enabled == 0) then - return false -end - -local dgunMapFraction = 3 -- use the modoption to define it instead? -local dgunRadiusSquared = (Game.mapSizeX*Game.mapSizeZ) / (dgunMapFraction*dgunMapFraction) -local dgunRadius = math.sqrt(dgunRadiusSquared) - -if (not gadgetHandler:IsSyncedCode()) then --begin unsynced section - -local glColor = gl.Color -local glDrawGroundCircle = gl.DrawGroundCircle -local glPushMatrix = gl.PushMatrix -local glPopMatrix = gl.PopMatrix -local glDeleteList = gl.DeleteList -local glCreateList = gl.CreateList -local glCallList = gl.CallList - -local GetSpectatingState = Spring.GetSpectatingState -local GetTeamStartPosition = Spring.GetTeamStartPosition -local GetTeamList = Spring.GetTeamList -local GetTeamStartPosition = Spring.GetTeamStartPosition -local AreTeamsAllied = Spring.AreTeamsAllied -local GetGaiaTeamID = Spring.GetGaiaTeamID -local GetMyTeamID = Spring.GetMyTeamID -local GetTeamInfo = Spring.GetTeamInfo - -local allyTeamVec = Spring.GetAllyTeamList() -local myTeamID = GetMyTeamID() -local gaiaTeamID = GetGaiaTeamID() -local circlesList - -function ReGenerateDisplayList() - if circlesList then - glDeleteList(circlesList) - end - circlesList = glCreateList( function() - for allyIndex,allyTeamID in ipairs(allyTeamVec) do - local teamList = GetTeamList(allyTeamID) - for _,teamID in ipairs(teamList) do - local _,_,isDead = GetTeamInfo(teamID) - if not isDead and teamID ~= gaiaTeamID then - if teamID == myTeamID then - glColor(0.0, 0.0, 1.0, 0.6) -- use blue circles for your range - elseif AreTeamsAllied( teamID, myTeamID ) == true then -- order matters! this tells if he's allied with us, not vice-versa - glColor(1.0, 1.0, 1.0, 0.6) -- use white circles for ally ranges - else - glColor(1.0, 1.0, 0.0, 0.6) -- use yellow circles for enemy ranges - end - local teamX,teamY,teamZ = GetTeamStartPosition(teamID) - if teamX and teamY and teamZ then - glDrawGroundCircle(teamX,teamY,teamZ, dgunRadius, 40) - end - end - end - end - end) -end - -function gadget:PlayerChanged() -- regenerate the display list, update even if it's not for ourself, so we can rid of unnecessary team circles - ReGenerateDisplayList() -end - -function gadget:Initialize() - ReGenerateDisplayList() -end - -function gadget:Shutdown() - glDeleteList(circlesList) -end - -function gadget:DrawWorld() -- paint dgun limit preview range circle - if circlesList then - glPushMatrix() - glCallList(circlesList) - glPopMatrix() - end -end - -else -- begin synced section - -local GetTeamStartPosition = Spring.GetTeamStartPosition -local GetUnitPosition = Spring.GetUnitPosition -local CMD_MANUALFIRE = CMD.MANUALFIRE - -local teamStartPosVec = {} -- cache for team starting pos - --- squared distance between 2 points in 3D cartesian space -function SqDistance(one, two) - dx = one[1] - two[1] - dy = one[2] - two[2] - dz = one[3] - two[3] - return dx*dx + dy*dy + dz*dz -end - -function gadget:AllowCommand(unitID, unitDefID, teamID, cmdID, cmdParams, cmdOptions, cmdTag, synced) - if cmdID ~= CMD_MANUALFIRE then -- pass all non-dgun commands - return true - end - - if not teamStartPosVec[teamID] then -- build cache if not available - local teamX,teamY,teamZ = GetTeamStartPosition(teamID) - if teamX and teamY and teamZ then - teamStartPosVec[teamID] = {teamX,teamY,teamZ} - end - end - local teamStartPos = teamStartPosVec[teamID] - if not teamStartPos then -- wtf - return true - end - - local unitX, unitY, unitZ = GetUnitPosition(unitID) - if not unitX or not unitY or not unitZ then -- wtf - return true - end - local unitPos = {unitX,unitY,unitZ} - - -- restrict the command to the allowed area - if SqDistance(teamStartPos,unitPos) > dgunRadiusSquared then -- unit outside of allowed range, block the command - return false - end - - return true -end - -end -- end synced section diff --git a/cont/base/springcontent/gamedata/unit_script_header.lua b/cont/base/springcontent/gamedata/unit_script_header.lua index 7e83408b754..6453110c23b 100644 --- a/cont/base/springcontent/gamedata/unit_script_header.lua +++ b/cont/base/springcontent/gamedata/unit_script_header.lua @@ -30,6 +30,7 @@ local MultiMove = UnitScript.MultiMove local MultiTurn = UnitScript.MultiTurn local MultiSpin = UnitScript.MultiSpin local MultiStopSpin = UnitScript.MultiStopSpin +local MultiScale = UnitScript.MultiScale local MultiExplode = UnitScript.MultiExplode local StartThread = UnitScript.StartThread @@ -38,6 +39,7 @@ local SetSignalMask = UnitScript.SetSignalMask local Sleep = UnitScript.Sleep local WaitForMove = UnitScript.WaitForMove local WaitForTurn = UnitScript.WaitForTurn +local WaitForScale = UnitScript.WaitForScale local x_axis = 1 local y_axis = 2 diff --git a/cont/base/springcontent/mapgenerator/mapinfo_template.lua b/cont/base/springcontent/mapgenerator/mapinfo_template.lua index 67afee82538..eda662b6833 100644 --- a/cont/base/springcontent/mapgenerator/mapinfo_template.lua +++ b/cont/base/springcontent/mapgenerator/mapinfo_template.lua @@ -100,16 +100,21 @@ local mapinfo = { --grassShadingTex = "", --detailTex = "", --specularTex = "", - --splatDetailTex = "", - --splatDistrTex = "", + splatDetailTex = ${SPLAT_DETAIL_TEX}, + splatDistrTex = ${SPLAT_DISTR_TEX}, + splatDetailNormalTex1 = ${SPLAT_DETAIL_NORMAL_TEX_1}, + splatDetailNormalTex2 = ${SPLAT_DETAIL_NORMAL_TEX_2}, + splatDetailNormalTex3 = ${SPLAT_DETAIL_NORMAL_TEX_3}, + splatDetailNormalTex4 = ${SPLAT_DETAIL_NORMAL_TEX_4}, + splatDetailNormalDiffuseAlpha = ${SPLAT_DETAIL_NORMAL_DIFFUSE_ALPHA}, --skyReflectModTex = "", --detailNormalTex = "", --lightEmissionTex = "", }, splats = { - texScales = {0.02, 0.02, 0.02, 0.02}, - texMults = {1.0, 1.0, 1.0, 1.0}, + texScales = {${SPLAT_TEXSCALES}}, + texMults = {${SPLAT_TEXMULTS}}, }, atmosphere = { diff --git a/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl b/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl index 468d850874e..aa05d764667 100644 --- a/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl +++ b/cont/base/springcontent/shaders/GLSL/ModelVertProgGL4.glsl @@ -262,7 +262,7 @@ Transform Lerp(Transform t0, Transform t1, float a) { void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) { - bool staticModel = (matrixMode > 0); + bool staticModel = (matrixMode == MATMODE_STATIC || matrixMode == MATMODE_ARRAY); vec4 piecePos = vec4(pos, 1.0); vec4 normal4 = vec4(normal, 0.0); @@ -271,7 +271,10 @@ void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) Transform tx; if (staticModel) { - tx = transforms[instData.x + bID0]; + // pieces always come from the bind-pose block (instData.w). In ARRAY_MATMODE + // instData.x is the per-instance world transform, not the bind pose; for static + // model submits instData.x == instData.w anyway, so instData.w is correct for both. + tx = transforms[instData.w + bID0]; } else { // do interpolation tx = Lerp( @@ -327,16 +330,20 @@ void GetModelSpaceVertex(out vec4 msPosition, out vec3 msNormal) void main(void) { - bool staticModel = (matrixMode > 0); - vec4 modelPos; vec3 modelNormal; GetModelSpaceVertex(modelPos, modelNormal); - if (staticModel) { + if (matrixMode == MATMODE_ARRAY) { + // static instanced: per-instance world transform read from the SSBO (no interpolation) + Transform wtx = transforms[instData.x + 0u]; + worldPos = ApplyTransform(wtx, modelPos); + wtx.trSc = vec4(0, 0, 0, 1); //nullify the translation part for the normal + worldNormal = ApplyTransform(wtx, modelNormal); + } else if (matrixMode == MATMODE_STATIC) { worldPos = staticModelMatrix * modelPos; worldNormal = mat3(staticModelMatrix) * modelNormal; - } else { + } else { // MATMODE_NORMAL // do interpolation Transform tx = Lerp( transforms[instData.x + 0u], diff --git a/cont/examples/Widgets/gui_buildsquare_gl4.lua b/cont/examples/Widgets/gui_buildsquare_gl4.lua index 6a6c7a35440..2456f9d87c7 100644 --- a/cont/examples/Widgets/gui_buildsquare_gl4.lua +++ b/cont/examples/Widgets/gui_buildsquare_gl4.lua @@ -140,16 +140,7 @@ flat in float v_status; out vec4 fragColor; void main() { - float pulse = 0.85 + 0.15 * sin(timeInfo.x * 4.0); - - vec3 col = v_color.rgb * pulse; - float alpha = v_color.a * pulse; - - if (v_status < 0.5) { - col = mix(col, vec3(1.0, 0.2, 0.2), 0.3 * sin(timeInfo.x * 8.0 + 1.0)); - } - - fragColor = vec4(col, alpha); + fragColor = v_color; } ]] diff --git a/doc/StartScriptFormat.txt b/doc/StartScriptFormat.txt index f7ce0a0c8e1..9c13689a6e4 100644 --- a/doc/StartScriptFormat.txt +++ b/doc/StartScriptFormat.txt @@ -137,8 +137,6 @@ StartMetal=1000; StartEnergy=1000; MaxUnits=500; // per team - GameMode=x; // 0 cmdr dead->game continues, 1 cmdr dead->game ends, 2 lineage, 3 openend - LimitDGun=0; // limit dgun to fixed radius around startpos? DisableMapDamage=0; // disable map craters? GhostedBuildings=1; // ghost enemy buildings after losing los on them NoHelperAIs=0; // are GroupAIs and other helper AIs allowed? diff --git a/doc/pr-changelogs/3217.md b/doc/pr-changelogs/3217.md new file mode 100644 index 00000000000..b5ba544c9af --- /dev/null +++ b/doc/pr-changelogs/3217.md @@ -0,0 +1 @@ + * `/skip` now works if called before game start diff --git a/doc/site/content/_index.md b/doc/site/content/_index.md index fcbbe83428f..0d0a8d3b14b 100644 --- a/doc/site/content/_index.md +++ b/doc/site/content/_index.md @@ -19,7 +19,7 @@ draft = false {{< /cards >}} {{< cards >}} {{< card title="Tech Annihilation" image="/showcase/ta.jpeg" link="https://github.com/techannihilation/TA" >}} -{{< card title="SplinterFaction" image="showcase/splinter_faction.jpg" link="splinterfaction.info" >}} +{{< card title="SplinterFaction" image="showcase/splinter_faction.jpg" link="https://splinterfaction.info" >}} {{< card title="Mechcommander: Legacy" image="/showcase/mcl.jpg" link="https://github.com/SpringMCLegacy/SpringMCLegacy/wiki" >}} {{< /cards >}} diff --git a/doc/site/content/articles/start-script-format.md b/doc/site/content/articles/start-script-format.md index 75222119e85..df2300799df 100644 --- a/doc/site/content/articles/start-script-format.md +++ b/doc/site/content/articles/start-script-format.md @@ -143,8 +143,6 @@ draft = false StartMetal=1000; StartEnergy=1000; MaxUnits=500; // per team - GameMode=x; // 0 cmdr dead->game continues, 1 cmdr dead->game ends, 2 lineage, 3 openend - LimitDGun=0; // limit dgun to fixed radius around startpos? DisableMapDamage=0; // disable map craters? GhostedBuildings=1; // ghost enemy buildings after losing los on them NoHelperAIs=0; // are GroupAIs and other helper AIs allowed? diff --git a/doc/site/content/articles/team-terminology.markdown b/doc/site/content/articles/team-terminology.markdown index 1108f8610b3..40b71271535 100644 --- a/doc/site/content/articles/team-terminology.markdown +++ b/doc/site/content/articles/team-terminology.markdown @@ -70,7 +70,7 @@ A "skirmish" AI is hosted by one of the players and generally acts very similar It can read the game state via AI interface and works by giving units commands. Strictly speaking, it is their hosting player relaying commands - this means that this type of AI is subject to lag and will drop if the hosting player quits. On the other hand, only the host player is taking on the burden of simulating the AI. -There are currently Skirmish AI bindings for C and Java (though distributing the Java runtime environment for a Java skirmish AI is up to the game). +There are currently Skirmish AI bindings available in C and C++. A game does not need explicit support for this kind of AI (meaning for example, somebody can homebrew one), though it will likely want to handle distribution and infrastructure issues (for example to block homebrew AI). A Lua AI generally has two components: a piece of game mechanics, and the AI instance itself which is just a handle to tell game mechanics which teams are legal to control. diff --git a/doc/site/content/changelogs/_index.markdown b/doc/site/content/changelogs/_index.markdown index 55096582c3a..d302589cf06 100644 --- a/doc/site/content/changelogs/_index.markdown +++ b/doc/site/content/changelogs/_index.markdown @@ -5,79 +5,6 @@ title = "Running changelog" type = "docs" +++ -This is the bleeding-edge changelog since version 2025.06, for **pre-release 2026.06**. +This is the bleeding-edge changelog since version 2026.07, for **pre-release 2026.08**. -## Caveats - -- UTF-8 file paths are now supported. -- some file accesses are now case-sensitive. -- rmlUI version used 6.0 → 6.2 -- ARM64 architecture builds now have nominal support. -- `/aicontrol` is now blocked by default. Call `/aiCtrl PlayerName` or `/aiCtrlByNum 123` to enable. -- `script:AimWeapon` now receives unit-relative heading and pitch, rather than world-space. This means units angled on slopes will receive different values. -- heading cast to radians will now return \[-pi; +pi) rather than \[0; tau). -- minor Lua env sandboxing changes, see the "Lua environment sandboxing" section below. -- always output logs to stdout. -- removed `CSphereParticleSpawner` particle class. Identical to `CSimpleParticleSystem`. -- archive cache version 20 → 21. - -## Features - -### RmlUi - -- rmlUI version used 6.0 → 6.2 -- add datamodel support for pairs: `pairs(dm_handle)` -- add datamodel support for ipairs: `dm_handle:__ipairs()` -- support for accessing the underlying datamodel table with `dm_handle.__raw()` -- allow datamodel self-referential assignments such as `dm_handle.property = dm_handle.another_property` -- support for retrieving datamodel property length: `dm_handle.property.__len()` -- fix datamodel array access -- fix `data-value` binds in rml elements -- added `RmlUi.GetDocumentPathRequests(string docPath) -> {"filePath", "filePath", ...}` which tracks all of the files opened by an RmlUi LoadDocument call -- added `RmlUi.ClearDocumentPathRequests(string docPath) -> nil` to clear tracked LoadDocument files - -### Radar icon Lua API -- added `Spring.SetUnitIcon(unitID, string? iconName)`. Pass `nil` to reset to default. -- added `Spring.GetUnitIcon(unitID) → string iconName`. - -### Minimap callins -- added `wupget:MiniMapRotationChanged(rotation, previousRotation)` unsynced callin. In radians. -- added `wupget:MiniMapGeometryChanged(x, y, sizeX, sizeY, prevX, prevY, prevSizeX, prevSizeY)` unsynced callin. In pixels. -- added `wupget:MiniMapStateChanged(isMinimized, isMaximized, isSlaved)` unsynced callin. - -### Lua environment sandboxing -- LuaSocket no longer inits before Lua sandboxing. -- unsynced LuaRules (incl. unsynced LuaGaia) now has access to `io` and `os` libraries. -- unsynced LuaRules (incl. unsynced LuaGaia) now has access to the `debug` library by default (no longer requires devmode). - -### Replay path getters -- add `Spring.GetReplayFilePath() → string?`, returns path of replay being watched. -- add `Spring.GetReplayRecordingFilePath() → string?`, returns path of replay to be produced. Note that this is just a prospective file path (nothing is written until the match ends), and that it possible to record a replay of a replay. - -### Build commands -- builders now perform an extra block check immediately when a build command reaches the front of the queue. This is in addition to the existing periodic (≈ 0.4 Hz) block check. A block check cancels a build command if the build site is hard-blocked ("red squares", as opposed to "yellow squares" with reclaimables/mobiles). -- add `Spring.SetEngineBuildSquareRendering(bool) → nil`, for disabling the native rendering of the footprint grid when a build command is selected. -- add `wupget:DrawBuildSquare(unitDefID, x, z, facing, statuses) → nil` unsynced callin, fires when a build command is selected. Statuses is a 1D array for the status of each tile: 0 blocked (red), 1 occupied (yellow), 2 reclaimable (yellow), 3 open (green). This fires even if native drawing is enabled. - -### Misc - -- UTF-8 file paths are now supported. -- some file accesses are now case-sensitive. -- `/aicontrol` is now blocked by default. Call `/aiCtrl PlayerName` or `/aiCtrlByNum 123` to enable. -- `script:AimWeapon` now receives unit-relative heading and pitch, rather than world-space. This means units angled on slopes will receive different values. -- heading cast to radians will now return \[-pi; +pi) rather than \[0; tau). -- `VFS.GetAvailableAIs()` returned entries now have a new `isLuaAI` boolean. -- add `Platform.architecture`, string. Usually "x86_64", with some ongoing work to support "arm64". -- add `Spring.SetCheatingEnabled(bool)`. -- add `Spring.SetGodMode(bool? controlAllies, bool? controlEnemies)`. -- add `Spring.GetClosestEnemyUnit(x, y, z, range = inf) → unitID?` to LuaUI. -- add `Spring.GetClosestEnemyUnit(x, y, z, range = inf, allyTeamID, bool useLoS = true, bool spherical = false, bool requireEnemyToSeePos = false) → unitID?` to LuaRules. -- large QTPFS perf improvements. -- always output logs to stdout. -- add `Engine.isHeadless`, available in unsynced only. -- archive cache version 20 → 21. - -## Fixes - -- fix logs sometimes not getting flushed on exit -- fix `CMD[20]` and `CMD[105]` returning legacy aliases for those commands rather than their standard names. +No changes as of yet. diff --git a/doc/site/content/changelogs/changelog-105-902.markdown b/doc/site/content/changelogs/changelog-105-902.markdown index c3fb9556884..f842b461a29 100644 --- a/doc/site/content/changelogs/changelog-105-902.markdown +++ b/doc/site/content/changelogs/changelog-105-902.markdown @@ -12,7 +12,7 @@ The changelog since release 105-861 **until minor release 105-902**, which happe ### GLDB queries -{: .warning } +> [!WARNING] > This feature only existed until release 105-2314 and is now removed. Add `Spring.MakeGLDBQuery(bool forced) → bool ok` to create a query to the OpenGL drivers database. There's generally only one query at a time allowed, forcing rewrites it. diff --git a/doc/site/content/changelogs/changelog-2026-06.markdown b/doc/site/content/changelogs/changelog-2026-06.markdown new file mode 100644 index 00000000000..d2fe46c6f8f --- /dev/null +++ b/doc/site/content/changelogs/changelog-2026-06.markdown @@ -0,0 +1,66 @@ ++++ +title = "Release 2026.06" +aliases = ['/changelogs/changelog-2026-06'] ++++ + +This is the changelog since version 2025.06 until **version 2026.06.12**, which was released on 2026-07-14. + +## Caveats + +- UTF-8 file paths are now supported. +- some file accesses are now case-sensitive. +- rmlUI version used 6.0 → 6.2 +- ARM64 architecture builds now have nominal support. +- `/aicontrol` is now blocked by default. Call `/aiCtrl PlayerName` or `/aiCtrlByNum 123` to enable. +- `script:AimWeapon` now receives unit-relative heading and pitch, rather than world-space. This means units angled on slopes will receive different values. +- heading cast to radians will now return \[-pi; +pi) rather than \[0; tau). +- minor Lua env sandboxing changes, see the "Lua environment sandboxing" section below. +- always output logs to stdout. +- removed `CSphereParticleSpawner` particle class. Identical to `CSimpleParticleSystem`. +- archive cache version 20 → 21. + +## Features + +### RmlUi + +- rmlUI version used 6.0 → 6.2 +- add datamodel support for pairs: `pairs(dm_handle)` +- add datamodel support for ipairs: `dm_handle:__ipairs()` +- support for accessing the underlying datamodel table with `dm_handle.__raw()` +- allow datamodel self-referential assignments such as `dm_handle.property = dm_handle.another_property` +- support for retrieving datamodel property length: `dm_handle.property.__len()` +- fix datamodel array access +- fix `data-value` binds in rml elements +- added `RmlUi.GetDocumentPathRequests(string docPath) -> {"filePath", "filePath", ...}` which tracks all of the files opened by an RmlUi LoadDocument call +- added `RmlUi.ClearDocumentPathRequests(string docPath) -> nil` to clear tracked LoadDocument files + +### Radar icon Lua API +- added `Spring.SetUnitIcon(unitID, string? iconName)`. Pass `nil` to reset to default. +- added `Spring.GetUnitIcon(unitID) → string iconName`. + +### Minimap callins +- added `wupget:MiniMapRotationChanged(rotation, previousRotation)` unsynced callin. In radians. +- added `wupget:MiniMapGeometryChanged(x, y, sizeX, sizeY, prevX, prevY, prevSizeX, prevSizeY)` unsynced callin. In pixels. +- added `wupget:MiniMapStateChanged(isMinimized, isMaximized, isSlaved)` unsynced callin. + +### Misc + +- UTF-8 file paths are now supported. +- some file accesses are now case-sensitive. +- `/aicontrol` is now blocked by default. Call `/aiCtrl PlayerName` or `/aiCtrlByNum 123` to enable. +- `script:AimWeapon` now receives unit-relative heading and pitch, rather than world-space. This means units angled on slopes will receive different values. +- heading cast to radians will now return \[-pi; +pi) rather than \[0; tau). +- add `Spring.SetCheatingEnabled(bool)`. +- add `Spring.SetGodMode(bool? controlAllies, bool? controlEnemies)`. +- large QTPFS perf improvements. +- always output logs to stdout. +- add boolean `Platform.isHeadless`. +- archive cache version 20 → 21. + +## Fixes + +- fix logs sometimes not getting flushed on exit +- fix `CMD[20]` and `CMD[105]` returning legacy aliases for those commands rather than their standard names. +- fix an archive scanner pool crash. +- fix AI commands not being validated. +- fix Windows reporting hardware incorrectly. diff --git a/doc/site/content/changelogs/changelog-2026-07.markdown b/doc/site/content/changelogs/changelog-2026-07.markdown new file mode 100644 index 00000000000..55a8ae9a270 --- /dev/null +++ b/doc/site/content/changelogs/changelog-2026-07.markdown @@ -0,0 +1,118 @@ ++++ +title = "Release 2026.07" +aliases = ['/changelogs/changelog-2026-07'] ++++ + +This is the changelog since version 2025.07 until **version 2025.07.04**, which was released on 2026-08-04. + +## Caveats +- removed Java bindings for Skirmish AI. +- Lua environment sandboxing changes, each has a caveat. See below. +- builders now perform an extra block check immediately when a build command reaches the front of the queue, in addition to the existing periodic check. This can result in an event spam e.g. if a builder is on repeat between multiple queued buildings and they're all blocked. +- mouse4 and mouse5 ignore mouse ownership and produce MousePress/MouseRelease events even if another mouse button is already pressed. Make sure your wupget handlers handle this correctly if they manage wupget mouse ownership. +- sonar jamming now jams sonar only; no longer blocks regular radar for units in water, i.e. surface ships. No easy replacement. +- errors when loading modrules are no longer silently ignored, rather there's an error popup. Wrap everything in pcall if needed. +- removed `LimitDgun` from being listed in `EngineOptions.lua`, add it to modoptions if you used it as such. +- removed some basecontent gadgets that used `LimitDgun` and other long-removed engineoptions. Copypaste them from older basecontent if needed. + +## Features + +### Lua environment sandboxing +- LuaSocket no longer inits before Lua sandboxing. Anything that relied on the previous order of execution won't work. +- unsynced LuaRules (incl. unsynced LuaGaia) now has access to `io` and `os` libraries. Watch out when loading maps etc. +- unsynced LuaRules (incl. unsynced LuaGaia) now has access to the `debug` library by default (no longer requires devmode). + +### Replay path getters +- add `Spring.GetReplayFilePath() → string?`, returns path of replay being watched. +- add `Spring.GetReplayRecordingFilePath() → string?`, returns path of replay to be produced. Note that this is just a prospective file path (nothing is written until the match ends), and that it possible to record a replay of a replay. + +### Build commands +- builders now perform an extra block check immediately when a build command reaches the front of the queue. This is in addition to the existing periodic (≈ 0.4 Hz) block check. A block check cancels a build command if the build site is hard-blocked ("red squares", as opposed to "yellow squares" with reclaimables/mobiles). +- add `Spring.SetEngineBuildSquareRendering(bool) → nil`, for disabling the native rendering of the footprint grid when a build command is selected. +- add `wupget:DrawBuildSquare(unitDefID, x, z, facing, statuses) → nil` unsynced callin, fires when a build command is selected. Statuses is a 1D array for the status of each tile: 0 blocked (red), 1 occupied (yellow), 2 reclaimable (yellow), 3 open (green). This fires even if native drawing is enabled. + +### Lua trace ray +- adds `Spring.TraceRayBetweenPositions(xA, yA, zA, xB, yB, zB, type) -> {{dist, objID, type}, ...}` +- adds `Spring.TraceRayInDirection(x, y, z, dx, dy, dz, length, type) -> {{dist, objID, type}, ...}` +- type is a string, "unit", "feature", or (for input only) "both" +- the returned array is sorted by distance, starting from closest. + +### Blank map splats + +Blank map generator now interprets some mapoptions. These are for filling entries in the generated `mapinfo.lua` file. + +- `blank_map_splatdetailtex`, string with the path +- `blank_map_splatdistr`, string with the path +- `blank_map_splattexscale1 .. 4`, number +- `blank_map_splattexmult1 .. 4`, number +- `blank_map_splatdetailnormaltex1 .. 4`, string with the path +- `blank_map_splatdetailnormaldiffusealpha`, bool + +### Other runtime map texturing stuff +- fix `Spring.SetSkyBoxTexture` not working if the map didn't have a skybox from the start +- fix map shaders having stale uniforms and being completely broken for forward rendering. This fix is signalled by the `Engine.FeatureSupport.reliableLuaMapShaders` flag. + +### Input emulation + +Added a bunch of `debug.emulateFoo` functions that emulate input. Useful for automated testing of UI. Buttons are considered pressed from any source for edge-based events (i.e. pressing a "real" button when it is already pressed via emulation, or vice versa, will not produce a KeyPressed event; ditto release if it is still pressed from the other source). + +- `debug.emulateKeyPress(keycode)`. The event will have a scancode based on the current keyboard layout (i.e. possibly "unknown" if no keyboard, such as a headless VM). +- `debug.emulateKeyRelease(keycode)` +- `debug.emulateMousePress(button)` +- `debug.emulateMouseRelease(button)` +- `debug.emulateMouseMove(dx, dy)`. Does not actually move the mouse, so getters won't reflect it, but you can `Spring.WarpMouse` alongside it. +- `debug.emulateMouseWheel(number delta)`. Note that this accepts fractions, but most physical mice produce integer deltas (+1, -1). +- `debug.clearEmulatedInput()`. Releases all emulated presses. + +### Keybind-related work +- add `cancelcommand` action for binding, cancels the currently selected command. The hardcoded Escape key binding still works. +- mouse4 and mouse5 ignore mouse ownership and produce MousePress/Release events even if another mouse button is already pressed. +- fix `sc_nonusbackslash` scancode name (was `sc_nonusbacklash`) +- fix `/unbindaction` not clearing scancode bindings +- fix `/unbind` not working with keychains longer than 1 key +- fix stale returns from `Spring.GetActionHotKeys` + +### Resourcing + +- add `gadget:ResourceExcess({[teamID] = {m, e, ...}}) -> bool handledGameside`. Runs every frame. If you return false, the engine will do the existing behaviour where the excess is accumulated until a slowupdate and shared to teammates if possible. +- add `Spring.AddTeamResourceExcessStats(teamID, resourcetype, amount)`. Adjusts the stats for endgame graphs purposes (does not do anything to the actual resources). Useful for when you handle excess yourself with the callin above. + +### Custom teamcolor palette +- add `Spring.SetCustomPaletteColor(paletteID, r, g, b) → nil`. Sets a palette color for shader use. See below. +- add `Spring.GetCustomPaletteColor(paletteID) → r, g, b`. +- add `Engine.maxCustomPaletteID`, the highest available palette ID. +- add `Spring.SetUnitPaletteIndex(unitID, paletteID?) → nil`. Assigns a paletteID to a unit. Use nil to reset to the default palette. +- add `Spring.GetUnitPaletteIndex(unitID) → paletteID?`. +- add `Spring.SetFeaturePaletteIndex(featureID, paletteID?)`. +- add `Spring.GetFeaturePaletteIndex(featureID) → paletteID?`. +- the `teamColor` array in shaders now has much more room and contains both teamcolors and colors of the custom palette, as per above. +- the actual teamID is now available as the fifth byte (first byte of the second 4-byte composite) in model uniform data. +- note that the value of the palette index is different than what is seen from Lua. Units with no custom paletteID have the index point to an entry that contains their team color. +- the basecontent teamcolor shader takes the above changes into account. Existing custom shaders that use `instData.z & 0xFF` for teamID should keep working as long as the palette feature isn't used, to support it properly the constant needs to be `0x7FF` instead. +- ghosts of out-of-sight buildings now draw with the unit's last seen palette color. This also fixes the bug where the teamcolor updated to always reflect the unit's real team even if it changed out of sight. +- projectiles and building ghosts in constructor queues unchanged, they still just draw teamcolor naively and cannot use a shader. + +### Building ghosts +- building ghosts now use the custom palette teamcolor. +- fix building ghosts always showing their "true" teamcolor, now they stick to their last known teamcolor (including custom palette). +- fix full-view spectators seeing a ground decal wherever their spectated team sees a ghost with decal. + +### Misc +- removed Java bindings for Skirmish AI. +- entries returned by `VFS.GetAvailableAIs()` now have a new `isLuaAI` boolean (skirmish AI otherwise). +- errors when loading modrules are no longer silently ignored, rather there's an error popup. +- add `Spring.GetPrevFrameSyncChecksum() -> string` and `Platform.hasSyncChecksums`. Useful for correctness checks. Note that the checksum looks like a number but is NOT convertible to one in Lua (checksum is a 32-bit number but Lua numbers have 24-bit precision). +- add string `Platform.architecture`. Usually "x86_64", with some ongoing work to support "arm64". +- add `Spring.GetClosestEnemyUnit(x, y, z, range = inf) → unitID?` to LuaUI. +- add `Spring.GetClosestEnemyUnit(x, y, z, range = inf, allyTeamID, bool useLoS = true, bool spherical = false, bool requireEnemyToSeePos = false) → unitID?` to LuaRules. +- add `Engine.FeatureSupport.reliableLuaMapShaders` bool, means issues with map shaders having stale uniforms and being completely broken for forward rendering are fixed. +- the `/dumpAtlas` command now accepts a file format (e.g. `/dumpatlas proj tga`), defaults to the existing `png`. +- Barbarian AI bundled with the engine updated to version 1.6.28. Just maintenance, no new features. + +### Fixes +- fix LuaSocket initialisation for LuaMenu. +- fix unitsync not running functions passed to `Spring.TimeCheck`. +- fix Skirmish AI API compilation issues due to `AIFloat3` having a non-trivial constructor. +- attempt to fix the lack of `wupget:GameProgress` calls when initially catching up. +- fix Lua `VBO::CopyTo` to copy VBO data CPU side in additon to GPU side. Formerly it only copied data GPU side. +- fix QTPFS path cleanup when an immediate path search fails diff --git a/doc/site/content/development/documenting-lua.md b/doc/site/content/development/documenting-lua.md index ca1b2fdc701..304ecafe702 100644 --- a/doc/site/content/development/documenting-lua.md +++ b/doc/site/content/development/documenting-lua.md @@ -97,8 +97,7 @@ All files under `/rts/Lua/Library/` are directly copied into the library when th - `integer` - `table<,>` -{: .note } - +> [!NOTE] > Literals (e.g. `true`, `false`, `5`) are also available as types. `true` is useful in the case where a table is being used as a set, e.g. > > ``` @@ -134,8 +133,7 @@ An array type is expressed as `type[]`. - Specify return type with `@return type name Description...` - For multiple returns use one per line. -{: .warning } - +> [!WARNING] > `@return` must specify the type _before_ the name, whereas `@param` takes the name before the type. ````cpp @@ -156,6 +154,19 @@ An array type is expressed as `type[]`. */ ```` +#### Aliases + +If a function is exported to two Lua functions, you can give it two `@function` tags: + +```cpp +/*** + * @function Spring.GetLocalPlayerID + * @function Spring.GetMyPlayerID + * @return integer playerID + */ +int LuaUnsyncedRead::GetLocalPlayerID(lua_State* L) +``` + ### Class Structured data is expressed as a class. This represents a table with expected key/value pairs. diff --git a/doc/site/content/docs/guides/getting-started/first-steps-with-the-engine.md b/doc/site/content/docs/guides/getting-started/first-steps-with-the-engine.md index 2f023a572ac..a30c72ad182 100644 --- a/doc/site/content/docs/guides/getting-started/first-steps-with-the-engine.md +++ b/doc/site/content/docs/guides/getting-started/first-steps-with-the-engine.md @@ -302,17 +302,17 @@ Two games are particularly suited for newcomers: 4. Edit Lua files and retest Changes to Lua code take effect on the next game start. For workflow tips, -see the [Lua Language Server guide](lua-language-server/) for setting up IDE +see the [Lua Language Server guide](lua-language-server.md) for setting up IDE autocompletion. ## Next Steps Once you have the engine running with a game and map: -- Learn about [VFS basics](vfs-basics/) to understand how the engine loads content -- Read about [Widgets and Gadgets](widgets-and-gadgets/) to understand Lua scripting -- Explore [basecontent](basecontent/) for the default scripts provided by the engine -- Check out [Unit Types Basics](unit-types-basics/) to define your first units +- Learn about [VFS basics](vfs-basics.md) to understand how the engine loads content +- Read about [Widgets and Gadgets](widgets-and-gadgets.md) to understand Lua scripting +- Explore [basecontent](basecontent.md) for the default scripts provided by the engine +- Check out [Unit Types Basics](unit-types-basics.md) to define your first units For API reference while developing, see the [Lua API documentation](/docs/lua-api/). @@ -340,4 +340,4 @@ error, see the [Write Directory](#write-directory) section above. Quick fixes: When in doubt, the engine sources contain the authoritative behavior. Search the [`rts/System/FileSystem/`](https://github.com/beyond-all-reason/RecoilEngine/tree/master/rts/System/FileSystem) -directory for data directory handling code. \ No newline at end of file +directory for data directory handling code. diff --git a/doc/site/content/docs/guides/getting-started/getting-started-with-rmlui.md b/doc/site/content/docs/guides/getting-started/getting-started-with-rmlui.md index b99aa69f3f7..2d21709bd9a 100644 --- a/doc/site/content/docs/guides/getting-started/getting-started-with-rmlui.md +++ b/doc/site/content/docs/guides/getting-started/getting-started-with-rmlui.md @@ -5,9 +5,9 @@ draft = false author = "Slashscreen" +++ -RmlUi is a UI framework that is defined using a HTML/CSS style workflow (using Lua instead of JS) intended to simplify UI development especially for those already familiar with web development. It is designed for interactive applications, and so is reactive by default. You can learn more about it on the [RmlUI website] and [differences in the Recoil version here](#differences-between-upstream-rmlui-and-rmlui-in-recoil). +RmlUi is a UI framework that is defined using a HTML/CSS style workflow (using Lua instead of JS) intended to simplify UI development especially for those already familiar with web development. It is designed for interactive applications, and so is reactive by default. You can learn more about it on the [RmlUi website] and [differences in the Recoil version here](#differences-between-upstream-rmlui-and-rmlui-in-recoil). -## How does RmlUI Work? +## How does RmlUi Work? To get started, it's important to learn a few key concepts. - Context: This is a bundle of documents and data models. @@ -416,7 +416,7 @@ document = widget.rmlContext:LoadDocument("document.rml", document_table) - The Beyond All Reason devs prefer to use one shared context for all rmlui widgets. -### Differences between upstream RmlUI and RmlUI in Recoil +### Differences between upstream RmlUi and RmlUi in Recoil - The SVG element allows either a filepath or raw SVG data in the src attribute, allowing for inline svg to be used (this may change to svg being supported between the opening and closing tag when implemented upstream) - An additional element `````` is available which allows for textures loaded in Recoil to be used, this behaves the same as an `````` element except the src attribute takes a [texture reference]({{% ref "articles/texture-reference-strings" %}}) diff --git a/doc/site/content/docs/guides/getting-started/widgets-and-gadgets.md b/doc/site/content/docs/guides/getting-started/widgets-and-gadgets.md index 216f9e21f72..6200e2f24b0 100644 --- a/doc/site/content/docs/guides/getting-started/widgets-and-gadgets.md +++ b/doc/site/content/docs/guides/getting-started/widgets-and-gadgets.md @@ -10,7 +10,7 @@ Before we get to Widgets and Gadgets we will start with an overview of some key - Synced mode is the environment that affects game simulation e.g ordering units around. Synced commands are distributed and run by all players in the game to ensure the simulation remains synced. - Unsynced mode is the players own environment, functionality here could for example enhance controls, display helpful information. -When in Unsynced mode LuaIntro, LuaUi, LuaRules and LuaGaia provide read access to synced but only LuaRules and LuaGaia have full access to simulation information (all players units etc.). In LuaIntro and LuaUi read access to synced is scoped to just what that player can see i.e. observing LoS & radar ranges. +When in Unsynced mode LuaIntro, LuaUI, LuaRules and LuaGaia provide read access to synced but only LuaRules and LuaGaia have full access to simulation information (all players units etc.). In LuaIntro and LuaUI read access to synced is scoped to just what that player can see i.e. observing LoS & radar ranges. ### Key Areas - LuaRules - Generally home to lower level customisations that affect unit behavior or overall game operation @@ -29,7 +29,7 @@ Widgets and Gadgets are concepts that have been adopted by several games using R Gadgets typically being lower level game logic, unit behaviour functionality that defines your game and should be present for all players, they are typically only found in LuaRules and LuaGaia and are often included using VFS.ZIP_ONLY so as not to be easily overridden by end users (your packaged version takes priority). -Widgets usually involve improving UX or showing helpful UI interfaces, and are often considered things that users can turn on/off in the game to suit their needs, be it through settings or a widget manager UI. They are usually specific to LuaIntro, LuaMenu and LuaUi. +Widgets usually involve improving UX or showing helpful UI interfaces, and are often considered things that users can turn on/off in the game to suit their needs, be it through settings or a widget manager UI. They are usually specific to LuaIntro, LuaMenu and LuaUI. To manage the invocation of Widgets & Gadgets a "**handler**" is setup in the Lua entrypoint. For environments with synced and unsyned entry points these typically use the same handler which calls the same widgets/gadgets but use a function the he handler like IsSyncedCode to check if they are being run in synced mode or unsynced mode and running the appropriate section of code. @@ -85,7 +85,7 @@ end ``` ## Handlers -Handlers are the code that runs in the entry point to load in addons/widgets/gadgets and distribute callins to them, and example implementation of a handler is included in the [Recoil base content](https://github.com/beyond-all-reason/RecoilEngine/blob/master/cont/base/springcontent/LuaHandler/handler.lua) for LuaIntro, LuaRules and LuaUi, and will likely do well for a simple game, although many games eventually choose to make their own handlers. This means for LuaIntro and LuaUi you can start creating widgets without worrying about handlers in the luaintro/widgets or luaui/widgets folders, and for creating gadgets for LuaRules in luarules/gadgets folder. +Handlers are the code that runs in the entry point to load in addons/widgets/gadgets and distribute callins to them, and example implementation of a handler is included in the [Recoil base content](https://github.com/beyond-all-reason/RecoilEngine/blob/master/cont/base/springcontent/LuaHandler/handler.lua) for LuaIntro, LuaRules and LuaUI, and will likely do well for a simple game, although many games eventually choose to make their own handlers. This means for LuaIntro and LuaUI you can start creating widgets without worrying about handlers in the luaintro/widgets or luaui/widgets folders, and for creating gadgets for LuaRules in luarules/gadgets folder. Below is a very simplified pseudocode example of a handler is below to illustrate adding a gadget and forwarding callins to gadgets. ```lua diff --git a/doc/site/mise.ci.toml b/doc/site/mise.ci.toml index 50f2d98fccb..f1d504be9db 100644 --- a/doc/site/mise.ci.toml +++ b/doc/site/mise.ci.toml @@ -6,6 +6,6 @@ hugo = "{{env.HUGO_VERSION}}" "cargo:emmylua_doc_cli" = "{{env.EMMYLUA_DOC_CLI_VERSION}}" "aqua:jqlang/jq" = "{{env.JQ_VERSION}}" - "aqua:ip7z/7zip" = "{{env.7Z_VERSION}}" + "aqua:ip7z/7zip" = "{{env.SEVENZIP_VERSION}}" "npm:lua-doc-extractor" = "{{env.LUA_DOC_EXTRACTOR_VERSION}}" "ruby" = "{{env.RUBY_VERSION}}" diff --git a/doc/site/mise.toml b/doc/site/mise.toml index 8604af17fac..c473389ac1d 100644 --- a/doc/site/mise.toml +++ b/doc/site/mise.toml @@ -1,3 +1,6 @@ +[settings.npm] + package_manager = "npm" + [tools] # We only install tools required for each task on each task, unless all tasks use a tool here # For CI when we need to install and cache all tools, see mise.ci.toml @@ -8,7 +11,7 @@ EMMYLUA_DOC_CLI_VERSION = "0.8.2" # lua_pages lua_library lua_check LUA_LANGUAGE_SERVER_VERSION = "3.15.0" # lua_check JQ_VERSION = "1.8.0" # binary_pages - 7Z_VERSION = "24.09" # binary_pages + SEVENZIP_VERSION = "24.09" # binary_pages RUBY_VERSION = "3.3" # lua_pages LUA_DOC_EXTRACTOR_SOURCE_REF = "https://github.com/beyond-all-reason/RecoilEngine/blob/master" RECOIL_LUA_LIBRARY_DIR = "rts/Lua/library" @@ -17,7 +20,7 @@ lua_doc_gen_dest = "$RECOIL_LUA_LIBRARY_DIR/generated" site_dir = 'doc/site' site_temp_dir = "{{vars.site_dir}}/temp" - lua_doc_paths = 'rts/{Lua,Rml/SolLua}/**/*.cpp' + lua_doc_paths = 'rts/{Lua,Rml/SolLua,Sim/Units/Scripts}/**/*.cpp' lua_pages_dir = "{{vars.site_dir}}/content/docs/lua-api" emmylua_cli_template = "{{vars.site_dir}}/emmylua-doc-cli-template" latest_release_data = 'data/latest_release.json' @@ -51,7 +54,7 @@ depends = ["latest_release_data"] description = "Generate Docs from Recoil binary" tools."aqua:jqlang/jq" = "{{env.JQ_VERSION}}" - tools."aqua:ip7z/7zip" = "{{env.7Z_VERSION}}" + tools."aqua:ip7z/7zip" = "{{env.SEVENZIP_VERSION}}" run = ''' download_url=$(jq -r '.assets[] | select(.name | contains("amd64-linux.7z")).browser_download_url' {{vars.latest_release_data}}) @@ -101,7 +104,7 @@ [tasks.lua_library] description = "Generate Lua Docs" - tools."npm:lua-doc-extractor" = "{{env.LUA_DOC_EXTRACTOR_VERSION}}" + tools."npm:lua-doc-extractor" = { version = "{{env.LUA_DOC_EXTRACTOR_VERSION}}", allow_low_downloads = true } dir = "../../" run = [ "lua-doc-extractor --src \"{{vars.lua_doc_paths}}\" --dest {{vars.lua_doc_gen_dest}} --repo \"${LUA_DOC_EXTRACTOR_SOURCE_REF}\"" diff --git a/docker-build-v2/build.sh b/docker-build-v2/build.sh index 765ba4a1495..6bded8b8b57 100755 --- a/docker-build-v2/build.sh +++ b/docker-build-v2/build.sh @@ -179,7 +179,16 @@ if [[ "$GIT_DIR" != "$GIT_COMMON_DIR" ]]; then WORKTREE_MOUNTS="-v $GIT_COMMON_DIR:$GIT_COMMON_DIR:ro" fi -$RUNTIME run --platform=linux/$ARCH -it --rm \ +# Docker's -t requires stdin AND stdout to be TTYs; in CI, pipes, or agent +# contexts one or both are missing and docker errors out with "the input +# device is not a TTY". Only add -t when it's safe; -i is harmless either +# way (non-interactive stdin just sees EOF). +TTY_FLAG= +if [[ -t 0 && -t 1 ]]; then + TTY_FLAG=-t +fi + +$RUNTIME run --platform=linux/$ARCH -i $TTY_FLAG --rm \ -v "$CWD${P}":/build/src:z,ro \ -v "$CWD${P}.cache${P}ccache-$PLATFORM":/build/cache:z,rw \ -v "$CWD${P}build-$PLATFORM":/build/out:z,rw \ diff --git a/rts/CMakeLists.txt b/rts/CMakeLists.txt index 001ea7cb6ba..ae47f2aa4a8 100644 --- a/rts/CMakeLists.txt +++ b/rts/CMakeLists.txt @@ -142,7 +142,6 @@ make_global_var(engineSources ${sources_engine_ExternalAI} ) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/assimp/include) #FIXME: hack for rts/Rendering/Models/IModelParser.cpp -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/simdjson/include) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/lib/fastgltf/include) ### Add headers for generated project files (e.g. Code::Blocks) diff --git a/rts/Game/Camera.cpp b/rts/Game/Camera.cpp index 8c5ea462a4b..921c1b9ffb5 100644 --- a/rts/Game/Camera.cpp +++ b/rts/Game/Camera.cpp @@ -746,8 +746,8 @@ float3 CCamera::GetMoveVectorFromState(bool fromKeyState) const } int2 border; - border.x = std::max(1, windowW * edgeMoveWidth); - border.y = std::max(1, viewH * edgeMoveWidth); + border.x = std::max(1, static_cast (windowW * edgeMoveWidth)); + border.y = std::max(1, static_cast ( viewH * edgeMoveWidth)); float2 move; // must be float, ints don't save the sign in case of 0 and we need it for copysign() diff --git a/rts/Game/Camera.h b/rts/Game/Camera.h index 7901f2218e2..5a114e152f8 100644 --- a/rts/Game/Camera.h +++ b/rts/Game/Camera.h @@ -284,7 +284,7 @@ class CCamera { */ float moveSlowMult; - int edgeMoveWidth; + float edgeMoveWidth; int useInterpolate; bool edgeMoveDynamic; diff --git a/rts/Game/Camera/SpringController.cpp b/rts/Game/Camera/SpringController.cpp index 8297bff4717..81c171a5ff0 100644 --- a/rts/Game/Camera/SpringController.cpp +++ b/rts/Game/Camera/SpringController.cpp @@ -39,6 +39,8 @@ CONFIG(bool, CamSpringEdgeRotate).defaultValue(false).description("Rotate camer CONFIG(float, CamSpringFastScaleMouseMove).defaultValue(3.0f / 10.0f).description("Scaling for CameraMoveFastMult in spring camera mode while moving mouse."); CONFIG(float, CamSpringFastScaleMousewheelMove).defaultValue(2.0f / 10.0f).description("Scaling for CameraMoveFastMult in spring camera mode while scrolling with mouse."); CONFIG(int, CamSpringTrackMapHeightMode).defaultValue(HeightTracking::Terrain).description("Camera height is influenced by terrain height. 0=Static 1=Terrain 2=Smoothmesh"); +CONFIG(float, CamSpringSmoothMeshBlendMinDist).defaultValue(150.0f).description("Zoom distance below which smoothmesh height tracking mode (2) follows the raw terrain."); +CONFIG(float, CamSpringSmoothMeshBlendMaxDist).defaultValue(600.0f).description("Zoom distance above which smoothmesh height tracking mode (2) fully follows the smooth mesh."); CSpringController::CSpringController() @@ -51,7 +53,7 @@ CSpringController::CSpringController() { RECOIL_DETAILED_TRACY_ZONE; enabled = configHandler->GetBool("CamSpringEnabled"); - configHandler->NotifyOnChange(this, {"CamSpringScrollSpeed", "CamSpringFOV", "CamSpringMinZoomDistance", "CamSpringZoomInToMousePos", "CamSpringZoomOutFromMousePos", "CamSpringFastScaleMousewheelMove", "CamSpringFastScaleMouseMove", "CamSpringEdgeRotate", "CamSpringLockCardinalDirections", "CamSpringTrackMapHeightMode"}); + configHandler->NotifyOnChange(this, {"CamSpringScrollSpeed", "CamSpringFOV", "CamSpringMinZoomDistance", "CamSpringZoomInToMousePos", "CamSpringZoomOutFromMousePos", "CamSpringFastScaleMousewheelMove", "CamSpringFastScaleMouseMove", "CamSpringEdgeRotate", "CamSpringLockCardinalDirections", "CamSpringTrackMapHeightMode", "CamSpringSmoothMeshBlendMinDist", "CamSpringSmoothMeshBlendMaxDist"}); ConfigUpdate(); } @@ -75,9 +77,11 @@ void CSpringController::ConfigUpdate() doRotate = configHandler->GetBool("CamSpringEdgeRotate"); lockCardinalDirections = configHandler->GetBool("CamSpringLockCardinalDirections"); trackMapHeight = configHandler->GetInt("CamSpringTrackMapHeightMode"); + meshBlendMinDist = configHandler->GetFloat("CamSpringSmoothMeshBlendMinDist"); + meshBlendMaxDist = std::max(configHandler->GetFloat("CamSpringSmoothMeshBlendMaxDist"), meshBlendMinDist + 1.0f); if (trackMapHeight == HeightTracking::Smooth && !modInfo.enableSmoothMesh) { - LOG_L(L_ERROR, "Smooth mesh disabled"); + LOG_L(L_WARNING, "[CSpringController] smoothmesh height tracking (mode 2) requested but the game disabled the smooth mesh, falling back to terrain tracking"); trackMapHeight = HeightTracking::Terrain; } } @@ -88,7 +92,7 @@ void CSpringController::ConfigNotify(const std::string & key, const std::string ConfigUpdate(); } -void CSpringController::SmoothCamHeight(const float3& prevPos) { +void CSpringController::FreezeCamHeight() { RECOIL_DETAILED_TRACY_ZONE; if (!pos.IsInBounds()) { return; @@ -103,12 +107,8 @@ void CSpringController::SmoothCamHeight(const float3& prevPos) { // when there's a hill blocking the view const float3 newGroundPos = camPos + dir * distToGround; if (distToGround > 0.0f && newGroundPos.IsInBounds()) { - const float camHeightDiff = (trackMapHeight == HeightTracking::Smooth) ? - smoothGround.GetHeight(pos.x, pos.z) - smoothGround.GetHeight(prevPos.x, prevPos.z) : - 0.0f; - pos = newGroundPos; - curDist = distToGround + (dir * camHeightDiff).Length() * Sign(camHeightDiff); + curDist = distToGround; } } @@ -126,8 +126,6 @@ void CSpringController::KeyMove(float3 move) return; } - const float3 prevPos = pos; - move *= 200.0f; const float3 flatForward = (dir * XZVector).ANormalize(); pos += (camera->GetRight() * move.x + flatForward * move.y) * pixelSize * 2.0f * scrollSpeed; @@ -138,13 +136,17 @@ void CSpringController::KeyMove(float3 move) // - 'pos' point of focus on the ground // - 'curDist' camera distance break; + case HeightTracking::Smooth: + // No pre-step needed here: Update() runs immediately below and applies + // smooth-mesh focus height via GetFocusSurfaceHeight(). FreezeCamHeight() + // is only required for Disabled mode, where we must raycast/recompute + // focus and distance before Update() to avoid resnapping. + break; case HeightTracking::Disabled: // freezing camera height requires raycasting from current // camera position and recalculating // point of focus and distance - [[fallthrough]]; - case HeightTracking::Smooth: - SmoothCamHeight(prevPos); + FreezeCamHeight(); break; } @@ -318,16 +320,74 @@ float CSpringController::ZoomOut(const float3& curCamPos, const float3& newDir, -float CSpringController::GetFocusSurfaceHeight(float x, float z) const +float CSpringController::GetFocusSurfaceHeight(float x, float z, float dist) const { RECOIL_DETAILED_TRACY_ZONE; - return CGround::GetHeightReal(x, z, false); + const float groundHeight = CGround::GetHeightReal(x, z, false); + + if (trackMapHeight != HeightTracking::Smooth) + return groundHeight; + + // fade to the raw ground at close zoom, the mesh hovers high near cliffs + // and would otherwise limit zoom-in depth and panning speed there + const float meshBlend = smoothstep(meshBlendMinDist, meshBlendMaxDist, dist); + return mix(groundHeight, smoothGround.GetHeightSmooth(x, z), meshBlend); } float CSpringController::DistanceToFocusSurface(const float3& from) const { RECOIL_DETAILED_TRACY_ZONE; - return DistanceToGround(from, dir, pos.y); + const float groundDist = DistanceToGround(from, dir, pos.y); + + if (trackMapHeight != HeightTracking::Smooth || groundDist <= 0.0f) + return groundDist; + + // intersect the view ray with the focus surface; using the ground distance + // would lift the camera by the mesh-ground gap on every zoom step + const auto heightAboveSurface = [&](float t) { + const float3 p = from + dir * t; + return p.y - GetFocusSurfaceHeight(p.x, p.z, t); + }; + + if (heightAboveSurface(0.0f) <= 0.0f) + return groundDist; // camera below the surface + + if (heightAboveSurface(groundDist) >= 0.0f) + return groundDist; // ground above the surface + + // March to bracket the first crossing, then bisect. The march has to be fine + // enough not to step over a near crossing on grazing rays (which can dip below + // the surface and back out several times), otherwise we would bracket a farther + // crossing and lock the camera onto the wrong hill. This runs once per zoom + // action, not per frame, so a generous step count is cheap. + constexpr int NUM_MARCH_STEPS = 16; + constexpr int NUM_BISECTION_STEPS = 16; + const float step = groundDist / NUM_MARCH_STEPS; + + float above = 0.0f; + float below = groundDist; + + for (int i = 1; i < NUM_MARCH_STEPS; ++i) { + const float t = step * i; + + if (heightAboveSurface(t) > 0.0f) { + above = t; + } else { + below = t; + break; + } + } + + for (int i = 0; i < NUM_BISECTION_STEPS; ++i) { + const float mid = (above + below) * 0.5f; + + if (heightAboveSurface(mid) > 0.0f) + above = mid; + else + below = mid; + } + + return (above + below) * 0.5f; } @@ -337,13 +397,13 @@ void CSpringController::Update() pos.x = std::clamp(pos.x, 0.01f, mapDims.mapx * SQUARE_SIZE - 0.01f); pos.z = std::clamp(pos.z, 0.01f, mapDims.mapy * SQUARE_SIZE - 0.01f); - pos.y = GetFocusSurfaceHeight(pos.x, pos.z); // always focus on the ground + curDist = std::clamp(curDist, minDist, maxDist); + pos.y = GetFocusSurfaceHeight(pos.x, pos.z, curDist); // always focus on the ground rot.x = std::clamp(rot.x, math::PI * 0.51f, math::PI * 0.99f); // camera->SetRot(float3(rot.x, GetAzimuth(), rot.z)); dir = CCamera::GetFwdFromRot(this->GetRot()); - curDist = std::clamp(curDist, minDist, maxDist); pixelSize = (camera->GetTanHalfFov() * 2.0f) / globalRendering->viewSizeY * curDist * 2.0f; } diff --git a/rts/Game/Camera/SpringController.h b/rts/Game/Camera/SpringController.h index a92b81ad3fe..61e3d8b246c 100644 --- a/rts/Game/Camera/SpringController.h +++ b/rts/Game/Camera/SpringController.h @@ -43,8 +43,8 @@ class CSpringController : public CCameraController inline float ZoomIn(const float3& curCamPos, const float3& dir, const float& curDistPre, const float& scaledMode); inline float ZoomOut(const float3& curCamPos, const float3& dir, const float& curDistPre, const float& scaledMode); - void SmoothCamHeight(const float3& prevPos); - float GetFocusSurfaceHeight(float x, float z) const; + void FreezeCamHeight(); + float GetFocusSurfaceHeight(float x, float z, float dist) const; float DistanceToFocusSurface(const float3& from) const; private: @@ -57,6 +57,9 @@ class CSpringController : public CCameraController float fastScaleMove; float fastScaleMousewheel; + float meshBlendMinDist; + float meshBlendMaxDist; + bool zoomBack; bool cursorZoomIn; bool cursorZoomOut; diff --git a/rts/Game/Game.cpp b/rts/Game/Game.cpp index 6da6bf2b229..d58c51301a0 100644 --- a/rts/Game/Game.cpp +++ b/rts/Game/Game.cpp @@ -53,6 +53,7 @@ #include "Rendering/Textures/NamedTextures.h" #include "Lua/LuaGaia.h" #include "Lua/LuaHandle.h" +#include "Lua/LuaDebugExtra.h" #include "Lua/LuaInputReceiver.h" #include "Lua/LuaMenu.h" #include "Lua/LuaRules.h" @@ -1022,6 +1023,9 @@ void CGame::KillInterface() spring::SafeDelete(tooltip); // CTooltipConsole* LOG("[Game::%s][2]", __func__); + // drop emulated input state (game-scoped, like the bindings below); no-fire + // since the Lua handles are being destroyed + LuaDebugExtra::ClearEmulatedInput(false); keyBindings.Kill(); selectionKeys.Kill(); // CSelectionKeyHandler* spring::SafeDelete(inMapDrawerModel); @@ -1033,6 +1037,12 @@ void CGame::KillSimulation() RECOIL_DETAILED_TRACY_ZONE; LOG("[Game::%s][1]", __func__); + // a failed load leaves half-initialized objects behind, freeing them crashes + if (spring::exitCode == spring::EXIT_CODE_NOLOAD && gu->globalQuit) { + LOG_L(L_WARNING, "[Game::%s] simulation never finished loading, leaking it", __func__); + return; + } + // Kill all teams that are still alive, in // case the game did not do so through Lua. // diff --git a/rts/Game/UI/GuiHandler.cpp b/rts/Game/UI/GuiHandler.cpp index 370e21cbe93..6cf530ad46c 100644 --- a/rts/Game/UI/GuiHandler.cpp +++ b/rts/Game/UI/GuiHandler.cpp @@ -1851,16 +1851,18 @@ int CGuiHandler::GetIconPosCommand(int slot) const // only called by SetActiveCo } +void CGuiHandler::CancelActiveCommand() +{ + activeMousePress = false; + SetActiveCommandIndex(-1); +} + + bool CGuiHandler::KeyPressed(int keyCode, int scanCode, bool isRepeat) { RECOIL_DETAILED_TRACY_ZONE; - if (keyCode == SDLK_ESCAPE && activeMousePress) { - activeMousePress = false; - SetActiveCommandIndex(-1); - return true; - } - if (keyCode == SDLK_ESCAPE && inCommand >= 0) { - SetActiveCommandIndex(-1); + if (keyCode == SDLK_ESCAPE && (activeMousePress || inCommand >= 0)) { + CancelActiveCommand(); return true; } diff --git a/rts/Game/UI/GuiHandler.h b/rts/Game/UI/GuiHandler.h index ee22de092ed..de1625aaf99 100644 --- a/rts/Game/UI/GuiHandler.h +++ b/rts/Game/UI/GuiHandler.h @@ -99,6 +99,7 @@ class CGuiHandler : public CInputReceiver { bool SetActiveCommand(int cmdIndex, bool rightMouseButton); bool SetActiveCommand(int cmdIndex, int button, bool leftMouseButton, bool rightMouseButton, bool alt, bool ctrl, bool meta, bool shift); bool SetActiveCommand(const Action& action, const CKeySet& ks, int actionIndex); + void CancelActiveCommand(); void SetDrawSelectionInfo(bool dsi) { drawSelectionInfo = dsi; } bool GetDrawSelectionInfo() const { return drawSelectionInfo; } diff --git a/rts/Game/UI/KeyBindings.cpp b/rts/Game/UI/KeyBindings.cpp index 9bafd6ca542..cc756cd26d5 100644 --- a/rts/Game/UI/KeyBindings.cpp +++ b/rts/Game/UI/KeyBindings.cpp @@ -611,6 +611,7 @@ void CKeyBindings::AddActionToKeyMap(KeyMap& bindings, Action& action) ActionList& al = bindings[ks]; action.bindingIndex = ++bindingsCount; al.push_back(action); + buildHotkeyMap = true; } else { ActionList& al = it->second; assert(it->first == ks); @@ -624,6 +625,7 @@ void CKeyBindings::AddActionToKeyMap(KeyMap& bindings, Action& action) // not yet bound, push it action.bindingIndex = ++bindingsCount; al.push_back(action); + buildHotkeyMap = true; } } } @@ -662,8 +664,8 @@ bool CKeyBindings::Bind(const std::string& keystr, const std::string& line) bool CKeyBindings::UnBind(const std::string& keystr, const std::string& command) { RECOIL_DETAILED_TRACY_ZONE; - CKeySet ks; - if (!ks.Parse(keystr)) { + CKeyChain kc; + if (!ParseKeyChain(keystr, &kc) || kc.empty()) { LOG_L(L_WARNING, "UnBind: could not parse key: %s", keystr.c_str()); return false; } @@ -671,6 +673,7 @@ bool CKeyBindings::UnBind(const std::string& keystr, const std::string& command) if (debugEnabled) LOG("[CKeyBindings::%s] keystr=%s command=%s", __func__, keystr.c_str(), command.c_str()); + const CKeySet& ks = kc.back(); KeyMap& bindings = ks.IsKeyCode() ? codeBindings : scanBindings; const auto it = bindings.find(ks); @@ -683,6 +686,9 @@ bool CKeyBindings::UnBind(const std::string& keystr, const std::string& command) if (al.empty()) bindings.erase(it); + if (success) + buildHotkeyMap = true; + return success; } @@ -707,6 +713,7 @@ bool CKeyBindings::UnBindKeyset(const std::string& keystr) return false; bindings.erase(it); + buildHotkeyMap = true; return true; } @@ -740,7 +747,16 @@ bool CKeyBindings::UnBindAction(const std::string& command) RECOIL_DETAILED_TRACY_ZONE; if (debugEnabled) LOG("[CKeyBindings::%s] command=%s", __func__, command.c_str()); - return RemoveActionFromKeyMap(command, codeBindings) || RemoveActionFromKeyMap(command, scanBindings); + // clear both maps; || would short-circuit and leave the scancode binding when + // the action is also bound to a keycode + const bool removedFromCode = RemoveActionFromKeyMap(command, codeBindings); + const bool removedFromScan = RemoveActionFromKeyMap(command, scanBindings); + const bool changed = removedFromCode || removedFromScan; + + if (changed) + buildHotkeyMap = true; + + return changed; } @@ -811,9 +827,6 @@ void CKeyBindings::ConfigNotify(const std::string& key, const std::string& value void CKeyBindings::LoadDefaults() { RECOIL_DETAILED_TRACY_ZONE; - const bool tmpBuildHotkeyMap = buildHotkeyMap; - buildHotkeyMap = false; - if (debugEnabled) LOG("[CKeyBindings::%s]", __func__); @@ -822,8 +835,7 @@ void CKeyBindings::LoadDefaults() for (const auto& b: defaultBindings) { Bind(b.key, b.action); } - - buildHotkeyMap = tmpBuildHotkeyMap; + // no rebuild here: only ever used as a building block, the caller rebuilds } @@ -858,7 +870,7 @@ void CKeyBindings::PushAction(const Action& action) } } -bool CKeyBindings::ExecuteCommand(const std::string& line) +bool CKeyBindings::ExecuteCommandInternal(const std::string& line) { RECOIL_DETAILED_TRACY_ZONE; const std::vector words = CSimpleParser::Tokenize(line, 2); @@ -887,7 +899,7 @@ bool CKeyBindings::ExecuteCommand(const std::string& line) if (loadStack.empty() && words.size() == 1) LoadDefaults(); - Load(filename); + LoadInternal(filename); } else if (command == "keyreload") { const std::string& filename = words.size() > 1 ? words[1] : DEFAULT_FILENAME; @@ -895,13 +907,13 @@ bool CKeyBindings::ExecuteCommand(const std::string& line) if (debugEnabled) LOG("[CKeyBindings::%s] line=%s", __func__, line.c_str()); - ExecuteCommand("unbindall"); - ExecuteCommand("unbind enter chat"); + ExecuteCommandInternal("unbindall"); + ExecuteCommandInternal("unbind enter chat"); if (loadStack.empty() && words.size() == 1) LoadDefaults(); - Load(filename); + LoadInternal(filename); } else if (command == "keydefaults") { LoadDefaults(); @@ -930,6 +942,7 @@ bool CKeyBindings::ExecuteCommand(const std::string& line) keyCodes.Reset(); scanCodes.Reset(); bindingsCount = 0; + buildHotkeyMap = true; Bind("enter", "chat"); // bare minimum if (debugEnabled) @@ -939,14 +952,19 @@ bool CKeyBindings::ExecuteCommand(const std::string& line) return false; } - if (buildHotkeyMap) - BuildHotkeyMap(); - return false; } -bool CKeyBindings::Load(const std::string& filename) +bool CKeyBindings::ExecuteCommand(const std::string& line) +{ + const bool ret = ExecuteCommandInternal(line); + MaybeBuildHotkeyMap(); + return ret; +} + + +bool CKeyBindings::LoadInternal(const std::string& filename) { RECOIL_DETAILED_TRACY_ZONE; if (std::find(loadStack.begin(), loadStack.end(), filename) != loadStack.end()) { @@ -958,9 +976,6 @@ bool CKeyBindings::Load(const std::string& filename) return false; } - const bool tmpBuildHotkeyMap = buildHotkeyMap; - buildHotkeyMap = false; - if (debugEnabled) { LOG("[CKeyBindings::%s] filename=%s%s", __func__, filename.c_str(), loadStack.empty() ? "" : ", load stack:"); for (auto it = loadStack.rbegin(); it != loadStack.rend(); ++it) @@ -973,17 +988,34 @@ bool CKeyBindings::Load(const std::string& filename) CSimpleParser parser(ifs); while (!parser.Eof()) { - ExecuteCommand(parser.GetCleanLine()); + ExecuteCommandInternal(parser.GetCleanLine()); } loadStack.pop_back(); - buildHotkeyMap = tmpBuildHotkeyMap; - return true; } +bool CKeyBindings::Load(const std::string& filename) +{ + const bool ret = LoadInternal(filename); + MaybeBuildHotkeyMap(); + return ret; +} + + +void CKeyBindings::MaybeBuildHotkeyMap() +{ + RECOIL_DETAILED_TRACY_ZONE; + if (!buildHotkeyMap) + return; + + BuildHotkeyMap(); + buildHotkeyMap = false; +} + + void CKeyBindings::BuildHotkeyMap() { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Game/UI/KeyBindings.h b/rts/Game/UI/KeyBindings.h index 04260a7993e..9caf37e6102 100644 --- a/rts/Game/UI/KeyBindings.h +++ b/rts/Game/UI/KeyBindings.h @@ -58,8 +58,16 @@ class CKeyBindings : public CommandReceiver protected: void BuildHotkeyMap(); + // rebuilds the reverse map once, and only if a binding actually changed + void MaybeBuildHotkeyMap(); void DebugActionList(const ActionList& actionList) const; + // the *Internal variants do the work but never rebuild the reverse map; + // the public Load/ExecuteCommand wrappers rebuild once at the end, so every + // outside call path rebuilds exactly once regardless of how it recurses + bool LoadInternal(const std::string& filename = DEFAULT_FILENAME); + bool ExecuteCommandInternal(const std::string& line); + void AddActionToKeyMap(KeyMap& bindings, Action& action); static bool RemoveActionFromKeyMap(const std::string& command, KeyMap& bindings); @@ -90,7 +98,7 @@ class CKeyBindings : public CommandReceiver int fakeMetaKey = -1; int keyChainTimeout = 750; - bool buildHotkeyMap = true; + bool buildHotkeyMap = true; // reverse hotkey map is stale and needs rebuilding bool debugEnabled = false; }; diff --git a/rts/Game/UI/MiniMap.cpp b/rts/Game/UI/MiniMap.cpp index 2f202bffc18..30037a609c1 100644 --- a/rts/Game/UI/MiniMap.cpp +++ b/rts/Game/UI/MiniMap.cpp @@ -457,7 +457,7 @@ void CMiniMap::ConfigCommand(const std::string& line) const bool wantMaximized = (words.size() >= 2) ? !!atoi(words[1].c_str()) : !isMaximized; if (isMaximized != wantMaximized) - ToggleMaximized(StrCaseStr(words[0].c_str(), "maxspect") == 0); + ToggleMaximized(hashStringLower(words[0].c_str()) != hashString("maxspect")); } break; case hashString("mouseevents"): { diff --git a/rts/Game/UI/MouseHandler.cpp b/rts/Game/UI/MouseHandler.cpp index 60640294423..a82b1dc2dc4 100644 --- a/rts/Game/UI/MouseHandler.cpp +++ b/rts/Game/UI/MouseHandler.cpp @@ -305,6 +305,33 @@ void CMouseHandler::MouseMove(int x, int y, int dx, int dy) } +void CMouseHandler::SetButtonEmulated(int button, bool pressed) +{ + if (button < 1 || button > NUM_BUTTONS) + return; + + const bool physicalDown = (SDL_GetMouseState(nullptr, nullptr) & SDL_BUTTON(button)) != 0; + const bool wasDown = physicalDown || buttons[button].emulated; + + buttons[button].emulated = pressed; + + // fire only on an effective (physical-or-emulated) edge, so a real press + // underneath an emulated one doesn't produce a duplicate event + if (pressed && !wasDown) { + MousePress(lastx, lasty, button); + } else if (!pressed && wasDown && !physicalDown) { + MouseRelease(lastx, lasty, button); + } +} + + +void CMouseHandler::ClearEmulatedButtons() +{ + for (int button = 1; button <= NUM_BUTTONS; ++button) + buttons[button].emulated = false; +} + + void CMouseHandler::MousePress(int x, int y, int button) { RECOIL_DETAILED_TRACY_ZONE; @@ -334,6 +361,24 @@ void CMouseHandler::MousePress(int x, int y, int button) pressedBitMask |= 1 << button; + const bool isXButton = (button == SDL_BUTTON_X1 || button == SDL_BUTTON_X2); + if (isXButton) { + + // 1. Lua first + if (luaInputReceiver->MousePress(x, y, button)) { + return; + } + + // 2. GameInputReceiver via the same path as mouse buttons + auto activeControllerReceiver = (activeController == nullptr) ? nullptr : activeController->GetInputReceiver(); + if (activeControllerReceiver && activeControllerReceiver->MousePress(x, y, button)) { + // NOTE: X‑buttons bypass ownership so they can be pressed/released without stealing or confusing activeReceiver + return; + } + return; + } + + if (activeReceiver != nullptr && activeReceiver->MousePress(x, y, button)) return; @@ -503,6 +548,22 @@ void CMouseHandler::MouseRelease(int x, int y, int button) return; } + const bool isXButton = (button == SDL_BUTTON_X1 || button == SDL_BUTTON_X2); + if (isXButton) { + + // 1. Lua first + luaInputReceiver->MouseRelease(x, y, button); + + // 2. GameInputReceiver via the same path as mouse buttons + auto activeControllerReceiver = (activeController == nullptr) ? nullptr : activeController->GetInputReceiver(); + if (activeControllerReceiver) { + activeControllerReceiver->MouseRelease(x, y, button); + } + + // 3. Skip ownership funnel + return; + } + if (activeReceiver != nullptr) { activeReceiver->MouseRelease(x, y, button); @@ -1107,6 +1168,12 @@ bool CMouseHandler::ReplaceMouseCursor( CMouseCursor newCursor = CMouseCursor(newName, hotSpot); + // a replacement that loaded no frames draws nothing, so this silently turns + // a working cursor invisible. Whether the engine should refuse it is a + // behaviour question, since content may rely on it to hide the cursor. + if (!newCursor.IsValid()) + LOG_L(L_WARNING, "[MouseHandler::%s] replacement \"%s\" for \"%s\" has no frames, the cursor will draw nothing", __func__, newName.c_str(), oldName.c_str()); + // replace here so SetCursor() operates with new CMouseCursor() object // hold on the destruction of old CMouseCursor() in this place. Otherwise bad things will happen. std::swap(loadedCursors.at(fileIt->second), newCursor); diff --git a/rts/Game/UI/MouseHandler.h b/rts/Game/UI/MouseHandler.h index b36f4b7deb1..0b8074efdb6 100644 --- a/rts/Game/UI/MouseHandler.h +++ b/rts/Game/UI/MouseHandler.h @@ -52,6 +52,15 @@ class CMouseHandler void MousePress(int x, int y, int button); void MouseMove(int x, int y, int dx, int dy); void MouseWheel(float delta); + + // input emulation (debug.emulateMouse*): a button held independent of hardware. + // SetButtonEmulated fires the press/release itself, on the physical-or-emulated edge + void SetButtonEmulated(int button, bool pressed); + // drop all emulated flags without firing an event (game teardown) + void ClearEmulatedButtons(); + // bounds-guarded: the SDL-event gate calls this with a raw Uint8 button that + // can exceed NUM_BUTTONS on multi-button mice + bool IsButtonEmulated(int button) const { return (button >= 1 && button <= NUM_BUTTONS) && buttons[button].emulated; } void WindowLeave(); void WindowEnter(); @@ -153,6 +162,7 @@ class CMouseHandler struct ButtonPressEvt { bool pressed = false; + bool emulated = false; bool chorded = false; int x = 0; int y = 0; diff --git a/rts/Game/UnsyncedGameCommands.cpp b/rts/Game/UnsyncedGameCommands.cpp index d5b80f5f44f..19a96edd4b7 100644 --- a/rts/Game/UnsyncedGameCommands.cpp +++ b/rts/Game/UnsyncedGameCommands.cpp @@ -1,5 +1,6 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ #include +#include #include #include @@ -253,6 +254,22 @@ class DeselectActionExecutor : public IUnsyncedActionExecutor { +class CancelCommandActionExecutor : public IUnsyncedActionExecutor { +public: + CancelCommandActionExecutor() : IUnsyncedActionExecutor("CancelCommand", "Cancels the active command (build/order mode)") { + } + + bool Execute(const UnsyncedAction& action) const final { + if (guihandler == nullptr) + return false; + + guihandler->CancelActiveCommand(); + return true; + } +}; + + + class MapMeshDrawerActionExecutor : public IUnsyncedActionExecutor { public: MapMeshDrawerActionExecutor() : IUnsyncedActionExecutor("mapmeshdrawer", "Switch map-mesh rendering modes: 0=GCM, 1=HLOD, 2=ROAM") { @@ -4037,6 +4054,7 @@ void UnsyncedGameCommands::AddDefaultActionExecutors() AddActionExecutor(AllocActionExecutor()); AddActionExecutor(AllocActionExecutor()); AddActionExecutor(AllocActionExecutor()); + AddActionExecutor(AllocActionExecutor()); AddActionExecutor(AllocActionExecutor()); AddActionExecutor(AllocActionExecutor()); AddActionExecutor(AllocActionExecutor()); diff --git a/rts/Lua/CMakeLists.txt b/rts/Lua/CMakeLists.txt index 84bb4553c3d..b14d9f088aa 100644 --- a/rts/Lua/CMakeLists.txt +++ b/rts/Lua/CMakeLists.txt @@ -10,6 +10,7 @@ set(sources_engine_Lua "${CMAKE_CURRENT_SOURCE_DIR}/LuaConstEngine.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LuaConstGame.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LuaConstPlatform.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/LuaDebugExtra.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LuaVFSDownload.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LuaEncoding.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LuaFBOs.cpp" diff --git a/rts/Lua/LuaArchive.cpp b/rts/Lua/LuaArchive.cpp index 24ff0727b00..5f85ca9a45b 100644 --- a/rts/Lua/LuaArchive.cpp +++ b/rts/Lua/LuaArchive.cpp @@ -356,21 +356,21 @@ int LuaArchive::GetAvailableAIs(lua_State* L) lua_createtable(L, 0, 3); { for (const auto& luaAIInfoItem: luaAIInfo) { if (luaAIInfoItem.key == SKIRMISH_AI_PROPERTY_SHORT_NAME) { - HSTR_PUSH_STRING(L, "shortName", luaAIInfoItem.GetValueAsString()); + LuaPushNamedString(L, "shortName", luaAIInfoItem.GetValueAsString()); } else if (luaAIInfoItem.key == SKIRMISH_AI_PROPERTY_VERSION) { - HSTR_PUSH_STRING(L, "version", luaAIInfoItem.GetValueAsString()); + LuaPushNamedString(L, "version", luaAIInfoItem.GetValueAsString()); } } - HSTR_PUSH_BOOL(L, "isLuaAI", true); + LuaPushNamedBool(L, "isLuaAI", true); } lua_rawseti(L, -2, ++count); } for (const auto& aiKey: skirmishAIKeys) { lua_createtable(L, 0, 3); { - HSTR_PUSH_STRING(L, "shortName", aiKey.GetShortName()); - HSTR_PUSH_STRING(L, "version", aiKey.GetVersion()); - HSTR_PUSH_BOOL(L, "isLuaAI", false); + LuaPushNamedString(L, "shortName", aiKey.GetShortName()); + LuaPushNamedString(L, "version", aiKey.GetVersion()); + LuaPushNamedBool(L, "isLuaAI", false); } lua_rawseti(L, -2, ++count); } diff --git a/rts/Lua/LuaConstEngine.cpp b/rts/Lua/LuaConstEngine.cpp index 068a26d0ecf..50d042462ed 100644 --- a/rts/Lua/LuaConstEngine.cpp +++ b/rts/Lua/LuaConstEngine.cpp @@ -28,6 +28,7 @@ * @field noHandicapForReclaim boolean Whether handicap is applied to income from reclaim * @field groupAddDoesntSelect boolean Whether 'group add' also selects the group (does both if false) * @field deadTeamsKeepUnitLimit boolean Whether engine redistributes dead team unitlimit to allies (false) or keeps it as-is (true) + * @field reliableLuaMapShaders boolean Whether forward-only Lua map shaders activate without a deferred draw and Spring.SetMapShader program swaps refresh cached uniform locations */ /*** @@ -42,10 +43,9 @@ * @field commitsNumber string Number of commits after the latest named release, non-zero indicates a "dev" build * @field buildFlags string Gets additional engine buildflags, e.g. "Debug" or "Sync-Debug" * @field featureSupport FeatureSupport Table containing various engine features as keys; use for cross-version compat - * @field wordSize number Indicates the build type always 64 these days - * @field gameSpeed number Number of simulation gameframes per second + * @field wordSize integer Indicates the build type always 64 these days + * @field gameSpeed integer Number of simulation gameframes per second * @field textColorCodes TextColorCode Table containing keys that represent the color code operations during font rendering - * @field isHeadless boolean? Whether this is a headless engine build. Not available in synced */ bool LuaConstEngine::PushEntries(lua_State* L) @@ -59,9 +59,6 @@ bool LuaConstEngine::PushEntries(lua_State* L) LuaPushNamedString(L, "buildFlags" , SpringVersion::GetAdditional()); LuaPushNamedNumber(L, "wordSize", (!CLuaHandle::GetHandleSynced(L))? Platform::NativeWordSize() * 8: 0); - if (!CLuaHandle::GetHandleSynced(L)) - LuaPushNamedBool(L, "isHeadless", SpringVersion::IsHeadless()); - LuaPushNamedNumber(L, "gameSpeed", GAME_SPEED); LuaPushNamedNumber(L, "maxCustomPaletteID", MAX_CUSTOM_COLORS - 1); @@ -87,6 +84,7 @@ bool LuaConstEngine::PushEntries(lua_State* L) LuaPushNamedBool(L, "noHandicapForReclaim", true); LuaPushNamedBool(L, "groupAddDoesntSelect", true); LuaPushNamedBool(L, "deadTeamsKeepUnitLimit", false); + LuaPushNamedBool(L, "reliableLuaMapShaders", true); lua_rawset(L, -3); lua_pushliteral(L, "textColorCodes"); diff --git a/rts/Lua/LuaConstGame.cpp b/rts/Lua/LuaConstGame.cpp index 75ff5851e0b..f9e2babafec 100644 --- a/rts/Lua/LuaConstGame.cpp +++ b/rts/Lua/LuaConstGame.cpp @@ -36,13 +36,13 @@ /*** Game specific information * * @table Game - * @field maxUnits number - * @field maxTeams number - * @field maxPlayers number - * @field squareSize number Divide Game.mapSizeX or Game.mapSizeZ by this to get engine's "mapDims" coordinates. The resolution of height, yard and type maps. - * @field metalMapSquareSize number The resolution of metalmap (for use in API such as Spring.GetMetalAmount etc.) - * @field gameSpeed number Number of simulation gameframes per second - * @field startPosType number + * @field maxUnits integer + * @field maxTeams integer + * @field maxPlayers integer + * @field squareSize integer Divide Game.mapSizeX or Game.mapSizeZ by this to get engine's "mapDims" coordinates. The resolution of height, yard and type maps. + * @field metalMapSquareSize integer The resolution of metalmap (for use in API such as Spring.GetMetalAmount etc.) + * @field gameSpeed integer Number of simulation gameframes per second + * @field startPosType integer * @field ghostedBuildings boolean * @field mapChecksum string * @field modChecksum string @@ -50,10 +50,10 @@ * @field mapName string * @field mapDescription string = string Game.mapHumanName * @field mapHardness number - * @field mapX number - * @field mapY number - * @field mapSizeX number in worldspace/opengl coords. Divide by Game.squareSize to get engine's "mapDims" coordinates - * @field mapSizeZ number in worldspace/opengl coords. Divide by Game.squareSize to get engine's "mapDims" coordinates + * @field mapX integer + * @field mapY integer + * @field mapSizeX integer in worldspace/opengl coords. Divide by Game.squareSize to get engine's "mapDims" coordinates + * @field mapSizeZ integer in worldspace/opengl coords. Divide by Game.squareSize to get engine's "mapDims" coordinates * @field gravity number * @field tidal number * @field windMin number @@ -67,20 +67,20 @@ * @field gameMutator string * @field gameDesc string * @field requireSonarUnderWater boolean - * @field transportAir number - * @field transportShip number - * @field transportHover number - * @field transportGround number - * @field fireAtKilled number - * @field fireAtCrashing number + * @field transportAir integer + * @field transportShip integer + * @field transportHover integer + * @field transportGround integer + * @field fireAtKilled integer + * @field fireAtCrashing integer * @field constructionDecay boolean * @field reclaimAllowEnemies boolean * @field reclaimAllowAllies boolean - * @field constructionDecayTime number + * @field constructionDecayTime integer * @field constructionDecaySpeed number - * @field multiReclaim number - * @field reclaimMethod number - * @field reclaimUnitMethod number + * @field multiReclaim integer + * @field reclaimMethod integer + * @field reclaimUnitMethod integer * @field reclaimUnitEnergyCostFactor number * @field reclaimUnitEfficiency number * @field reclaimFeatureEnergyCostFactor number diff --git a/rts/Lua/LuaConstPlatform.cpp b/rts/Lua/LuaConstPlatform.cpp index 0a5f667ed80..705465a7b65 100644 --- a/rts/Lua/LuaConstPlatform.cpp +++ b/rts/Lua/LuaConstPlatform.cpp @@ -2,6 +2,7 @@ #include "LuaConstPlatform.h" #include "LuaUtils.h" +#include "Game/GameVersion.h" #include "System/Platform/Hardware.h" #include "System/Platform/Misc.h" #include "Rendering/GlobalRendering.h" @@ -23,7 +24,7 @@ bool LuaConstPlatform::PushEntries(lua_State* L) LuaPushNamedString(L, "gpu", globalRenderingInfo.gpuName); /*** @field Platform.gpuVendor "Nvidia"|"Intel"|"ATI"|"Mesa"|"Unknown" */ LuaPushNamedString(L, "gpuVendor", globalRenderingInfo.gpuVendor); - /*** @field Platform.gpuMemorySize number Size of total GPU memory in MBs; only available for "Nvidia", (rest are 0) */ + /*** @field Platform.gpuMemorySize integer Size of total GPU memory in MBs; only available for "Nvidia", (rest are 0) */ LuaPushNamedNumber(L, "gpuMemorySize", globalRenderingInfo.gpuMemorySize.x); /*** @field Platform.glVersionShort string `major.minor.buildNumber` */ LuaPushNamedString(L, "glVersionShort", globalRenderingInfo.glVersionShort.data()); @@ -47,17 +48,17 @@ bool LuaConstPlatform::PushEntries(lua_State* L) /*** @field Platform.glewVersion string */ LuaPushNamedString(L, "glewVersion", globalRenderingInfo.gladVersion); - /*** @field Platform.sdlVersionCompiledMajor number */ + /*** @field Platform.sdlVersionCompiledMajor integer */ LuaPushNamedNumber(L, "sdlVersionCompiledMajor", globalRenderingInfo.sdlVersionCompiled.major); - /*** @field Platform.sdlVersionCompiledMinor number */ + /*** @field Platform.sdlVersionCompiledMinor integer */ LuaPushNamedNumber(L, "sdlVersionCompiledMinor", globalRenderingInfo.sdlVersionCompiled.minor); - /*** @field Platform.sdlVersionCompiledPatch number */ + /*** @field Platform.sdlVersionCompiledPatch integer */ LuaPushNamedNumber(L, "sdlVersionCompiledPatch", globalRenderingInfo.sdlVersionCompiled.patch); - /*** @field Platform.sdlVersionLinkedMajor number */ + /*** @field Platform.sdlVersionLinkedMajor integer */ LuaPushNamedNumber(L, "sdlVersionLinkedMajor", globalRenderingInfo.sdlVersionLinked.major); - /*** @field Platform.sdlVersionLinkedMinor number */ + /*** @field Platform.sdlVersionLinkedMinor integer */ LuaPushNamedNumber(L, "sdlVersionLinkedMinor", globalRenderingInfo.sdlVersionLinked.minor); - /*** @field Platform.sdlVersionLinkedPatch number */ + /*** @field Platform.sdlVersionLinkedPatch integer */ LuaPushNamedNumber(L, "sdlVersionLinkedPatch", globalRenderingInfo.sdlVersionLinked.patch); /*** @field Platform.availableVideoModes PlatformVideoMode[] */ @@ -76,13 +77,13 @@ bool LuaConstPlatform::PushEntries(lua_State* L) LuaPushNamedNumber(L, "display", avm.displayIndex); /*** @field PlatformVideoMode.displayName string */ LuaPushNamedString(L, "displayName", avm.displayName); - /*** @field PlatformVideoMode.w number */ + /*** @field PlatformVideoMode.w integer */ LuaPushNamedNumber(L, "w", avm.width); - /*** @field PlatformVideoMode.h number */ + /*** @field PlatformVideoMode.h integer */ LuaPushNamedNumber(L, "h", avm.height); /*** @field PlatformVideoMode.bpp integer */ LuaPushNamedNumber(L, "bpp", avm.bpp); - /*** @field PlatformVideoMode.hz number */ + /*** @field PlatformVideoMode.hz integer */ LuaPushNamedNumber(L, "hz", avm.refreshRate); lua_rawset(L, -3); @@ -111,7 +112,7 @@ bool LuaConstPlatform::PushEntries(lua_State* L) /*** @field Platform.glHaveGL4 boolean */ LuaPushNamedBool(L, "glHaveGL4", globalRendering->haveGL4); - /*** @field Platform.glSupportDepthBufferBitDepth number */ + /*** @field Platform.glSupportDepthBufferBitDepth integer */ LuaPushNamedNumber(L, "glSupportDepthBufferBitDepth", globalRendering->supportDepthBufferBitDepth); /*** @field Platform.glSupportRestartPrimitive boolean */ @@ -147,5 +148,15 @@ bool LuaConstPlatform::PushEntries(lua_State* L) /*** @field Platform.macAddrHash string */ LuaPushNamedString(L, "macAddrHash", Platform::GetMacAddrHash()); + /*** @field Platform.isHeadless boolean Is this a headless build which only simulates and doesnt offer interactive IO? */ + LuaPushNamedBool(L, "isHeadless", SpringVersion::IsHeadless()); + + /*** @field Platform.hasSyncChecksums boolean Whether the engine was built with sync-check support (i.e. Spring.GetPrevFrameSyncChecksum() returns a meaningful value). */ + #ifdef SYNCCHECK + LuaPushNamedBool(L, "hasSyncChecksums", true); + #else + LuaPushNamedBool(L, "hasSyncChecksums", false); + #endif + return true; } diff --git a/rts/Lua/LuaDebugExtra.cpp b/rts/Lua/LuaDebugExtra.cpp new file mode 100644 index 00000000000..1c1a35377d5 --- /dev/null +++ b/rts/Lua/LuaDebugExtra.cpp @@ -0,0 +1,251 @@ +/* This file is part of the Recoil engine (GPL v2 or later), see LICENSE.html */ + +#include "LuaDebugExtra.h" + +#include "LuaInclude.h" +#include "LuaUtils.h" + +#include "Game/GameController.h" +#include "Game/UI/KeyBindings.h" +#include "Game/UI/KeyCodes.h" +#include "Game/UI/ScanCodes.h" +#include "Game/UI/MouseHandler.h" +#include "Rendering/GlobalRendering.h" +#include "System/Input/KeyInput.h" +#include "System/Input/MouseInput.h" +#include "System/Platform/SDL1_keysym.h" + +#include + +#include +#include +#include + + +/****************************************************************************** + * debug input emulation + * + * Callouts that feed input to the engine as if it came from real hardware, + * for headless regression tests. Emulated presses are held in a separate store + * and OR'd into the real input state; an event fires only when the combined + * (physical-or-emulated) state actually changes, so Lua never sees two Presses + * or two Releases in a row. + * + * No engine-side access gate: the property that must not regress (no doubled + * events) is structural, not access-controlled. A game that wants to restrict + * these nils them out. + * + * @see rts/Lua/LuaDebugExtra.cpp +******************************************************************************/ + +bool LuaDebugExtra::PushEntries(lua_State* L) +{ + LuaPushNamedCFunc(L, "emulateKeyPress", EmulateKeyPress); + LuaPushNamedCFunc(L, "emulateKeyRelease", EmulateKeyRelease); + LuaPushNamedCFunc(L, "emulateMousePress", EmulateMousePress); + LuaPushNamedCFunc(L, "emulateMouseRelease", EmulateMouseRelease); + LuaPushNamedCFunc(L, "emulateMouseMove", EmulateMouseMove); + LuaPushNamedCFunc(L, "emulateMouseWheel", EmulateMouseWheel); + LuaPushNamedCFunc(L, "clearEmulatedInput", ClearEmulatedInputLua); + + return true; +} + + +// shared body for emulateKeyPress/emulateKeyRelease; only the store update and +// the edge-fire differ, keyed on `pressed` +static int emulateKey(lua_State* L, bool pressed) +{ + if (activeController == nullptr) + return 0; + + // Lua passes SDL1.2 keysyms; the held-state side (keyVec/IsKeyPressed) works in + // raw SDL2, while the event side wants the normalized code like a real KEYDOWN + const int rawKey = SDL12_keysyms(luaL_checkint(L, 1)); + + // reject a junk keycode (unmapped -> SDLK_UNKNOWN). We deliberately do NOT + // reject on an unknown scancode: headless has no keyboard layout, so + // SDL_GetScancodeFromKey returns SDL_SCANCODE_UNKNOWN even for valid keys + if (rawKey == SDLK_UNKNOWN) + return 0; + + const SDL_Scancode sc = SDL_GetScancodeFromKey((SDL_Keycode)rawKey); + const int eventKey = CKeyCodes::GetNormalizedSymbol(rawKey); + const int scanCode = CScanCodes::GetNormalizedSymbol(sc); + + int numKeys = 0; + const uint8_t* kbState = SDL_GetKeyboardState(&numKeys); + const bool physicalDown = ((int)sc < numKeys && kbState[sc] != 0); + + // effective (physical-or-emulated) state before this call + const bool wasDown = physicalDown || KeyInput::IsKeyEmulated(rawKey); + + KeyInput::SetKeyEmulated(rawKey, pressed); + KeyInput::Update(keyBindings.GetFakeMetaKey()); + + if (pressed) { + // fire only on a false->true edge + if (!wasDown) + activeController->KeyPressed(eventKey, scanCode, false); + } else { + // effective after = physical; fire only on a true->false edge + if (wasDown && !physicalDown) + activeController->KeyReleased(eventKey, scanCode); + } + + return 0; +} + + +/*** Emulate a keyboard key being pressed and held. + * + * Fires the KeyPress event and holds the key down (merged with real hardware + * state) until released or cleared. The accompanying scancode is derived from + * the keycode using the currently active system keyboard layout. + * + * @function debug.emulateKeyPress + * @param keycode integer + * @return nil + */ +int LuaDebugExtra::EmulateKeyPress(lua_State* L) { return emulateKey(L, true); } + + +/*** Emulate a held keyboard key being released. + * + * @function debug.emulateKeyRelease + * @param keycode integer + * @return nil + */ +int LuaDebugExtra::EmulateKeyRelease(lua_State* L) { return emulateKey(L, false); } + + +/*** Emulate a mouse button being pressed and held. + * + * @function debug.emulateMousePress + * @param button integer + * @return nil + */ +int LuaDebugExtra::EmulateMousePress(lua_State* L) +{ + if (mouse == nullptr) + return 0; + + const int button = luaL_checkint(L, 1); + + if (button < 1 || button > NUM_BUTTONS) + return 0; + + mouse->SetButtonEmulated(button, true); + return 0; +} + +int LuaDebugExtra::EmulateMouseWheel(lua_State* L) +{ + if (mouse == nullptr) + return 0; + + // momentary tick, no persistent state to track; fire directly like a real wheel event + mouse->MouseWheel((float)luaL_checknumber(L, 1)); + return 0; +} + + +/*** Emulate a held mouse button being released. + * + * @function debug.emulateMouseRelease + * @param button integer + * @return nil + */ +int LuaDebugExtra::EmulateMouseRelease(lua_State* L) +{ + if (mouse == nullptr) + return 0; + + const int button = luaL_checkint(L, 1); + + if (button < 1 || button > NUM_BUTTONS) + return 0; + + mouse->SetButtonEmulated(button, false); + return 0; +} + + +/*** Emulate the cursor moving to a screen position. + * + * Fires a MouseMove through the normal pipeline. Coordinates use the bottom-left + * origin like the rest of the Lua screen API. Does not move the OS cursor. + * + * @function debug.emulateMouseMove + * @param x integer + * @param y integer + * @return nil + */ +int LuaDebugExtra::EmulateMouseMove(lua_State* L) +{ + if (mouse == nullptr || mouseInput == nullptr) + return 0; + + const int x = luaL_checkint(L, 1); + const int y = globalRendering->viewSizeY - luaL_checkint(L, 2) - 1; + + const int2 prev = mouseInput->GetPos(); + mouseInput->SetPos(int2(x, y)); + mouse->MouseMove(x, y, x - prev.x, y - prev.y); + + return 0; +} + + +/*** Release everything currently held via emulation. + * + * @function debug.clearEmulatedInput + * @return nil + */ +int LuaDebugExtra::ClearEmulatedInputLua(lua_State* L) +{ + ClearEmulatedInput(); + return 0; +} + + +void LuaDebugExtra::ClearEmulatedInput(bool fireReleases) +{ + // snapshot the emulated keys before clearing, so a fired release can't walk + // the store we are emptying + const std::set keyCodes = KeyInput::GetEmulatedKeys(); + + KeyInput::ClearEmulatedKeys(); + KeyInput::Update(keyBindings.GetFakeMetaKey()); + + if (fireReleases && activeController != nullptr) { + int numKeys = 0; + const uint8_t* kbState = SDL_GetKeyboardState(&numKeys); + + // the store holds raw SDL2 keycodes; the event side wants the normalized code + for (const int rawKey: keyCodes) { + const SDL_Scancode sc = SDL_GetScancodeFromKey((SDL_Keycode)rawKey); + + if ((int)sc < numKeys && kbState[sc] != 0) + continue; + + activeController->KeyReleased(CKeyCodes::GetNormalizedSymbol(rawKey), CScanCodes::GetNormalizedSymbol(sc)); + } + } + + if (mouse == nullptr) + return; + + // no-fire path (game teardown): drop the flags without dispatching into + // handles that are already being destroyed + if (!fireReleases) { + mouse->ClearEmulatedButtons(); + return; + } + + // SetButtonEmulated fires the release itself if the button ends up effectively up + for (int button = 1; button <= NUM_BUTTONS; ++button) { + if (mouse->IsButtonEmulated(button)) + mouse->SetButtonEmulated(button, false); + } +} diff --git a/rts/Lua/LuaDebugExtra.h b/rts/Lua/LuaDebugExtra.h new file mode 100644 index 00000000000..1d552c08615 --- /dev/null +++ b/rts/Lua/LuaDebugExtra.h @@ -0,0 +1,28 @@ +/* This file is part of the Recoil engine (GPL v2 or later), see LICENSE.html */ + +#ifndef LUA_DEBUG_EXTRA_H +#define LUA_DEBUG_EXTRA_H + +struct lua_State; + +class LuaDebugExtra { + public: + static bool PushEntries(lua_State* L); + + // drops all emulated key/button state; with fireReleases (default) it also + // dispatches balancing KeyReleased/MouseRelease. Shared by + // debug.clearEmulatedInput, the focus-loss handler (fire), and game teardown + // (no fire - the handles are being destroyed). + static void ClearEmulatedInput(bool fireReleases = true); + + private: + static int EmulateKeyPress(lua_State* L); + static int EmulateKeyRelease(lua_State* L); + static int EmulateMousePress(lua_State* L); + static int EmulateMouseRelease(lua_State* L); + static int EmulateMouseMove(lua_State* L); + static int EmulateMouseWheel(lua_State* L); + static int ClearEmulatedInputLua(lua_State* L); +}; + +#endif /* LUA_DEBUG_EXTRA_H */ diff --git a/rts/Lua/LuaDefs.h b/rts/Lua/LuaDefs.h index 091f2bb5f6e..2959e058afd 100644 --- a/rts/Lua/LuaDefs.h +++ b/rts/Lua/LuaDefs.h @@ -60,16 +60,18 @@ namespace { // Requires a "start" address, use ADDRESS() #define ADD_INT(lua, cpp) \ + static_assert(std::is_integral_v, \ + "Lua API break! Add DataElement manually"); \ paramMap[lua] = DataElement(GetDataType(cpp), ADDRESS(cpp) - start) -#define ADD_BOOL(lua, cpp) \ +#define ADD_SIMPLE_DATA(lua, cpp, expected_type) \ + static_assert(std::is_same_v, \ + "Lua API break! Add DataElement manually"); \ paramMap[lua] = DataElement(GetDataType(cpp), ADDRESS(cpp) - start) -#define ADD_FLOAT(lua, cpp) \ - paramMap[lua] = DataElement(GetDataType(cpp), ADDRESS(cpp) - start) - -#define ADD_STRING(lua, cpp) \ - paramMap[lua] = DataElement(GetDataType(cpp), ADDRESS(cpp) - start) +#define ADD_BOOL(lua, cpp) ADD_SIMPLE_DATA(lua, cpp, bool) +#define ADD_FLOAT(lua, cpp) ADD_SIMPLE_DATA(lua, cpp, float) +#define ADD_STRING(lua, cpp) ADD_SIMPLE_DATA(lua, cpp, std::string) #define ADD_FUNCTION(lua, cpp, func) \ paramMap[lua] = DataElement(FUNCTION_TYPE, ADDRESS(cpp) - start, func) diff --git a/rts/Lua/LuaFBOs.cpp b/rts/Lua/LuaFBOs.cpp index 7269bf5e9a6..36cccbf714e 100644 --- a/rts/Lua/LuaFBOs.cpp +++ b/rts/Lua/LuaFBOs.cpp @@ -64,9 +64,9 @@ bool LuaFBOs::CreateMetatable(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; luaL_newmetatable(L, "FBO"); - HSTR_PUSH_CFUNC(L, "__gc", meta_gc); - HSTR_PUSH_CFUNC(L, "__index", meta_index); - HSTR_PUSH_CFUNC(L, "__newindex", meta_newindex); + LuaPushNamedCFunc(L, "__gc", meta_gc); + LuaPushNamedCFunc(L, "__index", meta_index); + LuaPushNamedCFunc(L, "__newindex", meta_newindex); lua_pop(L, 1); return true; } @@ -563,7 +563,7 @@ int LuaFBOs::DeleteFBO(lua_State* L) * @param fbo FBO * @param target GL? * @return boolean valid - * @return number? status + * @return GL? status */ int LuaFBOs::IsValidFBO(lua_State* L) { @@ -688,7 +688,7 @@ int LuaFBOs::ActiveFBO(lua_State* L) * @function gl.RawBindFBO * @param fbo FBO * @param target GL? (Default: `fbo.target`) - * @return number previouslyBoundRawFboId + * @return integer previouslyBoundRawFboId */ int LuaFBOs::RawBindFBO(lua_State* L) { @@ -720,32 +720,32 @@ int LuaFBOs::RawBindFBO(lua_State* L) /*** needs `GLAD_GL_EXT_framebuffer_blit` * * @function gl.BlitFBO - * @param x0Src number - * @param y0Src number - * @param x1Src number - * @param y1Src number - * @param x0Dst number - * @param y0Dst number - * @param x1Dst number - * @param y1Dst number - * @param mask number? (Default: GL_COLOR_BUFFER_BIT) - * @param filter number? (Default: GL_NEAREST) + * @param x0Src integer + * @param y0Src integer + * @param x1Src integer + * @param y1Src integer + * @param x0Dst integer + * @param y0Dst integer + * @param x1Dst integer + * @param y1Dst integer + * @param mask GL? (Default: GL_COLOR_BUFFER_BIT) + * @param filter GL? (Default: GL_NEAREST) */ /*** needs `GLAD_GL_EXT_framebuffer_blit` * * @function gl.BlitFBO * @param fboSrc FBO - * @param x0Src number - * @param y0Src number - * @param x1Src number - * @param y1Src number + * @param x0Src integer + * @param y0Src integer + * @param x1Src integer + * @param y1Src integer * @param fboDst FBO - * @param x0Dst number - * @param y0Dst number - * @param x1Dst number - * @param y1Dst number - * @param mask number? (Default: GL_COLOR_BUFFER_BIT) - * @param filter number? (Default: GL_NEAREST) + * @param x0Dst integer + * @param y0Dst integer + * @param x1Dst integer + * @param y1Dst integer + * @param mask GL? (Default: GL_COLOR_BUFFER_BIT) + * @param filter GL? (Default: GL_NEAREST) */ int LuaFBOs::BlitFBO(lua_State* L) { @@ -820,7 +820,7 @@ namespace Impl { * Clears the "attachment" of the currently bound FBO type "target" with "clearValues" * * @function gl.ClearAttachmentFBO - * @param target number? (Default: `GL.FRAMEBUFFER`) + * @param target GL? (Default: `GL.FRAMEBUFFER`) * @param attachment GL|Attachment (e.g. `"color0"` or `GL.COLOR_ATTACHMENT0`) * @param clearValue0 number? (Default: `0`) * @param clearValue1 number? (Default: `0`) diff --git a/rts/Lua/LuaFeatureDefs.cpp b/rts/Lua/LuaFeatureDefs.cpp index 54c4a8981bc..2bde825f395 100644 --- a/rts/Lua/LuaFeatureDefs.cpp +++ b/rts/Lua/LuaFeatureDefs.cpp @@ -83,17 +83,17 @@ bool LuaFeatureDefs::PushEntries(lua_State* L) lua_newtable(L); { // the metatable - HSTR_PUSH(L, "__index"); + LuaPushString(L, "__index"); lua_pushlightuserdata(L, (void*)fd); lua_pushcclosure(L, FeatureDefIndex, 1); lua_rawset(L, -3); // closure - HSTR_PUSH(L, "__newindex"); + LuaPushString(L, "__newindex"); lua_pushlightuserdata(L, (void*)fd); lua_pushcclosure(L, FeatureDefNewIndex, 1); lua_rawset(L, -3); - HSTR_PUSH(L, "__metatable"); + LuaPushString(L, "__metatable"); lua_pushlightuserdata(L, (void*)fd); lua_pushcclosure(L, FeatureDefMetatable, 1); lua_rawset(L, -3); @@ -102,11 +102,11 @@ bool LuaFeatureDefs::PushEntries(lua_State* L) lua_setmetatable(L, -2); } - HSTR_PUSH(L, "pairs"); + LuaPushString(L, "pairs"); lua_pushcfunction(L, Pairs); lua_rawset(L, -3); - HSTR_PUSH(L, "next"); + LuaPushString(L, "next"); lua_pushcfunction(L, Next); lua_rawset(L, -3); diff --git a/rts/Lua/LuaFonts.cpp b/rts/Lua/LuaFonts.cpp index 05a0b3d0f55..69c9f64baba 100644 --- a/rts/Lua/LuaFonts.cpp +++ b/rts/Lua/LuaFonts.cpp @@ -46,8 +46,8 @@ bool LuaFonts::CreateMetatable(lua_State* L) RECOIL_DETAILED_TRACY_ZONE; luaL_newmetatable(L, "Font"); - HSTR_PUSH_CFUNC(L, "__gc", meta_gc); - HSTR_PUSH_CFUNC(L, "__index", meta_index); + LuaPushNamedCFunc(L, "__gc", meta_gc); + LuaPushNamedCFunc(L, "__index", meta_index); LuaPushNamedString(L, "__metatable", "protected metatable"); // push userdata callouts @@ -192,6 +192,15 @@ int LuaFonts::meta_index(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Load a font from a file. + * + * @function gl.LoadFont + * @param fontFile string + * @param size integer? + * @param outlineWidth integer? + * @param outlineWeight number? + * @return LuaFont font + */ int LuaFonts::LoadFont(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -208,6 +217,11 @@ int LuaFonts::LoadFont(lua_State* L) } +/*** Delete a font object. + * + * @function gl.DeleteFont + * @param font LuaFont + */ int LuaFonts::DeleteFont(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -261,6 +275,15 @@ int LuaFonts::ClearFallbackFonts(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Draws text in screen space at the given position. + * + * @function LuaFont:Print + * @param text string + * @param x number + * @param y number + * @param size number? Defaults to the font's point size. + * @param options string? Flag characters for alignment, outline, shadow, scaling, etc. (e.g. `"co"` for center and outline). + */ int LuaFonts::Print(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -309,6 +332,16 @@ int LuaFonts::Print(lua_State* L) return 0; } +/*** Draws text in world space at the given position. + * + * @function LuaFont:PrintWorld + * @param text string + * @param x number + * @param y number + * @param z number + * @param size number? Defaults to the font's point size. + * @param options string? Flag characters for alignment, outline, shadow, scaling, etc. (e.g. `"co"` for center and outline). + */ int LuaFonts::PrintWorld(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -388,6 +421,10 @@ int LuaFonts::Begin(lua_State* L) return 0; } +/*** Ends a font command block started with `LuaFont:Begin`. + * + * @function LuaFont:End + */ int LuaFonts::End(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -426,6 +463,16 @@ int LuaFonts::SubmitBuffered(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Wraps text to fit within a maximum width (and optional max height), in-place. + * + * @function LuaFont:WrapText + * @param text string + * @param maxWidth number + * @param maxHeight number? Defaults to an engine-defined maximum height. + * @param size number? Defaults to the font's point size. + * @return string wrappedText + * @return integer lineCount + */ int LuaFonts::WrapText(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -447,6 +494,12 @@ int LuaFonts::WrapText(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Returns the horizontal extent of a string for this font at its current size. + * + * @function LuaFont:GetTextWidth + * @param text string + * @return number width + */ int LuaFonts::GetTextWidth(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -457,6 +510,14 @@ int LuaFonts::GetTextWidth(lua_State* L) } +/*** Returns layout metrics for a string: total height, descender depth, and line count. + * + * @function LuaFont:GetTextHeight + * @param text string + * @return number height + * @return number descender + * @return integer lines + */ int LuaFonts::GetTextHeight(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -508,10 +569,26 @@ static int SetTextColorShared(lua_State* L, bool outline) return 0; } +/*** Sets the RGBA color used when drawing text (fill). + * + * @function LuaFont:SetTextColor + * @param color table Four-component RGBA array (`{r, g, b, a}`), or pass `r`, `g`, `b`, and optional `a` as separate numbers (requires at least three numeric components after the font). + */ int LuaFonts::SetTextColor(lua_State* L) { return (SetTextColorShared(L, false)); } + +/*** Sets the RGBA color used for text outline when outline rendering is enabled. + * + * @function LuaFont:SetOutlineColor + * @param color table Four-component RGBA array (`{r, g, b, a}`), or pass `r`, `g`, `b`, and optional `a` as separate numbers (requires at least three numeric components after the font). + */ int LuaFonts::SetOutlineColor(lua_State* L) { return (SetTextColorShared(L, true)); } +/*** When enabled, outline color is derived automatically instead of using `SetOutlineColor`. + * + * @function LuaFont:SetAutoOutlineColor + * @param enabled boolean + */ int LuaFonts::SetAutoOutlineColor(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Lua/LuaHandle.cpp b/rts/Lua/LuaHandle.cpp index 650a810499d..fcfadffeb00 100644 --- a/rts/Lua/LuaHandle.cpp +++ b/rts/Lua/LuaHandle.cpp @@ -41,6 +41,8 @@ #include "Sim/Units/UnitDef.h" #include "Sim/Weapons/Weapon.h" #include "Sim/Weapons/WeaponDef.h" +#include "System/FileSystem/FileHandler.h" +#include "System/FileSystem/VFSModes.h" #include "System/creg/SerializeLuaState.h" #include "System/Config/ConfigHandler.h" #include "System/EventHandler.h" @@ -56,6 +58,8 @@ #include "LuaInclude.h" +#include "lib/luasocket/src/luasocket.h" + #include #include #include @@ -116,7 +120,7 @@ void CLuaHandle::PushTracebackFuncToRegistry(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; SPRING_LUA_OPEN_LIB(L, luaopen_debug); - HSTR_PUSH(L, "traceback"); + LuaPushString(L, "traceback"); LuaUtils::PushDebugTraceback(L); lua_rawset(L, LUA_REGISTRYINDEX); // We only need the debug.traceback function, the others are unsafe for syncing. @@ -549,6 +553,28 @@ bool CLuaHandle::LoadCode(lua_State* L, std::string code, const string& debug) } +void CLuaHandle::InitLuaSocket(lua_State* L) +{ + RECOIL_DETAILED_TRACY_ZONE; + + const std::string filename = "LuaSocket/socket.lua"; + CFileHandler f(filename, SPRING_VFS_BASE); + if (!f.FileExists()) { + LOG_L(L_ERROR, "Error loading %s (file does not exist)", filename.c_str()); + return; + } + + LUA_OPEN_LIB(L, luaopen_socket_core); + + std::string code; + if (f.LoadStringData(code)) { + LoadCode(L, std::move(code), filename); + } else { + LOG_L(L_ERROR, "Error loading %s", filename.c_str()); + } +} + + int CLuaHandle::LoadStringData(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -604,7 +630,7 @@ void CLuaHandle::Shutdown() * * @function Callins:GotChatMsg * @param msg string - * @param playerID integer + * @param playerID PlayerID */ bool CLuaHandle::GotChatMsg(const string& msg, int playerID) { @@ -679,11 +705,6 @@ bool CLuaHandle::HasCallIn(lua_State* L, const string& name) const return found; } - -/*** - * @function Script.UpdateCallin - * @param name string - */ bool CLuaHandle::UpdateCallIn(lua_State* L, const string& name) { RECOIL_DETAILED_TRACY_ZONE; @@ -748,7 +769,7 @@ void CLuaHandle::GameStart() /*** Called when the game ends * * @function Callins:GameOver - * @param winningAllyTeams number[] list of winning allyTeams, if empty the game result was undecided (like when dropping from an host). + * @param winningAllyTeams AllyTeamID[] list of winning allyTeams, if empty the game result was undecided (like when dropping from an host). */ void CLuaHandle::GameOver(const std::vector& winningAllyTeams) { @@ -775,7 +796,7 @@ void CLuaHandle::GameOver(const std::vector& winningAllyTeams) /*** Called when the game is paused. * * @function Callins:GamePaused - * @param playerID integer + * @param playerID PlayerID * @param paused boolean */ void CLuaHandle::GamePaused(int playerID, bool paused) @@ -825,7 +846,7 @@ void CLuaHandle::RunDelayedFunctions(int frameNum) /*** Called for every game simulation frame (30 per second). * * @function Callins:GameFrame - * @param frame number Starts at frame 1 + * @param frame integer Starts at frame 1 */ void CLuaHandle::GameFrame(int frameNum) { @@ -859,7 +880,7 @@ void CLuaHandle::GameFrame(int frameNum) /*** Called at the end of every game simulation frame * * @function Callins:GameFramePost - * @param frame number Starts at frame 1 + * @param frame integer Starts at frame 1 */ void CLuaHandle::GameFramePost(int frameNum) { @@ -914,7 +935,7 @@ void CLuaHandle::GameID(const unsigned char* gameID, unsigned int numBytes) /*** Called when a team dies (see `Spring.KillTeam`). * * @function Callins:TeamDied - * @param teamID integer + * @param teamID TeamID */ void CLuaHandle::TeamDied(int teamID) { @@ -937,7 +958,7 @@ void CLuaHandle::TeamDied(int teamID) /*** @function Callins:TeamChanged * - * @param teamID integer + * @param teamID TeamID */ void CLuaHandle::TeamChanged(int teamID) { @@ -961,7 +982,7 @@ void CLuaHandle::TeamChanged(int teamID) /*** Called whenever a player's status changes e.g. becoming a spectator. * * @function Callins:PlayerChanged - * @param playerID integer + * @param playerID PlayerID */ void CLuaHandle::PlayerChanged(int playerID) { @@ -985,7 +1006,7 @@ void CLuaHandle::PlayerChanged(int playerID) /*** Called whenever a new player joins the game. * * @function Callins:PlayerAdded - * @param playerID integer + * @param playerID PlayerID */ void CLuaHandle::PlayerAdded(int playerID) { @@ -1009,8 +1030,8 @@ void CLuaHandle::PlayerAdded(int playerID) /*** Called whenever a player is removed from the game. * * @function Callins:PlayerRemoved - * @param playerID integer - * @param reason string + * @param playerID PlayerID + * @param reason integer */ void CLuaHandle::PlayerRemoved(int playerID, int reason) { @@ -1059,10 +1080,10 @@ inline void CLuaHandle::UnitCallIn(const LuaHashString& hs, const CUnit* unit) /*** Called at the moment the unit is created. * * @function Callins:UnitCreated - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer - * @param builderID integer? + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID + * @param builderID UnitID? */ void CLuaHandle::UnitCreated(const CUnit* unit, const CUnit* builder) { @@ -1090,9 +1111,9 @@ void CLuaHandle::UnitCreated(const CUnit* unit, const CUnit* builder) /*** Called at the moment the unit is completed. * * @function Callins:UnitFinished - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitFinished(const CUnit* unit) { @@ -1104,11 +1125,11 @@ void CLuaHandle::UnitFinished(const CUnit* unit) /*** Called when a factory finishes construction of a unit. * * @function Callins:UnitFromFactory - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer - * @param factID integer - * @param factDefID integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID + * @param factID UnitID + * @param factDefID UnitDefID * @param userOrders boolean */ void CLuaHandle::UnitFromFactory(const CUnit* unit, @@ -1138,9 +1159,9 @@ void CLuaHandle::UnitFromFactory(const CUnit* unit, /*** Called when a living unit becomes a nanoframe again. * * @function Callins:UnitReverseBuilt - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitReverseBuilt(const CUnit* unit) { @@ -1153,9 +1174,9 @@ void CLuaHandle::UnitReverseBuilt(const CUnit* unit) /*** Called when a unit being built starts decaying. * * @function Callins:UnitConstructionDecayed - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID * @param timeSinceLastBuild number * @param iterationPeriod number * @param part number @@ -1186,13 +1207,13 @@ void CLuaHandle::UnitConstructionDecayed(const CUnit* unit, float timeSinceLastB /*** Called when a unit is destroyed. * * @function Callins:UnitDestroyed - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer - * @param attackerID integer? Subject to visibility rules - * @param attackerDefID integer? Subject to visibility rules - * @param attackerTeam integer? Subject to visibility rules - * @param weaponDefID integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID + * @param attackerID UnitID? Subject to visibility rules + * @param attackerDefID UnitDefID? Subject to visibility rules + * @param attackerTeam TeamID? Subject to visibility rules + * @param weaponDefID WeaponDefID */ void CLuaHandle::UnitDestroyed(const CUnit* unit, const CUnit* attacker, int weaponDefID) { @@ -1224,10 +1245,10 @@ void CLuaHandle::UnitDestroyed(const CUnit* unit, const CUnit* attacker, int wea /*** Called when a unit is transferred between teams. This is called before `UnitGiven` and in that moment unit is still assigned to the oldTeam. * * @function Callins:UnitTaken - * @param unitID integer - * @param unitDefID integer - * @param oldTeam number - * @param newTeam number + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param oldTeam TeamID + * @param newTeam TeamID */ void CLuaHandle::UnitTaken(const CUnit* unit, int oldTeam, int newTeam) { @@ -1253,10 +1274,10 @@ void CLuaHandle::UnitTaken(const CUnit* unit, int oldTeam, int newTeam) /*** Called when a unit is transferred between teams. This is called after `UnitTaken` and in that moment unit is assigned to the newTeam. * * @function Callins:UnitGiven - * @param unitID integer - * @param unitDefID integer - * @param newTeam number - * @param oldTeam number + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param newTeam TeamID + * @param oldTeam TeamID */ void CLuaHandle::UnitGiven(const CUnit* unit, int oldTeam, int newTeam) { @@ -1282,9 +1303,9 @@ void CLuaHandle::UnitGiven(const CUnit* unit, int oldTeam, int newTeam) /*** Called when a unit is idle (empty command queue). * * @function Callins:UnitIdle - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitIdle(const CUnit* unit) { @@ -1296,13 +1317,13 @@ void CLuaHandle::UnitIdle(const CUnit* unit) /*** Called after when a unit accepts a command, after `AllowCommand` returns true. * * @function Callins:UnitCommand - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID * @param cmdID integer * @param cmdParams table * @param options CommandOptions - * @param cmdTag number + * @param cmdTag integer */ void CLuaHandle::UnitCommand(const CUnit* unit, const Command& command, int playerNum, bool fromSynced, bool fromLua) { @@ -1330,13 +1351,13 @@ void CLuaHandle::UnitCommand(const CUnit* unit, const Command& command, int play /*** Called when a unit completes a command. * * @function Callins:UnitCmdDone - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID * @param cmdID integer * @param cmdParams table * @param options CommandOptions - * @param cmdTag number + * @param cmdTag integer */ void CLuaHandle::UnitCmdDone(const CUnit* unit, const Command& command) { @@ -1360,16 +1381,16 @@ void CLuaHandle::UnitCmdDone(const CUnit* unit, const Command& command) /*** Called when a unit is damaged (after UnitPreDamaged). * * @function Callins:UnitDamaged - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID * @param damage number * @param paralyzer number - * @param weaponDefID integer - * @param projectileID integer - * @param attackerID integer - * @param attackerDefID integer - * @param attackerTeam number + * @param weaponDefID WeaponDefID + * @param projectileID ProjectileID + * @param attackerID UnitID + * @param attackerDefID UnitDefID + * @param attackerTeam TeamID */ void CLuaHandle::UnitDamaged( const CUnit* unit, @@ -1408,9 +1429,9 @@ void CLuaHandle::UnitDamaged( /*** Called when a unit changes its stun status. * * @function Callins:UnitStunned - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID * @param stunned boolean */ void CLuaHandle::UnitStunned( @@ -1443,9 +1464,9 @@ void CLuaHandle::UnitStunned( * * @function Callins:UnitExperience * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID * @param experience number * @param oldExperience number */ @@ -1475,9 +1496,9 @@ void CLuaHandle::UnitExperience(const CUnit* unit, float oldExperience) /*** Called when a unit's harvestStorage is full (according to its unitDef's entry). * * @function Callins:UnitHarvestStorageFull - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitHarvestStorageFull(const CUnit* unit) { @@ -1498,9 +1519,9 @@ void CLuaHandle::UnitHarvestStorageFull(const CUnit* unit) * @param y number * @param z number * @param strength number - * @param allyTeam integer - * @param unitID integer - * @param unitDefID integer + * @param allyTeam AllyTeamID + * @param unitID UnitID + * @param unitDefID UnitDefID */ void CLuaHandle::UnitSeismicPing(const CUnit* unit, int allyTeam, const float3& pos, float strength) @@ -1561,10 +1582,10 @@ void CLuaHandle::LosCallIn(const LuaHashString& hs, * Also called when a unit enters LOS without any radar coverage. * * @function Callins:UnitEnteredRadar - * @param unitID integer - * @param unitTeam integer - * @param allyTeam integer - * @param unitDefID integer + * @param unitID UnitID + * @param unitTeam TeamID + * @param allyTeam AllyTeamID + * @param unitDefID UnitDefID */ void CLuaHandle::UnitEnteredRadar(const CUnit* unit, int allyTeam) { @@ -1580,10 +1601,10 @@ void CLuaHandle::UnitEnteredRadar(const CUnit* unit, int allyTeam) * Its called after the unit is in LOS, so you can query that unit. * * @function Callins:UnitEnteredLos - * @param unitID integer - * @param unitTeam integer - * @param allyTeam integer who's LOS the unit entered. - * @param unitDefID integer + * @param unitID UnitID + * @param unitTeam TeamID + * @param allyTeam AllyTeamID who's LOS the unit entered. + * @param unitDefID UnitDefID */ void CLuaHandle::UnitEnteredLos(const CUnit* unit, int allyTeam) { @@ -1600,10 +1621,10 @@ void CLuaHandle::UnitEnteredLos(const CUnit* unit, int allyTeam) * widgets cannot get the position of units that left their radar. * * @function Callins:UnitLeftRadar - * @param unitID integer - * @param unitTeam integer - * @param allyTeam integer - * @param unitDefID integer + * @param unitID UnitID + * @param unitTeam TeamID + * @param allyTeam AllyTeamID + * @param unitDefID UnitDefID */ void CLuaHandle::UnitLeftRadar(const CUnit* unit, int allyTeam) { @@ -1619,10 +1640,10 @@ void CLuaHandle::UnitLeftRadar(const CUnit* unit, int allyTeam) * For widgets, this one is called just before the unit leaves los, so you can still get the position of a unit that left los. * * @function Callins:UnitLeftLos - * @param unitID integer - * @param unitTeam integer - * @param allyTeam integer - * @param unitDefID integer + * @param unitID UnitID + * @param unitTeam TeamID + * @param allyTeam AllyTeamID + * @param unitDefID UnitDefID */ void CLuaHandle::UnitLeftLos(const CUnit* unit, int allyTeam) { @@ -1640,11 +1661,11 @@ void CLuaHandle::UnitLeftLos(const CUnit* unit, int allyTeam) /*** Called when a unit is loaded by a transport. * * @function Callins:UnitLoaded - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer - * @param transportID integer - * @param transportTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID + * @param transportID UnitID + * @param transportTeam TeamID */ void CLuaHandle::UnitLoaded(const CUnit* unit, const CUnit* transport) { @@ -1672,11 +1693,11 @@ void CLuaHandle::UnitLoaded(const CUnit* unit, const CUnit* transport) /*** Called when a unit is unloaded by a transport. * * @function Callins:UnitUnloaded - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer - * @param transportID integer - * @param transportTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID + * @param transportID UnitID + * @param transportTeam TeamID */ void CLuaHandle::UnitUnloaded(const CUnit* unit, const CUnit* transport) { @@ -1710,9 +1731,9 @@ void CLuaHandle::UnitUnloaded(const CUnit* unit, const CUnit* transport) /*** * * @function Callins:UnitEnteredUnderwater - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitEnteredUnderwater(const CUnit* unit) { @@ -1724,9 +1745,9 @@ void CLuaHandle::UnitEnteredUnderwater(const CUnit* unit) /*** * * @function Callins:UnitEnteredWater - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitEnteredWater(const CUnit* unit) { @@ -1739,9 +1760,9 @@ void CLuaHandle::UnitEnteredWater(const CUnit* unit) * * @function Callins:UnitLeftAir * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitEnteredAir(const CUnit* unit) { @@ -1754,9 +1775,9 @@ void CLuaHandle::UnitEnteredAir(const CUnit* unit) * * @function Callins:UnitLeftUnderwater * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitLeftUnderwater(const CUnit* unit) { @@ -1768,9 +1789,9 @@ void CLuaHandle::UnitLeftUnderwater(const CUnit* unit) * * @function Callins:UnitLeftWater * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitLeftWater(const CUnit* unit) { @@ -1783,9 +1804,9 @@ void CLuaHandle::UnitLeftWater(const CUnit* unit) * * @function Callins:UnitEnteredAir * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitLeftAir(const CUnit* unit) { @@ -1798,9 +1819,9 @@ void CLuaHandle::UnitLeftAir(const CUnit* unit) * * @function Callins:UnitCloaked * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitCloaked(const CUnit* unit) { @@ -1813,9 +1834,9 @@ void CLuaHandle::UnitCloaked(const CUnit* unit) * * @function Callins:UnitDecloaked * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitDecloaked(const CUnit* unit) { @@ -1829,8 +1850,8 @@ void CLuaHandle::UnitDecloaked(const CUnit* unit) * Both units must be registered with `Script.SetWatchUnit`. * * @function Callins:UnitUnitCollision - * @param colliderID integer - * @param collideeID integer + * @param colliderID UnitID + * @param collideeID UnitID */ bool CLuaHandle::UnitUnitCollision(const CUnit* collider, const CUnit* collidee) { @@ -1881,8 +1902,8 @@ bool CLuaHandle::UnitUnitCollision(const CUnit* collider, const CUnit* collidee) * * The unit must be registered with `Script.SetWatchUnit` and the feature registered with `Script.SetWatchFeature`. * - * @param colliderID integer - * @param collideeID integer + * @param colliderID UnitID + * @param collideeID UnitID */ bool CLuaHandle::UnitFeatureCollision(const CUnit* collider, const CFeature* collidee) { @@ -1932,9 +1953,9 @@ bool CLuaHandle::UnitFeatureCollision(const CUnit* collider, const CFeature* col * * @function Callins:UnitMoveFailed * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitMoveFailed(const CUnit* unit) { @@ -1954,9 +1975,9 @@ void CLuaHandle::UnitMoveFailed(const CUnit* unit) * * @function Callins:UnitArrivedAtGoal * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::UnitArrivedAtGoal(const CUnit* unit) { @@ -1971,9 +1992,9 @@ void CLuaHandle::UnitArrivedAtGoal(const CUnit* unit) * * @function Callins:RenderUnitDestroyed * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID */ void CLuaHandle::RenderUnitDestroyed(const CUnit* unit) { @@ -2009,8 +2030,8 @@ void CLuaHandle::RenderUnitDestroyed(const CUnit* unit) * * @function Callins:FeatureCreated * - * @param featureID integer - * @param allyTeamID integer + * @param featureID FeatureID + * @param allyTeamID AllyTeamID */ void CLuaHandle::FeatureCreated(const CFeature* feature) { @@ -2036,8 +2057,8 @@ void CLuaHandle::FeatureCreated(const CFeature* feature) * * @function Callins:FeatureDestroyed * - * @param featureID integer - * @param allyTeamID integer + * @param featureID FeatureID + * @param allyTeamID AllyTeamID */ void CLuaHandle::FeatureDestroyed(const CFeature* feature) { @@ -2063,15 +2084,15 @@ void CLuaHandle::FeatureDestroyed(const CFeature* feature) * * @function Callins:FeatureDamaged * - * @param featureID integer - * @param featureDefID integer - * @param featureTeam number + * @param featureID FeatureID + * @param featureDefID FeatureDefID + * @param featureTeam TeamID * @param damage number - * @param weaponDefID integer - * @param projectileID integer - * @param attackerID integer - * @param attackerDefID integer - * @param attackerTeam number + * @param weaponDefID WeaponDefID + * @param projectileID ProjectileID + * @param attackerID UnitID + * @param attackerDefID UnitDefID + * @param attackerTeam TeamID */ void CLuaHandle::FeatureDamaged( const CFeature* feature, @@ -2120,9 +2141,9 @@ void CLuaHandle::FeatureDamaged( * * Note that weaponDefID is missing if the projectile is spawned as part of a burst, but `Spring.GetProjectileDefID` and `Spring.GetProjectileName` still work in callin scope using proID. * - * @param proID integer - * @param proOwnerID integer - * @param weaponDefID integer + * @param proID ProjectileID + * @param proOwnerID UnitID + * @param weaponDefID WeaponDefID * * @see Script.SetWatchProjectile * @see Script.SetWatchWeapon @@ -2169,9 +2190,9 @@ void CLuaHandle::ProjectileCreated(const CProjectile* p) /*** Called when the projectile is destroyed. * * @function Callins:ProjectileDestroyed - * @param proID integer - * @param ownerID integer - * @param proWeaponDefID integer + * @param proID ProjectileID + * @param ownerID UnitID + * @param proWeaponDefID WeaponDefID * * @see Script.SetWatchProjectile * @see Script.SetWatchWeapon @@ -2238,12 +2259,12 @@ bool CLuaHandle::IsExplosionVisible(const WeaponDef* weaponDef, const CExplosion * * Only called for weaponDefIDs registered via Script.SetWatchExplosion or Script.SetWatchWeapon. * - * @param weaponDefID integer + * @param weaponDefID WeaponDefID * @param px number * @param py number * @param pz number - * @param attackerID integer - * @param projectileID integer + * @param attackerID UnitID + * @param projectileID ProjectileID * * @return boolean noGfx if then no graphical effects are drawn by the engine for this explosion. * @@ -2302,9 +2323,9 @@ bool CLuaHandle::Explosion(int weaponDefID, const WeaponDef* weaponDef, const CE * * @function Callins:StockpileChanged * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID * @param weaponNum integer * @param oldCount integer * @param newCount integer @@ -2337,7 +2358,7 @@ void CLuaHandle::StockpileChanged(const CUnit* unit, * * @function Callins:RecvLuaMsg * @param msg string - * @param playerID integer + * @param playerID PlayerID */ bool CLuaHandle::RecvLuaMsg(const string& msg, int playerID) { @@ -2498,10 +2519,10 @@ void CLuaHandle::Save(zipFile archive) /*** Called when the unsynced copy of the height-map is altered. * * @function Callins:UnsyncedHeightMapUpdate - * @return number x1 - * @return number z1 - * @return number x2 - * @return number z2 + * @return integer x1 + * @return integer z1 + * @return integer x2 + * @return integer z2 */ void CLuaHandle::UnsyncedHeightMapUpdate(const SRectangle& rect) { @@ -2546,8 +2567,8 @@ void CLuaHandle::Update() /*** Called whenever the window is resized. * * @function Callins:ViewResize - * @param viewSizeX number - * @param viewSizeY number + * @param viewSizeX integer + * @param viewSizeY integer */ void CLuaHandle::ViewResize() { @@ -2623,9 +2644,9 @@ void CLuaHandle::SunChanged() * * @function Callins:DefaultCommand * @param type "unit"|"feature" The type of the object pointed at. - * @param id integer The `unitID` or `featureID`. + * @param id ObjectID The `unitID` or `featureID`. * @param cmd integer The current command ID. - * @return integer The command ID to use as the default, or nil to keep the current ID. + * @return integer cmdID The command ID to use as the default, or nil to keep the current ID. */ bool CLuaHandle::DefaultCommand(const CUnit* unit, const CFeature* feature, int& cmd) @@ -2638,11 +2659,11 @@ bool CLuaHandle::DefaultCommand(const CUnit* unit, return false; if (unit) { - HSTR_PUSH(L, "unit"); + LuaPushString(L, "unit"); lua_pushnumber(L, unit->id); } else if (feature) { - HSTR_PUSH(L, "feature"); + LuaPushString(L, "feature"); lua_pushnumber(L, feature->id); } else { @@ -2653,14 +2674,14 @@ bool CLuaHandle::DefaultCommand(const CUnit* unit, /* FIXME else if (groundPos) { - HSTR_PUSH(L, "ground"); + LuaPushString(L, "ground"); lua_pushnumber(L, groundPos->x); lua_pushnumber(L, groundPos->y); lua_pushnumber(L, groundPos->z); args = 4; } else { - HSTR_PUSH(L, "selection"); + LuaPushString(L, "selection"); args = 1; } */ @@ -2825,9 +2846,9 @@ DRAW_CALLIN(DrawShadowFeaturesLua) * Called when build square data is computed, before engine rendering. * Grid dimensions can be inferred from UnitDefs[unitDefID].xsize and UnitDefs[unitDefID].zsize. * Grid origin in square coords: x - xsize/2, z - zsize/2 (accounting for facing). - * @param unitDefID number - * @param x number build position x - * @param z number build position z + * @param unitDefID UnitDefID + * @param x integer build position x + * @param z integer build position z * @param facing number build facing * @param statuses table flat 1D row-major array of BuildSquareStatus values: BLOCKED=0, OCCUPIED=1, RECLAIMABLE=2, OPEN=3 */ @@ -2907,8 +2928,8 @@ inline void CLuaHandle::DrawScreenCommon(const LuaHashString& cmdStr) /*** Also available to LuaMenu. * * @function Callins:DrawScreen - * @param viewSizeX number - * @param viewSizeY number + * @param viewSizeX integer + * @param viewSizeY integer */ void CLuaHandle::DrawScreen() { @@ -2923,8 +2944,8 @@ void CLuaHandle::DrawScreen() /*** * @function Callins:DrawScreenEffects - * @param viewSizeX number - * @param viewSizeY number + * @param viewSizeX integer + * @param viewSizeY integer */ void CLuaHandle::DrawScreenEffects() { @@ -2943,8 +2964,8 @@ void CLuaHandle::DrawScreenEffects() * * Note: This callin is invoked after the software rendered cursor (configuration variable HardwareCursor=0) is drawn. * - * @param viewSizeX number - * @param viewSizeY number + * @param viewSizeX integer + * @param viewSizeY integer */ void CLuaHandle::DrawScreenPost() { @@ -2960,8 +2981,8 @@ void CLuaHandle::DrawScreenPost() /*** * * @function Callins:DrawInMiniMap - * @param sx number relative to the minimap's position and scale. - * @param sy number relative to the minimap's position and scale. + * @param sx integer relative to the minimap's position and scale. + * @param sy integer relative to the minimap's position and scale. */ void CLuaHandle::DrawInMiniMap() { @@ -2988,8 +3009,8 @@ void CLuaHandle::DrawInMiniMap() /*** * * @function Callins:DrawInMiniMapBackground - * @param sx number relative to the minimap's position and scale. - * @param sy number relative to the minimap's position and scale. + * @param sx integer relative to the minimap's position and scale. + * @param sy integer relative to the minimap's position and scale. */ void CLuaHandle::DrawInMiniMapBackground() { @@ -3139,9 +3160,9 @@ bool CLuaHandle::KeyMapChanged() * @class KeyModifiers * @x_helper * - * @field right boolean Right mouse key pressed * @field alt boolean Alt key pressed * @field ctrl boolean Ctrl key pressed + * @field meta boolean Meta/GUI key pressed * @field shift boolean Shift key pressed */ @@ -3152,13 +3173,13 @@ bool CLuaHandle::KeyMapChanged() * * Return true if you don't want other callins or the engine to also receive this keypress. A list of key codes can be seen at the SDL wiki. * - * @param keyCode number + * @param keyCode integer * @param mods KeyModifiers * @param isRepeat boolean If you want an action to occur only once check for isRepeat == false. - * @param label boolean the name of the key - * @param utf32char number (deprecated) always 0 - * @param scanCode number - * @param actionList table the list of actions for this keypress + * @param label string the name of the key + * @param utf32char integer (deprecated) always 0 + * @param scanCode integer + * @param actionList table? the list of actions for this keypress, when available * @return boolean halt whether to halt the chain for consumers of the keypress */ bool CLuaHandle::KeyPress(int keyCode, int scanCode, bool isRepeat) @@ -3179,10 +3200,10 @@ bool CLuaHandle::KeyPress(int keyCode, int scanCode, bool isRepeat) lua_pushinteger(L, SDL21_keysyms(keyCode)); lua_createtable(L, 0, 4); - HSTR_PUSH_BOOL(L, "alt", !!KeyInput::GetKeyModState(KMOD_ALT)); - HSTR_PUSH_BOOL(L, "ctrl", !!KeyInput::GetKeyModState(KMOD_CTRL)); - HSTR_PUSH_BOOL(L, "meta", !!KeyInput::GetKeyModState(KMOD_GUI)); - HSTR_PUSH_BOOL(L, "shift", !!KeyInput::GetKeyModState(KMOD_SHIFT)); + LuaPushNamedBool(L, "alt", !!KeyInput::GetKeyModState(KMOD_ALT)); + LuaPushNamedBool(L, "ctrl", !!KeyInput::GetKeyModState(KMOD_CTRL)); + LuaPushNamedBool(L, "meta", !!KeyInput::GetKeyModState(KMOD_GUI)); + LuaPushNamedBool(L, "shift", !!KeyInput::GetKeyModState(KMOD_SHIFT)); lua_pushboolean(L, isRepeat); @@ -3218,12 +3239,12 @@ bool CLuaHandle::KeyPress(int keyCode, int scanCode, bool isRepeat) * * @function Callins:KeyRelease * - * @param keyCode number + * @param keyCode integer * @param mods KeyModifiers - * @param label boolean the name of the key - * @param utf32char number (deprecated) always 0 - * @param scanCode number - * @param actionList table the list of actions for this keyrelease + * @param label string the name of the key + * @param utf32char integer (deprecated) always 0 + * @param scanCode integer + * @param actionList table? the list of actions for this keyrelease, when available * * @return boolean */ @@ -3242,10 +3263,10 @@ bool CLuaHandle::KeyRelease(int keyCode, int scanCode) lua_pushinteger(L, SDL21_keysyms(keyCode)); lua_createtable(L, 0, 4); - HSTR_PUSH_BOOL(L, "alt", !!KeyInput::GetKeyModState(KMOD_ALT)); - HSTR_PUSH_BOOL(L, "ctrl", !!KeyInput::GetKeyModState(KMOD_CTRL)); - HSTR_PUSH_BOOL(L, "meta", !!KeyInput::GetKeyModState(KMOD_GUI)); - HSTR_PUSH_BOOL(L, "shift", !!KeyInput::GetKeyModState(KMOD_SHIFT)); + LuaPushNamedBool(L, "alt", !!KeyInput::GetKeyModState(KMOD_ALT)); + LuaPushNamedBool(L, "ctrl", !!KeyInput::GetKeyModState(KMOD_CTRL)); + LuaPushNamedBool(L, "meta", !!KeyInput::GetKeyModState(KMOD_GUI)); + LuaPushNamedBool(L, "shift", !!KeyInput::GetKeyModState(KMOD_SHIFT)); CKeySet ks(keyCode); lua_pushsstring(L, ks.GetString(true)); @@ -3308,8 +3329,8 @@ bool CLuaHandle::TextInput(const std::string& utf8) * @function Callins:TextEditing * * @param utf8 string - * @param start number - * @param length number + * @param start integer + * @param length integer */ bool CLuaHandle::TextEditing(const std::string& utf8, unsigned int start, unsigned int length) { @@ -3338,9 +3359,9 @@ bool CLuaHandle::TextEditing(const std::string& utf8, unsigned int start, unsign * The button parameter supports up to 7 buttons. Must return true for `MouseRelease` and other functions to be called. * * @function Callins:MousePress - * @param x number - * @param y number - * @param button number + * @param x integer + * @param y integer + * @param button integer * @return boolean becomeMouseOwner */ bool CLuaHandle::MousePress(int x, int y, int button) @@ -3372,9 +3393,9 @@ bool CLuaHandle::MousePress(int x, int y, int button) * * Please note that in order to have Spring call `Spring.MouseRelease`, you need to have a `Spring.MousePress` call-in in the same addon that returns true. * - * @param x number - * @param y number - * @param button number + * @param x integer + * @param y integer + * @param button integer * @return boolean becomeMouseOwner */ void CLuaHandle::MouseRelease(int x, int y, int button) @@ -3399,11 +3420,11 @@ void CLuaHandle::MouseRelease(int x, int y, int button) * * @function Callins:MouseMove * - * @param x number final x position - * @param y number final y position - * @param dx number distance travelled in x - * @param dy number distance travelled in y - * @param button number + * @param x integer final x position + * @param y integer final y position + * @param dx integer distance travelled in x + * @param dy integer distance travelled in y + * @param button integer */ bool CLuaHandle::MouseMove(int x, int y, int dx, int dy, int button) { @@ -3463,8 +3484,8 @@ bool CLuaHandle::MouseWheel(bool up, float value) * * Must return true for `Mouse*` events and `Spring.GetToolTip` to be called. * - * @param x number - * @param y number + * @param x integer + * @param y integer * @return boolean isAbove */ bool CLuaHandle::IsAbove(int x, int y) @@ -3491,8 +3512,8 @@ bool CLuaHandle::IsAbove(int x, int y) /*** Called when `Spring.IsAbove` returns true. * * @function Callins:GetTooltip - * @param x number - * @param y number + * @param x integer + * @param y integer * @return string tooltip */ string CLuaHandle::GetTooltip(int x, int y) @@ -3616,6 +3637,7 @@ void CLuaHandle::MiniMapRotationChanged(const float newRot, const float oldRot) * @function Callins:MiniMapStateChanged * @param isMinimized boolean * @param isMaximized boolean + * @param isSlaved boolean */ void CLuaHandle::MiniMapStateChanged(const bool isMinimized, const bool isMaximized, @@ -3640,14 +3662,14 @@ void CLuaHandle::MiniMapStateChanged(const bool isMinimized, /*** Called when the MiniMap Geometry changes * * @function Callins:MiniMapGeometryChanged - * @param newPosX number in pixels - * @param newPosY number in pixels - * @param newDimX number in pixels - * @param newDimY number in pixels - * @param oldPosX number in pixels - * @param oldPosY number in pixels - * @param oldDimX number in pixels - * @param oldDimY number in pixels + * @param newPosX integer in pixels + * @param newPosY integer in pixels + * @param newDimX integer in pixels + * @param newDimY integer in pixels + * @param oldPosX integer in pixels + * @param oldPosY integer in pixels + * @param oldDimX integer in pixels + * @param oldDimY integer in pixels */ void CLuaHandle::MiniMapGeometryChanged(const int2 newPos, const int2 newDim, const int2 oldPos, const int2 oldDim) { @@ -3678,7 +3700,7 @@ void CLuaHandle::MiniMapGeometryChanged(const int2 newPos, const int2 newDim, co * @param cmdID integer * @param cmdParams table * @param options CommandOptions - * @return boolean Returning true deletes the command and does not send it through the network. + * @return boolean delete Returning true deletes the command and does not send it through the network. */ bool CLuaHandle::CommandNotify(const Command& cmd) { @@ -3734,7 +3756,7 @@ bool CLuaHandle::AddConsoleLine(const string& msg, const string& section, int le /*** Called when a unit is added to or removed from a control group. * * @function Callins:GroupChanged - * @param groupID integer + * @param groupID GroupID */ bool CLuaHandle::GroupChanged(int groupID) { @@ -3754,13 +3776,13 @@ bool CLuaHandle::GroupChanged(int groupID) /*** * @function Callins:WorldTooltip * @param type "unit" - * @param unitId integer + * @param unitId UnitID * @return string tooltip */ /*** * @function Callins:WorldTooltip * @param type "feature" - * @param featureId integer + * @param featureId FeatureID * @return string tooltip */ /*** @@ -3789,24 +3811,24 @@ string CLuaHandle::WorldTooltip(const CUnit* unit, int args; if (unit) { - HSTR_PUSH(L, "unit"); + LuaPushString(L, "unit"); lua_pushnumber(L, unit->id); args = 2; } else if (feature) { - HSTR_PUSH(L, "feature"); + LuaPushString(L, "feature"); lua_pushnumber(L, feature->id); args = 2; } else if (groundPos) { - HSTR_PUSH(L, "ground"); + LuaPushString(L, "ground"); lua_pushnumber(L, groundPos->x); lua_pushnumber(L, groundPos->y); lua_pushnumber(L, groundPos->z); args = 4; } else { - HSTR_PUSH(L, "selection"); + LuaPushString(L, "selection"); args = 1; } @@ -3821,7 +3843,7 @@ string CLuaHandle::WorldTooltip(const CUnit* unit, /*** * @function Callins:MapDrawCmd - * @param playerID integer + * @param playerID PlayerID * @param type "point" * @param posX number * @param posY number @@ -3830,7 +3852,7 @@ string CLuaHandle::WorldTooltip(const CUnit* unit, */ /*** * @function Callins:MapDrawCmd - * @param playerID integer + * @param playerID PlayerID * @param type "line" * @param pos1X number * @param pos1Y number @@ -3841,7 +3863,7 @@ string CLuaHandle::WorldTooltip(const CUnit* unit, */ /*** * @function Callins:MapDrawCmd - * @param playerID integer + * @param playerID PlayerID * @param type "erase" * @param posX number * @param posY number @@ -3865,7 +3887,7 @@ bool CLuaHandle::MapDrawCmd(int playerID, int type, lua_pushnumber(L, playerID); if (type == MAPDRAW_POINT) { - HSTR_PUSH(L, "point"); + LuaPushString (L, "point"); lua_pushnumber(L, pos0->x); lua_pushnumber(L, pos0->y); lua_pushnumber(L, pos0->z); @@ -3873,7 +3895,7 @@ bool CLuaHandle::MapDrawCmd(int playerID, int type, args = 6; } else if (type == MAPDRAW_LINE) { - HSTR_PUSH(L, "line"); + LuaPushString (L, "line"); lua_pushnumber(L, pos0->x); lua_pushnumber(L, pos0->y); lua_pushnumber(L, pos0->z); @@ -3883,7 +3905,7 @@ bool CLuaHandle::MapDrawCmd(int playerID, int type, args = 8; } else if (type == MAPDRAW_ERASE) { - HSTR_PUSH(L, "erase"); + LuaPushString (L, "erase"); lua_pushnumber(L, pos0->x); lua_pushnumber(L, pos0->y); lua_pushnumber(L, pos0->z); @@ -3930,7 +3952,7 @@ bool CLuaHandle::MapDrawCmd(int playerID, int type, * @function Callins:GameSetup * @param state READY_MESSAGE the current message the engine would display to the player * @param ready boolean whether the player is currently ready or not - * @param playerStates table indexed by playerID + * @param playerStates table indexed by playerID * @return boolean? gameHandled disables the engine ui when true * @return boolean? newReady whether the player is ready (ignored unless `gameHandled = true`) */ @@ -3978,7 +4000,7 @@ bool CLuaHandle::GameSetup(const string& state, bool& ready, /*** @function Callins:RecvSkirmishAIMessage * - * @param aiTeam integer + * @param aiTeam TeamID * @param dataStr string */ const char* CLuaHandle::RecvSkirmishAIMessage(int aiTeam, const char* inData, int inSize, size_t* outSize) @@ -4225,29 +4247,29 @@ void CLuaHandle::CollectGarbage(bool forced) bool CLuaHandle::AddBasicCalls(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; - HSTR_PUSH(L, "Script"); + LuaPushString(L, "Script"); lua_createtable(L, 0, 17); { - HSTR_PUSH_CFUNC(L, "Kill", KillActiveHandle); - HSTR_PUSH_CFUNC(L, "UpdateCallIn", CallOutUpdateCallIn); - HSTR_PUSH_CFUNC(L, "GetName", CallOutGetName); - HSTR_PUSH_CFUNC(L, "GetSynced", CallOutGetSynced); - HSTR_PUSH_CFUNC(L, "GetFullCtrl", CallOutGetFullCtrl); - HSTR_PUSH_CFUNC(L, "GetFullRead", CallOutGetFullRead); - HSTR_PUSH_CFUNC(L, "GetCtrlTeam", CallOutGetCtrlTeam); - HSTR_PUSH_CFUNC(L, "GetReadTeam", CallOutGetReadTeam); - HSTR_PUSH_CFUNC(L, "GetReadAllyTeam", CallOutGetReadAllyTeam); - HSTR_PUSH_CFUNC(L, "GetSelectTeam", CallOutGetSelectTeam); - HSTR_PUSH_CFUNC(L, "GetGlobal", CallOutGetGlobal); - HSTR_PUSH_CFUNC(L, "GetRegistry", CallOutGetRegistry); - HSTR_PUSH_CFUNC(L, "GetCallInList", CallOutGetCallInList); - HSTR_PUSH_CFUNC(L, "DelayByFrames", CallOutDelayByFrames); - HSTR_PUSH_CFUNC(L, "IsEngineMinVersion", CallOutIsEngineMinVersion); + LuaPushNamedCFunc(L, "Kill", KillActiveHandle); + LuaPushNamedCFunc(L, "UpdateCallIn", CallOutUpdateCallIn); + LuaPushNamedCFunc(L, "GetName", CallOutGetName); + LuaPushNamedCFunc(L, "GetSynced", CallOutGetSynced); + LuaPushNamedCFunc(L, "GetFullCtrl", CallOutGetFullCtrl); + LuaPushNamedCFunc(L, "GetFullRead", CallOutGetFullRead); + LuaPushNamedCFunc(L, "GetCtrlTeam", CallOutGetCtrlTeam); + LuaPushNamedCFunc(L, "GetReadTeam", CallOutGetReadTeam); + LuaPushNamedCFunc(L, "GetReadAllyTeam", CallOutGetReadAllyTeam); + LuaPushNamedCFunc(L, "GetSelectTeam", CallOutGetSelectTeam); + LuaPushNamedCFunc(L, "GetGlobal", CallOutGetGlobal); + LuaPushNamedCFunc(L, "GetRegistry", CallOutGetRegistry); + LuaPushNamedCFunc(L, "GetCallInList", CallOutGetCallInList); + LuaPushNamedCFunc(L, "DelayByFrames", CallOutDelayByFrames); + LuaPushNamedCFunc(L, "IsEngineMinVersion", CallOutIsEngineMinVersion); // special team constants /*** @field Script.NO_ACCESS_TEAM -1 */ - HSTR_PUSH_NUMBER(L, "NO_ACCESS_TEAM", CEventClient::NoAccessTeam); + LuaPushNamedNumber(L, "NO_ACCESS_TEAM", CEventClient::NoAccessTeam); /*** @field Script.ALL_ACCESS_TEAM -2 */ - HSTR_PUSH_NUMBER(L, "ALL_ACCESS_TEAM", CEventClient::AllAccessTeam); + LuaPushNamedNumber(L, "ALL_ACCESS_TEAM", CEventClient::AllAccessTeam); } lua_rawset(L, -3); @@ -4313,7 +4335,7 @@ int CLuaHandle::CallOutGetFullRead(lua_State* L) /*** * @function Script.GetCtrlTeam - * @return integer teamID + * @return TeamID teamID */ int CLuaHandle::CallOutGetCtrlTeam(lua_State* L) { @@ -4324,7 +4346,7 @@ int CLuaHandle::CallOutGetCtrlTeam(lua_State* L) /*** * @function Script.GetReadTeam - * @return integer teamID + * @return TeamID teamID */ int CLuaHandle::CallOutGetReadTeam(lua_State* L) { @@ -4335,7 +4357,7 @@ int CLuaHandle::CallOutGetReadTeam(lua_State* L) /*** * @function Script.GetReadAllyTeam - * @return integer allyTeamID + * @return AllyTeamID allyTeamID */ int CLuaHandle::CallOutGetReadAllyTeam(lua_State* L) { @@ -4346,7 +4368,7 @@ int CLuaHandle::CallOutGetReadAllyTeam(lua_State* L) /*** * @function Script.GetSelectTeam - * @return integer teamID + * @return TeamID teamID */ int CLuaHandle::CallOutGetSelectTeam(lua_State* L) { @@ -4439,6 +4461,11 @@ int CLuaHandle::CallOutGetCallInList(lua_State* L) } +/*** + * @function Script.UpdateCallIn + * @param name string + * @return nil + */ int CLuaHandle::CallOutUpdateCallIn(lua_State* L) { diff --git a/rts/Lua/LuaHandle.h b/rts/Lua/LuaHandle.h index 5256825a2b6..97ddf4323c9 100644 --- a/rts/Lua/LuaHandle.h +++ b/rts/Lua/LuaHandle.h @@ -329,6 +329,7 @@ class CLuaHandle : public CEventClient bool AddBasicCalls(lua_State* L); bool AddCommonModules(lua_State* L); bool LoadCode(lua_State* L, std::string code, const std::string& debug); + void InitLuaSocket(lua_State* L); static bool AddEntriesToTable(lua_State* L, const char* name, bool (*entriesFunc)(lua_State*)); /// returns error code and sets traceback on error diff --git a/rts/Lua/LuaHandleSynced.cpp b/rts/Lua/LuaHandleSynced.cpp index 85851f3bc3c..709b40367c7 100644 --- a/rts/Lua/LuaHandleSynced.cpp +++ b/rts/Lua/LuaHandleSynced.cpp @@ -1,5 +1,6 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ +#include "System/RangesCompat.h" #include "LuaHandleSynced.h" #include "LuaInclude.h" @@ -22,6 +23,7 @@ #include "LuaSyncedTable.h" #include "LuaUICommand.h" #include "LuaUnsyncedCtrl.h" +#include "LuaDebugExtra.h" #include "LuaUnsyncedRead.h" #include "LuaFeatureDefs.h" #include "LuaUnitDefs.h" @@ -54,6 +56,7 @@ #include "System/Misc/TracyDefs.h" +#include LuaRulesParams::Params CSplitLuaHandle::gameParams; @@ -130,6 +133,7 @@ bool CUnsyncedLuaHandle::Init(std::string code, const std::string& file) if (!AddEntriesToTable(L, "Spring", LuaUnsyncedCtrl::PushEntries )) KILL if (!AddEntriesToTable(L, "Spring", LuaUnsyncedRead::PushEntries )) KILL if (!AddEntriesToTable(L, "Spring", LuaUICommand::PushEntries )) KILL + if (!AddEntriesToTable(L, "debug", LuaDebugExtra::PushEntries )) KILL if (!AddEntriesToTable(L, "gl", LuaOpenGL::PushEntries )) KILL if (!AddEntriesToTable(L, "GL", LuaConstGL::PushEntries )) KILL if (!AddEntriesToTable(L, "Engine", LuaConstEngine::PushEntries )) KILL @@ -205,8 +209,8 @@ void CUnsyncedLuaHandle::RecvFromSynced(lua_State* srcState, int args) /*** For custom rendering of units * * @function UnsyncedCallins:DrawUnit - * @param unitID integer - * @param drawMode number + * @param unitID UnitID + * @param drawMode integer * @return boolean suppressEngineDraw * @deprecated */ @@ -242,8 +246,8 @@ bool CUnsyncedLuaHandle::DrawUnit(const CUnit* unit) /*** For custom rendering of features * * @function UnsyncedCallins:DrawFeature - * @param featureID integer - * @param drawMode number + * @param featureID FeatureID + * @param drawMode integer * @return boolean suppressEngineDraw * @deprecated */ @@ -278,9 +282,9 @@ bool CUnsyncedLuaHandle::DrawFeature(const CFeature* feature) /*** For custom rendering of shields. * * @function UnsyncedCallins:DrawShield - * @param featureID integer + * @param unitID UnitID * @param weaponID integer - * @param drawMode number + * @param drawMode integer * @return boolean suppressEngineDraw * @deprecated */ @@ -317,8 +321,8 @@ bool CUnsyncedLuaHandle::DrawShield(const CUnit* unit, const CWeapon* weapon) /*** For custom rendering of weapon (& other) projectiles * * @function UnsyncedCallins:DrawProjectile - * @param projectileID integer - * @param drawMode number + * @param projectileID ProjectileID + * @param drawMode integer * @return boolean suppressEngineDraw * @deprecated */ @@ -356,7 +360,7 @@ bool CUnsyncedLuaHandle::DrawProjectile(const CProjectile* projectile) * * @function UnsyncedCallins:DrawMaterial * @param uuid integer - * @param drawMode number + * @param drawMode integer * @return boolean suppressEngineDraw * @deprecated */ @@ -569,14 +573,14 @@ bool CSyncedLuaHandle::SyncedActionFallback(const std::string& msg, int playerID /*** Called when the unit reaches an unknown command in its queue (i.e. one not handled by the engine). * * @function SyncedCallins:CommandFallback - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID * @param cmdID integer * @param cmdParams number[] * @param cmdOptions CommandOptions - * @param cmdTag number - * @return boolean whether to remove the command from the queue + * @param cmdTag integer + * @return boolean removeCmd whether to remove the command from the queue */ bool CSyncedLuaHandle::CommandFallback(const CUnit* unit, const Command& cmd) { @@ -605,16 +609,16 @@ bool CSyncedLuaHandle::CommandFallback(const CUnit* unit, const Command& cmd) * * The queue remains untouched when a command is blocked, whether it would be queued or replace the queue. * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID * @param cmdID integer * @param cmdParams number[] * @param cmdOptions CommandOptions - * @param cmdTag number + * @param cmdTag integer * @param synced boolean * @param fromLua boolean - * @return boolean whether it should be let into the queue. + * @return boolean allowCmd whether it should be let into the queue. */ bool CSyncedLuaHandle::AllowCommand(const CUnit* unit, const Command& cmd, int playerNum, bool fromSynced, bool fromLua) { @@ -646,14 +650,15 @@ bool CSyncedLuaHandle::AllowCommand(const CUnit* unit, const Command& cmd, int p /*** Called just before unit is created. * * @function SyncedCallins:AllowUnitCreation - * @param unitDefID integer - * @param builderID integer - * @param builderTeam integer + * @param unitDefID UnitDefID + * @param builderID UnitID + * @param builderTeam TeamID * @param x number * @param y number * @param z number * @param facing FacingInteger - * @return boolean allow, boolean dropOrder + * @return boolean allow + * @return boolean dropOrder */ std::pair CSyncedLuaHandle::AllowUnitCreation( const UnitDef* unitDef, @@ -693,12 +698,12 @@ std::pair CSyncedLuaHandle::AllowUnitCreation( /*** Called just before a unit is transferred to a different team. * * @function SyncedCallins:AllowUnitTransfer - * @param unitID integer - * @param unitDefID integer - * @param oldTeam integer - * @param newTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param oldTeam TeamID + * @param newTeam TeamID * @param capture boolean - * @return boolean whether or not the transfer is permitted. + * @return boolean allow whether or not the transfer is permitted. */ bool CSyncedLuaHandle::AllowUnitTransfer(const CUnit* unit, int newTeam, bool capture) { @@ -730,12 +735,12 @@ bool CSyncedLuaHandle::AllowUnitTransfer(const CUnit* unit, int newTeam, bool ca /*** Called just before a unit progresses its build percentage. * * @function SyncedCallins:AllowUnitBuildStep - * @param builderID integer - * @param builderTeam integer - * @param unitID integer - * @param unitDefID integer + * @param builderID UnitID + * @param builderTeam TeamID + * @param unitID UnitID + * @param unitDefID UnitDefID * @param part number - * @return boolean whether or not the build makes progress. + * @return boolean allow whether or not the build makes progress. */ bool CSyncedLuaHandle::AllowUnitBuildStep(const CUnit* builder, const CUnit* unit, float part) { @@ -767,12 +772,12 @@ bool CSyncedLuaHandle::AllowUnitBuildStep(const CUnit* builder, const CUnit* uni /*** * * @function SyncedCallins:AllowUnitCaptureStep - * @param builderID integer - * @param builderTeam integer - * @param unitID integer - * @param unitDefID integer + * @param builderID UnitID + * @param builderTeam TeamID + * @param unitID UnitID + * @param unitDefID UnitDefID * @param part number - * @return boolean whether or not the capture makes progress. + * @return boolean allow whether or not the capture makes progress. */ bool CSyncedLuaHandle::AllowUnitCaptureStep(const CUnit* builder, const CUnit* unit, float part) { @@ -804,13 +809,13 @@ bool CSyncedLuaHandle::AllowUnitCaptureStep(const CUnit* builder, const CUnit* u /*** * * @function SyncedCallins:AllowUnitTransport - * @param transporterID integer - * @param transporterUnitDefID integer - * @param transporterTeam integer - * @param transporteeID integer - * @param transporteeUnitDefID integer - * @param transporteeTeam integer - * @return boolean whether or not the transport is allowed + * @param transporterID UnitID + * @param transporterUnitDefID UnitDefID + * @param transporterTeam TeamID + * @param transporteeID UnitID + * @param transporteeUnitDefID UnitDefID + * @param transporteeTeam TeamID + * @return boolean allow whether or not the transport is allowed */ bool CSyncedLuaHandle::AllowUnitTransport(const CUnit* transporter, const CUnit* transportee) { @@ -842,16 +847,16 @@ bool CSyncedLuaHandle::AllowUnitTransport(const CUnit* transporter, const CUnit* /*** * * @function SyncedCallins:AllowUnitTransportLoad - * @param transporterID integer - * @param transporterUnitDefID integer - * @param transporterTeam integer - * @param transporteeID integer - * @param transporteeUnitDefID integer - * @param transporteeTeam integer + * @param transporterID UnitID + * @param transporterUnitDefID UnitDefID + * @param transporterTeam TeamID + * @param transporteeID UnitID + * @param transporteeUnitDefID UnitDefID + * @param transporteeTeam TeamID * @param x number * @param y number * @param z number - * @return boolean whether or not the transport load is allowed + * @return boolean allow whether or not the transport load is allowed */ bool CSyncedLuaHandle::AllowUnitTransportLoad( const CUnit* transporter, @@ -892,16 +897,16 @@ bool CSyncedLuaHandle::AllowUnitTransportLoad( /*** * * @function SyncedCallins:AllowUnitTransportUnload - * @param transporterID integer - * @param transporterUnitDefID integer - * @param transporterTeam integer - * @param transporteeID integer - * @param transporteeUnitDefID integer - * @param transporteeTeam integer + * @param transporterID UnitID + * @param transporterUnitDefID UnitDefID + * @param transporterTeam TeamID + * @param transporteeID UnitID + * @param transporteeUnitDefID UnitDefID + * @param transporteeTeam TeamID * @param x number * @param y number * @param z number - * @return boolean whether or not the transport unload is allowed + * @return boolean allow whether or not the transport unload is allowed */ bool CSyncedLuaHandle::AllowUnitTransportUnload( const CUnit* transporter, @@ -940,9 +945,9 @@ bool CSyncedLuaHandle::AllowUnitTransportUnload( /*** * * @function SyncedCallins:AllowUnitCloak - * @param unitID integer - * @param enemyID integer? - * @return boolean whether unit is allowed to cloak + * @param unitID UnitID + * @param enemyID UnitID? + * @return boolean allow whether unit is allowed to cloak */ bool CSyncedLuaHandle::AllowUnitCloak(const CUnit* unit, const CUnit* enemy) { @@ -975,11 +980,11 @@ bool CSyncedLuaHandle::AllowUnitCloak(const CUnit* unit, const CUnit* enemy) /*** * - * @function SyncedCallins:AllowUnitCloak - * @param unitID integer - * @param objectID integer? - * @param weaponNum number? - * @return boolean whether unit is allowed to decloak + * @function SyncedCallins:AllowUnitDecloak + * @param unitID UnitID + * @param objectID ObjectID? + * @param weaponNum integer? + * @return boolean allow whether unit is allowed to decloak */ bool CSyncedLuaHandle::AllowUnitDecloak(const CUnit* unit, const CSolidObject* object, const CWeapon* weapon) { @@ -1020,9 +1025,9 @@ bool CSyncedLuaHandle::AllowUnitDecloak(const CUnit* unit, const CSolidObject* o /*** * * @function SyncedCallins:AllowUnitKamikaze - * @param unitID integer - * @param targetID integer - * @return boolean whether unit is allowed to selfd + * @param unitID UnitID + * @param targetID UnitID + * @return boolean allow whether unit is allowed to selfd */ bool CSyncedLuaHandle::AllowUnitKamikaze(const CUnit* unit, const CUnit* target, bool allowed) { @@ -1050,12 +1055,12 @@ bool CSyncedLuaHandle::AllowUnitKamikaze(const CUnit* unit, const CUnit* target, /*** Called just before feature is created. * * @function SyncedCallins:AllowFeatureCreation - * @param featureDefID integer - * @param teamID integer + * @param featureDefID FeatureDefID + * @param teamID TeamID * @param x number * @param y number * @param z number - * @return boolean whether or not the creation is permitted + * @return boolean allow whether or not the creation is permitted */ bool CSyncedLuaHandle::AllowFeatureCreation(const FeatureDef* featureDef, int teamID, const float3& pos) { @@ -1094,13 +1099,13 @@ bool CSyncedLuaHandle::AllowFeatureCreation(const FeatureDef* featureDef, int te * Eg. for a 30 workertime builder, that's a build power of 1 per frame. * For a 50 buildtime feature reclaimed by this builder, part will be 100/-50(/1) = -2%, or -0.02 numerically. * - * @param builderID integer - * @param builderTeam integer - * @param featureID integer - * @param featureDefID integer + * @param builderID UnitID + * @param builderTeam TeamID + * @param featureID FeatureID + * @param featureDefID FeatureDefID * @param part number * - * @return boolean whether or not the change is permitted + * @return boolean allow whether or not the change is permitted */ bool CSyncedLuaHandle::AllowFeatureBuildStep(const CUnit* builder, const CFeature* feature, float part) { @@ -1132,10 +1137,10 @@ bool CSyncedLuaHandle::AllowFeatureBuildStep(const CUnit* builder, const CFeatur /*** Called when a team sets the sharing level of a resource. * * @function SyncedCallins:AllowResourceLevel - * @param teamID integer + * @param teamID TeamID * @param res string * @param level number - * @return boolean whether or not the sharing level is permitted + * @return boolean allow whether or not the sharing level is permitted */ bool CSyncedLuaHandle::AllowResourceLevel(int teamID, const std::string& type, float level) { @@ -1165,11 +1170,11 @@ bool CSyncedLuaHandle::AllowResourceLevel(int teamID, const std::string& type, f /*** Called just before resources are transferred between players. * * @function SyncedCallins:AllowResourceTransfer - * @param oldTeamID integer - * @param newTeamID integer + * @param oldTeamID TeamID + * @param newTeamID TeamID * @param res string * @param amount number - * @return boolean whether or not the transfer is permitted. + * @return boolean allow whether or not the transfer is permitted. */ bool CSyncedLuaHandle::AllowResourceTransfer(int oldTeam, int newTeam, const char* type, float amount) { @@ -1196,14 +1201,50 @@ bool CSyncedLuaHandle::AllowResourceTransfer(int oldTeam, int newTeam, const cha return allow; } +/*** Called when excess resources are added. + * Accumulates all excesses within a single gameframe. + * + * @function SyncedCallins:ResourceExcess + * @param excesses table + * @return boolean handled whether or not Lua handled the event + */ +bool CSyncedLuaHandle::ResourceExcess(const std::map & excesses) +{ + RECOIL_DETAILED_TRACY_ZONE; + LUA_CALL_IN_CHECK(L, true); + luaL_checkstack(L, 3, __func__); + + static const LuaHashString cmdStr(__func__); + if (!cmdStr.GetGlobalFunc(L)) + return false; + + lua_createtable(L, excesses.size(), 1); + + for (const auto &[teamID, excess] : excesses) { + lua_createtable(L, excess.MAX_RESOURCES, 0); + for (const auto &[resourceID, resource] : spring::views::enumerate(excess)) { + lua_pushnumber(L, resource); + lua_rawseti(L, -2, resourceID + 1); + } + lua_rawseti(L, -2, teamID); + } + + if (!RunCallIn(L, cmdStr, 1, 1)) + return false; + + const bool handled = luaL_optboolean(L, -1, false); + lua_pop(L, 1); + return handled; +} + /*** Determines if this unit can be controlled directly in FPS view. * * @function SyncedCallins:AllowDirectUnitControl - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer - * @param playerID integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID + * @param playerID PlayerID * @return boolean allow */ bool CSyncedLuaHandle::AllowDirectUnitControl(int playerID, const CUnit* unit) @@ -1236,8 +1277,8 @@ bool CSyncedLuaHandle::AllowDirectUnitControl(int playerID, const CUnit* unit) * * @function SyncedCallins:AllowBuilderHoldFire * - * @param unitID integer - * @param unitDefID integer + * @param unitID UnitID + * @param unitDefID UnitDefID * @param action -1|CMD * * One of the following: @@ -1289,9 +1330,9 @@ bool CSyncedLuaHandle::AllowBuilderHoldFire(const CUnit* unit, int action) * 3 - the player failed to load. * The default 'failed to choose' start-position is the north-west point of their startbox, or (0,0,0) if they do not have a startbox. * - * @param playerID integer - * @param teamID integer - * @param readyState number + * @param playerID PlayerID + * @param teamID TeamID + * @param readyState integer * @param clampedX number * @param clampedY number * @param clampedZ number @@ -1337,12 +1378,12 @@ bool CSyncedLuaHandle::AllowStartPosition(int playerID, int teamID, unsigned cha * * @function SyncedCallins:MoveCtrlNotify * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer - * @param data number was supposed to indicate the type of notification but currently never has a value other than 1 ("unit hit the ground"). + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID + * @param data integer was supposed to indicate the type of notification but currently never has a value other than 1 ("unit hit the ground"). * - * @return boolean whether or not the unit should remain script-controlled (false) or return to engine controlled movement (true). + * @return boolean engineControl whether or not the unit should remain script-controlled (false) or return to engine controlled movement (true). */ bool CSyncedLuaHandle::MoveCtrlNotify(const CUnit* unit, int data) { @@ -1374,13 +1415,13 @@ bool CSyncedLuaHandle::MoveCtrlNotify(const CUnit* unit, int data) /*** Called when pre-building terrain levelling terraforms are completed (c.f. levelGround) * * @function SyncedCallins:TerraformComplete - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer - * @param buildUnitID integer - * @param buildUnitDefID integer - * @param buildUnitTeam integer - * @return boolean if true the current build order is terminated + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID + * @param buildUnitID UnitID + * @param buildUnitDefID UnitDefID + * @param buildUnitTeam TeamID + * @return boolean stop if true the current build order is terminated */ bool CSyncedLuaHandle::TerraformComplete(const CUnit* unit, const CUnit* build) { @@ -1439,18 +1480,19 @@ bool CSyncedLuaHandle::TerraformComplete(const CUnit* unit, const CUnit* build) * 1st is stored under *newDamage if newDamage != NULL * 2nd is stored under *impulseMult if impulseMult != NULL * - * @param unitID integer - * @param unitDefID integer - * @param unitTeam integer + * @param unitID UnitID + * @param unitDefID UnitDefID + * @param unitTeam TeamID * @param damage number * @param paralyzer boolean - * @param weaponDefID integer? Synced Only - * @param projectileID integer? Synced Only - * @param attackerID integer? Synced Only - * @param attackerDefID integer? Synced Only - * @param attackerTeam integer? Synced Only + * @param weaponDefID WeaponDefID? Synced Only + * @param projectileID ProjectileID? Synced Only + * @param attackerID UnitID? Synced Only + * @param attackerDefID UnitDefID? Synced Only + * @param attackerTeam TeamID? Synced Only * - * @return number newDamage, number impulseMult + * @return number newDamage + * @return number impulseMult */ bool CSyncedLuaHandle::UnitPreDamaged( const CUnit* unit, @@ -1531,15 +1573,15 @@ bool CSyncedLuaHandle::UnitPreDamaged( * * Allows fine control over how much damage and impulse is applied. * - * @param featureID integer - * @param featureDefID integer - * @param featureTeam integer + * @param featureID FeatureID + * @param featureDefID FeatureDefID + * @param featureTeam TeamID * @param damage number - * @param weaponDefID integer - * @param projectileID integer - * @param attackerID integer - * @param attackerDefID integer - * @param attackerTeam integer + * @param weaponDefID WeaponDefID + * @param projectileID ProjectileID + * @param attackerID UnitID + * @param attackerDefID UnitDefID + * @param attackerTeam TeamID * @return number newDamage * @return number impulseMult */ @@ -1612,13 +1654,13 @@ bool CSyncedLuaHandle::FeaturePreDamaged( * * @function SyncedCallins:ShieldPreDamaged * - * @param projectileID integer `-1` when the weapon type is `BeamLaser` or `LightningCannon` - * @param projectileOwnerID integer `-1` when the weapon type is `BeamLaser` or `LightningCannon` + * @param projectileID ProjectileID `-1` when the weapon type is `BeamLaser` or `LightningCannon` + * @param projectileOwnerID UnitID `-1` when the weapon type is `BeamLaser` or `LightningCannon` * @param shieldWeaponNum integer - * @param shieldCarrierID integer + * @param shieldCarrierID UnitID * @param bounceProjectile boolean * @param beamEmitterWeaponNum integer? present only when the weapon type is `BeamLaser` or `LightningCannon` - * @param beamEmitterUnitID integer? present only when the weapon type is `BeamLaser` or `LightningCannon` + * @param beamEmitterUnitID UnitID? present only when the weapon type is `BeamLaser` or `LightningCannon` * @param startX number * @param startY number * @param startZ number @@ -1626,7 +1668,7 @@ bool CSyncedLuaHandle::FeaturePreDamaged( * @param hitY number * @param hitZ number * - * @return boolean if true the gadget handles the collision event and the engine does not remove the projectile + * @return boolean handle if true the gadget handles the collision event and the engine does not remove the projectile */ bool CSyncedLuaHandle::ShieldPreDamaged( const CProjectile* projectile, @@ -1694,9 +1736,9 @@ bool CSyncedLuaHandle::ShieldPreDamaged( * * Only called for weaponDefIDs registered via `Script.SetWatchAllowTarget` or `Script.SetWatchWeapon`. * - * @param attackerID integer + * @param attackerID UnitID * @param attackerWeaponNum integer - * @param attackerWeaponDefID integer + * @param attackerWeaponDefID WeaponDefID * * @return boolean allowCheck * @return boolean ignoreCheck @@ -1741,14 +1783,14 @@ int CSyncedLuaHandle::AllowWeaponTargetCheck(unsigned int attackerID, unsigned i * * Only called for weaponDefIDs registered via `Script.SetWatchAllowTarget` or `Script.SetWatchWeapon`. * - * @param attackerID integer - * @param targetID integer + * @param attackerID UnitID + * @param targetID UnitID * @param attackerWeaponNum integer - * @param attackerWeaponDefID integer + * @param attackerWeaponDefID WeaponDefID * @param defPriority number * * @return boolean allowed - * @return number the new priority for this target (if you don't want to change it, return defPriority). Lower priority targets are targeted first. + * @return number newPriority The new priority for this target (if you don't want to change it, return defPriority). Lower priority targets are targeted first. * * @see Script.SetWatchAllowTarget * @see Script.SetWatchWeapon @@ -1811,9 +1853,9 @@ bool CSyncedLuaHandle::AllowWeaponTarget( * * Only called for weaponDefIDs registered via `Script.SetWatchAllowTarget` or `Script.SetWatchWeapon`. * - * @param interceptorUnitID integer + * @param interceptorUnitID UnitID * @param interceptorWeaponID integer - * @param targetProjectileID integer + * @param targetProjectileID ProjectileID * * @return boolean allowed * @@ -2151,7 +2193,7 @@ int CSyncedLuaHandle::GetWatchWeaponDef(lua_State* L) { * * @function Script.GetWatchUnit * - * @param unitDefID integer + * @param unitDefID UnitDefID * @return boolean watched Watch status. * * @see Script.SetWatchUnit @@ -2164,7 +2206,7 @@ GetWatchDef(Synced, Unit) * * @function Script.GetWatchFeature * - * @param featureDefID integer + * @param featureDefID FeatureDefID * @return boolean watched `true` if callins are registered, otherwise `false`. * * @see Script.SetWatchFeature @@ -2182,8 +2224,8 @@ GetWatchDef(Synced, Feature) * Script.GetWatchExplosion(weaponDefID) or Script.GetWatchProjectile(weaponDefID) or Script.GetWatchAllowTarget(weaponDefID) * ``` * - * @param weaponDefID integer - * @return boolean watched True if watch is enabled for any weaponDefID callins. + * @param weaponDefID WeaponDefID + * @return boolean watched `true` if watch is enabled for any weaponDefID callins. * * @see Script.SetWatchWeapon */ @@ -2192,7 +2234,7 @@ GetWatchDef(Synced, Feature) * * @function Script.GetWatchExplosion * - * @param weaponDefID integer + * @param weaponDefID WeaponDefID * @return boolean watched `true` if callins are registered, otherwise `false`. * * @see Script.SetWatchExplosion @@ -2206,7 +2248,7 @@ GetWatchDef(Unsynced, Explosion) * * @function Script.GetWatchProjectile * - * @param weaponDefID integer + * @param weaponDefID WeaponDefID * @return boolean watched `true` if callins are registered, otherwise `false`. * * @see Script.SetWatchProjectile @@ -2219,7 +2261,7 @@ GetWatchDef(Synced, Projectile) * * @function Script.GetWatchAllowTarget * - * @param weaponDefID integer + * @param weaponDefID WeaponDefID * @return boolean watched `true` if callins are registered, otherwise `false`. * * @see Script.SetWatchAllowTarget @@ -2232,7 +2274,7 @@ GetWatchDef(Synced, AllowTarget) * * @function Script.SetWatchUnit * - * @param unitDefID integer + * @param unitDefID UnitDefID * @param watch boolean Whether to register or deregister. * * @see Script.GetWatchUnit @@ -2248,7 +2290,7 @@ SetWatchDef(Synced, Unit) * * @function Script.SetWatchFeature * - * @param featureDefID integer + * @param featureDefID FeatureDefID * @param watch boolean Whether to register or deregister. * * @see Script.GetWatchFeature @@ -2272,7 +2314,7 @@ SetWatchDef(Synced, Feature) * * Generally it's better to use those methods to avoid registering uneeded callins. * - * @param weaponDefID integer + * @param weaponDefID WeaponDefID * @param watch boolean Whether to register or deregister. * * @see Script.GetWatchWeapon @@ -2285,7 +2327,7 @@ SetWatchDef(Synced, Feature) * * @function Script.SetWatchExplosion * - * @param weaponDefID integer + * @param weaponDefID WeaponDefID * @param watch boolean Whether to register or deregister. * * @see Script.GetWatchExplosion @@ -2300,7 +2342,7 @@ SetWatchDef(Unsynced, Explosion) * * @function Script.SetWatchProjectile * - * @param weaponDefID integer weaponDefID for weapons or -1 to watch for debris. + * @param weaponDefID WeaponDefID weaponDefID for weapons or -1 to watch for debris. * @param watch boolean Whether to register or deregister. * * @see Script.GetWatchProjectile @@ -2315,7 +2357,7 @@ SetWatchDef(Synced, Projectile) * * @function Script.SetWatchAllowTarget * - * @param weaponDefID integer + * @param weaponDefID WeaponDefID * @param watch boolean Whether to register or deregister. * * @see Script.GetWatchAllowTarget @@ -2494,15 +2536,15 @@ string CSplitLuaHandle::LoadFile(const std::string& filename, const std::string& /*** * @class CallAsTeamOptions * @x_helper - * @field ctrl integer Ctrl team ID. - * @field read integer Read team ID. - * @field select integer Select team ID. + * @field ctrl TeamID + * @field read TeamID + * @field select TeamID */ /*** Calls a function from given team's PoV. In particular this makes callouts obey that team's visibility rules. * * @function Spring.CallAsTeam - * @param teamID integer Team ID. + * @param teamID TeamID Team ID. * @param func fun(...) The function to call. * @param ... any Arguments to pass to the function. * @return any ... The return values of the function. diff --git a/rts/Lua/LuaHandleSynced.h b/rts/Lua/LuaHandleSynced.h index 5769543daa1..6acda8aa859 100644 --- a/rts/Lua/LuaHandleSynced.h +++ b/rts/Lua/LuaHandleSynced.h @@ -57,6 +57,8 @@ class CSyncedLuaHandle : public CLuaHandle bool CommandFallback(const CUnit* unit, const Command& cmd) override; bool AllowCommand(const CUnit* unit, const Command& cmd, int playerNum, bool fromSynced, bool fromLua) override; + bool ResourceExcess(const std::map & excess) override; + std::pair AllowUnitCreation(const UnitDef* unitDef, const CUnit* builder, const BuildInfo* buildInfo) override; bool AllowUnitTransfer(const CUnit* unit, int newTeam, bool capture) override; bool AllowUnitBuildStep(const CUnit* builder, const CUnit* unit, float part) override; diff --git a/rts/Lua/LuaHashString.h b/rts/Lua/LuaHashString.h index 16821eccdbe..8de56c5b812 100644 --- a/rts/Lua/LuaHashString.h +++ b/rts/Lua/LuaHashString.h @@ -1,7 +1,6 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ -#ifndef LUA_HASH_STRING_H -#define LUA_HASH_STRING_H +#pragma once #include #include @@ -9,7 +8,6 @@ #include "LuaInclude.h" #include "System/StringHash.h" - struct LuaHashString { public: LuaHashString(const char* s): hash(lua_calchash(s, slen = strlen(s))) { @@ -96,31 +94,4 @@ struct LuaHashString { char str[strMaxLen]; uint32_t slen = 0; uint32_t hash = 0; -}; - - -// NOTE: scoped to avoid name conflicts -// NOTE: since all the following are static, if name can change (e.g. within a loop) -// peculiar things will happen. => Only use raw strings (and not variables) in name. - -#define HSTR_PUSH(L, name) \ - { lua_pushhstring(L, COMPILE_TIME_HASH(name), name, sizeof(name) - 1); } - -#define HSTR_PUSH_BOOL(L, name, val) \ - { HSTR_PUSH(L, name); lua_pushboolean(L, val); lua_rawset(L, -3); } - -#define HSTR_PUSH_NUMBER(L, name, val) \ - { HSTR_PUSH(L, name); lua_pushnumber(L, val); lua_rawset(L, -3); } - -#define HSTR_PUSH_STRING(L, name, val) \ - { HSTR_PUSH(L, name); lua_pushsstring(L, val); lua_rawset(L, -3); } - -#define HSTR_PUSH_CSTRING(L, name, val) \ - { HSTR_PUSH(L, name); lua_pushhstring(L, COMPILE_TIME_HASH(val), val, sizeof(val) - 1); lua_rawset(L, -3); } - -#define HSTR_PUSH_CFUNC(L, name, val) \ - { HSTR_PUSH(L, name); lua_pushcfunction(L, val); lua_rawset(L, -3); } - - -#endif // LUA_HASH_STRING_H - +}; \ No newline at end of file diff --git a/rts/Lua/LuaMathExtra.cpp b/rts/Lua/LuaMathExtra.cpp index 544a6298b4a..cde0d9d65c8 100644 --- a/rts/Lua/LuaMathExtra.cpp +++ b/rts/Lua/LuaMathExtra.cpp @@ -64,7 +64,7 @@ bool LuaMathExtra::PushEntries(lua_State* L) * @function math.hypot * @param x number * @param y number - * @return number `sqrt(x*x+y*y)` + * @return number hypotenuse `sqrt(x*x+y*y)` */ int LuaMathExtra::hypot(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -146,7 +146,7 @@ int LuaMathExtra::sgn(lua_State* L) { * @param x number * @param y number * @param a number - * @return number (x+(y-x)*a) + * @return number mixed (x+(y-x)*a) */ int LuaMathExtra::mix(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Lua/LuaMenu.cpp b/rts/Lua/LuaMenu.cpp index bafbc08be23..bce52197ec8 100644 --- a/rts/Lua/LuaMenu.cpp +++ b/rts/Lua/LuaMenu.cpp @@ -24,7 +24,6 @@ #include "System/FileSystem/FileHandler.h" #include "System/FileSystem/FileSystem.h" #include "System/Threading/SpringThreading.h" -#include "lib/luasocket/src/luasocket.h" #include "LuaUI.h" #include "System/Misc/TracyDefs.h" @@ -134,22 +133,6 @@ string CLuaMenu::LoadFile(const string& name) const } -void CLuaMenu::InitLuaSocket(lua_State* L) { - RECOIL_DETAILED_TRACY_ZONE; - std::string code; - std::string filename = "socket.lua"; - CFileHandler f(filename); - - LUA_OPEN_LIB(L, luaopen_socket_core); - - if (f.LoadStringData(code)) { - LoadCode(L, std::move(code), filename); - } else { - LOG_L(L_ERROR, "Error loading %s", filename.c_str()); - } -} - - bool CLuaMenu::RemoveSomeOpenGLFunctions(lua_State* L) { // remove some spring opengl functions that don't work preloading diff --git a/rts/Lua/LuaMenu.h b/rts/Lua/LuaMenu.h index 0c65859e90d..aa7a990475e 100644 --- a/rts/Lua/LuaMenu.h +++ b/rts/Lua/LuaMenu.h @@ -61,7 +61,6 @@ class CLuaMenu : public CLuaHandle static bool LoadUnsyncedReadFunctions(lua_State* L); static bool RemoveSomeOpenGLFunctions(lua_State* L); static bool PushGameVersion(lua_State* L); - void InitLuaSocket(lua_State* L); protected: QueuedAction queuedAction; }; diff --git a/rts/Lua/LuaObjectRendering.cpp b/rts/Lua/LuaObjectRendering.cpp index 2d7a73174ac..c6902a2d4d2 100644 --- a/rts/Lua/LuaObjectRendering.cpp +++ b/rts/Lua/LuaObjectRendering.cpp @@ -1,5 +1,16 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ + +/*** +Object rendering API — controls LOD, materials, and custom draw for units/features. +Registered as `Spring.UnitRendering` and `Spring.FeatureRendering`. +@see rts/Lua/LuaObjectRendering.cpp +*/ + +/*** +@class ObjectRenderingTable +*/ + #include "LuaObjectRendering.h" #include "LuaMaterial.h" @@ -106,9 +117,9 @@ void LuaObjectRenderingImpl::CreateMatRefMetatable(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; luaL_newmetatable(L, "MatRef"); - HSTR_PUSH_CFUNC(L, "__gc", material_gc); - HSTR_PUSH_CFUNC(L, "__index", material_index); - HSTR_PUSH_CFUNC(L, "__newindex", material_newindex); + LuaPushNamedCFunc(L, "__gc", material_gc); + LuaPushNamedCFunc(L, "__index", material_index); + LuaPushNamedCFunc(L, "__newindex", material_newindex); lua_pop(L, 1); } @@ -122,6 +133,13 @@ void LuaObjectRenderingImpl::PushFunction(lua_State* L, int (*fnPntr)(lua_State* +/*** Get the number of LOD levels and the current LOD. + * + * @function ObjectRenderingTable.GetLODCount + * @param objectID ObjectID + * @return integer lodCount + * @return integer currentLOD + */ int LuaObjectRenderingImpl::GetLODCount(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -137,6 +155,13 @@ int LuaObjectRenderingImpl::GetLODCount(lua_State* L) return 2; } +/*** Set the number of LOD levels. + * + * @function ObjectRenderingTable.SetLODCount + * @param objectID ObjectID + * @param lodCount integer + * @return nil + */ int LuaObjectRenderingImpl::SetLODCount(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -167,6 +192,14 @@ static int SetLODLengthCommon(lua_State* L, CSolidObject* obj, float scale) return 0; } +/*** Set the LOD transition length for a given level. + * + * @function ObjectRenderingTable.SetLODLength + * @param objectID ObjectID + * @param lodLevel integer + * @param lodLength number + * @return nil + */ int LuaObjectRenderingImpl::SetLODLength(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -174,6 +207,14 @@ int LuaObjectRenderingImpl::SetLODLength(lua_State* L) return (SetLODLengthCommon(L, ParseSolidObject(L, __func__, 1, GetObjectType()), 1.0f)); } +/*** Set the LOD transition distance for a given level (scaled for 45-degree FOV). + * + * @function ObjectRenderingTable.SetLODDistance + * @param objectID ObjectID + * @param lodLevel integer + * @param lodDistance number + * @return nil + */ int LuaObjectRenderingImpl::SetLODDistance(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -187,6 +228,15 @@ int LuaObjectRenderingImpl::SetLODDistance(lua_State* L) /******************************************************************************/ +/*** Set a display list for a piece at a given LOD and material. + * + * @function ObjectRenderingTable.SetPieceList + * @param objectID ObjectID + * @param lodLevel integer + * @param piece integer + * @param ... any + * @return nil + */ int LuaObjectRenderingImpl::SetPieceList(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -334,6 +384,14 @@ static LuaMatRef ParseMaterial(lua_State* L, int index, LuaMatType matType) { /******************************************************************************/ /******************************************************************************/ +/*** Get a material reference for a LOD level. + * + * @function ObjectRenderingTable.GetMaterial + * @param objectID ObjectID + * @param lodLevel integer + * @param materialName string + * @return userdata matRef + */ int LuaObjectRenderingImpl::GetMaterial(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -356,6 +414,15 @@ int LuaObjectRenderingImpl::GetMaterial(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Set a material for a LOD level. + * + * @function ObjectRenderingTable.SetMaterial + * @param objectID ObjectID + * @param lodLevel integer + * @param materialName string + * @param materialTable table + * @return nil + */ int LuaObjectRenderingImpl::SetMaterial(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -392,6 +459,14 @@ int LuaObjectRenderingImpl::SetMaterial(lua_State* L) } +/*** Set the last LOD level that uses a given material. + * + * @function ObjectRenderingTable.SetMaterialLastLOD + * @param objectID ObjectID + * @param materialName string + * @param lastLOD integer + * @return nil + */ int LuaObjectRenderingImpl::SetMaterialLastLOD(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -410,6 +485,15 @@ int LuaObjectRenderingImpl::SetMaterialLastLOD(lua_State* L) return 0; } +/*** Set display lists for a material. + * + * @function ObjectRenderingTable.SetMaterialDisplayLists + * @param objectID ObjectID + * @param lodLevel integer + * @param materialName string + * @param displayListTable table + * @return nil + */ int LuaObjectRenderingImpl::SetMaterialDisplayLists(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -498,7 +582,17 @@ static int SetMaterialUniform(lua_State* L, LuaObjType objType, LuaMatShader::Pa return 1; } +/*** @function ObjectRenderingTable.SetDeferredMaterialUniform + * @param objectID ObjectID + * @param ... any uniform values + * @return nil + */ int LuaObjectRenderingImpl::SetDeferredMaterialUniform(lua_State* L) { return (SetMaterialUniform(L, GetObjectType(), LuaMatShader::LUASHADER_PASS_DFR)); } +/*** @function ObjectRenderingTable.SetForwardMaterialUniform + * @param objectID ObjectID + * @param ... any uniform values + * @return nil + */ int LuaObjectRenderingImpl::SetForwardMaterialUniform(lua_State* L) { return (SetMaterialUniform(L, GetObjectType(), LuaMatShader::LUASHADER_PASS_FWD)); } @@ -537,7 +631,17 @@ static int ClearMaterialUniform(lua_State* L, LuaObjType objType, LuaMatShader:: return 1; } +/*** @function ObjectRenderingTable.ClearDeferredMaterialUniform + * @param objectID ObjectID + * @param ... any uniform indices + * @return nil + */ int LuaObjectRenderingImpl::ClearDeferredMaterialUniform(lua_State* L) { return (ClearMaterialUniform(L, GetObjectType(), LuaMatShader::LUASHADER_PASS_FWD)); } +/*** @function ObjectRenderingTable.ClearForwardMaterialUniform + * @param objectID ObjectID + * @param ... any uniform indices + * @return nil + */ int LuaObjectRenderingImpl::ClearForwardMaterialUniform(lua_State* L) { return (ClearMaterialUniform(L, GetObjectType(), LuaMatShader::LUASHADER_PASS_DFR)); } @@ -559,18 +663,39 @@ static int SetObjectLuaDraw(lua_State* L, ObjectType* obj) } +/*** Enable or disable custom Lua drawing for a unit. + * + * @function ObjectRenderingTable.SetUnitLuaDraw + * @param unitID UnitID + * @param enable boolean + * @return nil + */ int LuaObjectRenderingImpl::SetUnitLuaDraw(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; return (SetObjectLuaDraw(L, unitHandler.GetUnit(luaL_checkint(L, 1)))); } +/*** Enable or disable custom Lua drawing for a feature. + * + * @function ObjectRenderingTable.SetFeatureLuaDraw + * @param featureID FeatureID + * @param enable boolean + * @return nil + */ int LuaObjectRenderingImpl::SetFeatureLuaDraw(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; return (SetObjectLuaDraw(L, featureHandler.GetFeature(luaL_checkint(L, 1)))); } +/*** Enable or disable custom Lua drawing for a projectile. + * + * @function ObjectRenderingTable.SetProjectileLuaDraw + * @param projectileID ProjectileID + * @param enable boolean + * @return nil + */ int LuaObjectRenderingImpl::SetProjectileLuaDraw(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -602,6 +727,12 @@ static void PrintObjectLOD(const CSolidObject* obj, int lod) } +/*** Print debug info about the object's material data. + * + * @function ObjectRenderingTable.Debug + * @param objectID ObjectID + * @return nil + */ int LuaObjectRenderingImpl::Debug(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Lua/LuaOpenGL.cpp b/rts/Lua/LuaOpenGL.cpp index b588056ead2..dd7450fd6cc 100644 --- a/rts/Lua/LuaOpenGL.cpp +++ b/rts/Lua/LuaOpenGL.cpp @@ -10,6 +10,8 @@ #include "Rendering/GL/myGL.h" +#include +#include #include #include #include @@ -350,6 +352,8 @@ bool LuaOpenGL::PushEntries(lua_State* L) REGISTER_LUA_CFUNC(DeleteTexture); REGISTER_LUA_CFUNC(TextureInfo); REGISTER_LUA_CFUNC(CopyToTexture); + if (GLAD_GL_ARB_copy_image) + REGISTER_LUA_CFUNC(CopyImageSubData); if (FBO::IsSupported()) { // FIXME: obsolete REGISTER_LUA_CFUNC(DeleteTextureFBO); @@ -1187,6 +1191,7 @@ int LuaOpenGL::GetNumber(lua_State* L) * Get a string describing the current OpenGL connection. * @function gl.GetString * @param pname GL + * @return string value `"[NULL]"` if the driver returns no string for `pname`. */ int LuaOpenGL::GetString(lua_State* L) { @@ -1220,8 +1225,8 @@ int LuaOpenGL::GetScreenViewTrans(lua_State* L) /*** * @function gl.GetViewSizes - * @return number x - * @return number y + * @return integer x + * @return integer y */ int LuaOpenGL::GetViewSizes(lua_State* L) { @@ -1369,7 +1374,7 @@ int LuaOpenGL::EndText(lua_State* L) * @param text string * @param x number * @param y number - * @param size number + * @param size number? * @param options string? concatenated string of option characters. * * - horizontal alignment: @@ -1540,7 +1545,7 @@ static bool GLObjectDrawWithLuaMat(lua_State* L, CSolidObject* obj, LuaObjType o * Pushes or pops the model render state for the given object. * * Parses params, starting at param 2: - * @param teamID integer + * @param teamID TeamID * @param rawState boolean? (Default: `true`) * @param toScreen boolean? (Default: `false`) * @param opaque boolean? (Default: `true`) If `true`, draw opaque; if `false`, draw alpha. @@ -1665,7 +1670,7 @@ int LuaOpenGL::UnitCommon(lua_State* L, bool applyTransform, bool callDrawUnit) * Draw the unit, applying transform. * * @function gl.Unit - * @param unitID integer + * @param unitID UnitID * @param doRawDraw boolean? (Default: `false`) * @param useLuaMat integer? * @param noLuaCall boolean? (Default: `false`) Skip the `DrawUnit` callin. @@ -1680,7 +1685,7 @@ int LuaOpenGL::Unit(lua_State* L) { return (UnitCommon(L, true, true)); } * recursion is blocked. * * @function gl.UnitRaw - * @param unitID integer + * @param unitID UnitID * @param doRawDraw boolean? (Default: `false`) * @param useLuaMat integer? * @param noLuaCall boolean? (Default: `true`) Skip the `DrawUnit` callin. @@ -1690,7 +1695,7 @@ int LuaOpenGL::UnitRaw(lua_State* L) { return (UnitCommon(L, false, false)); } /*** * @function gl.UnitTextures - * @param unitID integer + * @param unitID UnitID * @param push boolean If `true`, push the render state; if `false`, pop it. */ int LuaOpenGL::UnitTextures(lua_State* L) @@ -1702,8 +1707,8 @@ int LuaOpenGL::UnitTextures(lua_State* L) /*** * @function gl.UnitShape - * @param unitDefID integer - * @param teamID integer + * @param unitDefID UnitDefID + * @param teamID TeamID * @param rawState boolean? (Default: `true`) * @param toScreen boolean? (Default: `false`) * @param opaque boolean? (Default: `true`) If `true`, draw opaque; if `false`, draw alpha. @@ -1717,7 +1722,7 @@ int LuaOpenGL::UnitShape(lua_State* L) /*** * @function gl.UnitShapeTextures - * @param unitDefID integer + * @param unitDefID UnitDefID * @param push boolean If `true`, push the render state; if `false`, pop it. */ int LuaOpenGL::UnitShapeTextures(lua_State* L) @@ -1730,7 +1735,7 @@ int LuaOpenGL::UnitShapeTextures(lua_State* L) /*** * @function gl.UnitMultMatrix - * @param unitID integer + * @param unitID UnitID */ int LuaOpenGL::UnitMultMatrix(lua_State* L) { @@ -1748,7 +1753,7 @@ int LuaOpenGL::UnitMultMatrix(lua_State* L) /*** * @function gl.UnitPiece - * @param unitID integer + * @param unitID UnitID * @param pieceID integer */ int LuaOpenGL::UnitPiece(lua_State* L) @@ -1759,14 +1764,14 @@ int LuaOpenGL::UnitPiece(lua_State* L) /*** * @function gl.UnitPieceMatrix - * @param unitID integer + * @param unitID UnitID * @param pieceID integer */ int LuaOpenGL::UnitPieceMatrix(lua_State* L) {return (UnitPieceMultMatrix(L)); } /*** * @function gl.UnitPieceMultMatrix - * @param unitID integer + * @param unitID UnitID * @param pieceID integer */ int LuaOpenGL::UnitPieceMultMatrix(lua_State* L) @@ -1833,7 +1838,7 @@ int LuaOpenGL::FeatureCommon(lua_State* L, bool applyTransform, bool callDrawFea * Draw the feature, applying transform. * * @function gl.Feature - * @param featureID integer + * @param featureID FeatureID * @param doRawDraw boolean? (Default: `false`) * @param useLuaMat integer? * @param noLuaCall boolean? (Default: `false`) Skip the `DrawFeature` callin. @@ -1847,7 +1852,7 @@ int LuaOpenGL::Feature(lua_State* L) { return (FeatureCommon(L, true, true)); } * recursion is blocked. * @function gl.FeatureRaw - * @param featureID integer + * @param featureID FeatureID * @param doRawDraw boolean? (Default: `false`) * @param useLuaMat integer? * @param noLuaCall boolean? (Default: `true`) Skip the `DrawFeature` callin. @@ -1856,7 +1861,7 @@ int LuaOpenGL::FeatureRaw(lua_State* L) { return (FeatureCommon(L, false, false) /*** * @function gl.FeatureTextures - * @param featureID integer + * @param featureID FeatureID * @param push boolean If `true`, push the render state; if `false`, pop it. */ int LuaOpenGL::FeatureTextures(lua_State* L) @@ -1868,8 +1873,8 @@ int LuaOpenGL::FeatureTextures(lua_State* L) /*** * @function gl.FeatureShape - * @param featureDefID integer - * @param teamID integer + * @param featureDefID FeatureDefID + * @param teamID TeamID * @param rawState boolean? (Default: `true`) * @param toScreen boolean? (Default: `false`) * @param opaque boolean? (Default: `true`) If `true`, draw opaque; if `false`, draw alpha. @@ -1883,7 +1888,7 @@ int LuaOpenGL::FeatureShape(lua_State* L) /*** * @function gl.FeatureShapeTextures - * @param featureDefID integer + * @param featureDefID FeatureDefID * @param push boolean If `true`, push the render state; if `false`, pop it. */ int LuaOpenGL::FeatureShapeTextures(lua_State* L) @@ -1896,7 +1901,7 @@ int LuaOpenGL::FeatureShapeTextures(lua_State* L) /*** * @function gl.FeatureMultMatrix - * @param featureID integer + * @param featureID FeatureID */ int LuaOpenGL::FeatureMultMatrix(lua_State* L) { @@ -1914,7 +1919,7 @@ int LuaOpenGL::FeatureMultMatrix(lua_State* L) /*** * @function gl.FeaturePiece - * @param featureID integer + * @param featureID FeatureID * @param pieceID integer */ int LuaOpenGL::FeaturePiece(lua_State* L) @@ -1926,7 +1931,7 @@ int LuaOpenGL::FeaturePiece(lua_State* L) /*** * @function gl.FeaturePieceMatrix - * @param featureID integer + * @param featureID FeatureID * @param pieceID integer */ int LuaOpenGL::FeaturePieceMatrix(lua_State* L) { return (FeaturePieceMultMatrix(L)); } @@ -1934,7 +1939,7 @@ int LuaOpenGL::FeaturePieceMatrix(lua_State* L) { return (FeaturePieceMultMatrix /*** * @function gl.FeaturePieceMultMatrix - * @param featureID integer + * @param featureID FeatureID * @param pieceID integer */ int LuaOpenGL::FeaturePieceMultMatrix(lua_State* L) @@ -1951,7 +1956,7 @@ int LuaOpenGL::FeaturePieceMultMatrix(lua_State* L) /*** * @function gl.DrawListAtUnit - * @param unitID integer + * @param unitID UnitID * @param listIndex integer * @param useMidPos boolean? (Default: `true`) * @param scaleX number? (Default: `1.0`) @@ -2008,7 +2013,7 @@ int LuaOpenGL::DrawListAtUnit(lua_State* L) /*** * @function gl.DrawFuncAtUnit - * @param unitID integer + * @param unitID UnitID * @param useMidPos boolean? (Default: `true`) * @param fun(...) func Function to call. * @param ... any Arguments passed to function. @@ -2065,8 +2070,8 @@ int LuaOpenGL::DrawFuncAtUnit(lua_State* L) * @param radius number * @param resolution integer * @param slope number - * @param gravity number - * @param weaponDefID integer + * @param gravity number? + * @param weaponDefID WeaponDefID? */ int LuaOpenGL::DrawGroundCircle(lua_State* L) { @@ -2109,7 +2114,7 @@ int LuaOpenGL::DrawGroundCircle(lua_State* L) /*** - * @function gl.DrawGroundCircle + * @function gl.DrawGroundQuad * @param x0 number * @param z0 number * @param x1 number @@ -2118,7 +2123,7 @@ int LuaOpenGL::DrawGroundCircle(lua_State* L) * @param useTxcd boolean? (Default: `false`) */ /*** - * @function gl.DrawGroundCircle + * @function gl.DrawGroundQuad * @param x0 number * @param z0 number * @param x1 number @@ -2825,7 +2830,7 @@ int LuaOpenGL::Rect(lua_State* L) /*** - * @function gl.Rect + * @function gl.TexRect * @param x1 number * @param y1 number * @param x2 number @@ -2834,7 +2839,7 @@ int LuaOpenGL::Rect(lua_State* L) * @param flipTCoords boolean? */ /*** - * @function gl.Rect + * @function gl.TexRect * @param x1 number * @param y1 number * @param x2 number @@ -2901,7 +2906,7 @@ int LuaOpenGL::TexRect(lua_State* L) /*** * @function gl.DispatchCompute - * @param numGroupX integer + * @param numGroupX integer? * @param numGroupY integer * @param numGroupZ integer * @param barriers integer? (Default: `0`) @@ -4316,14 +4321,14 @@ int LuaOpenGL::TextureInfo(lua_State* L) lua_createtable(L, 0, 5); const auto [xsize, ysize, zsize] = tex.GetSize(); - HSTR_PUSH_NUMBER(L, "xsize", xsize) - HSTR_PUSH_NUMBER(L, "ysize", ysize) - HSTR_PUSH_NUMBER(L, "zsize", zsize) - - HSTR_PUSH_NUMBER(L, "id" , tex.GetTextureID()); - HSTR_PUSH_NUMBER(L, "target", tex.GetTextureTarget()); - // HSTR_PUSH_BOOL(L, "alpha", texInfo.alpha); FIXME - // HSTR_PUSH_NUMBER(L, "type", texInfo.type); + LuaPushNamedNumber(L, "xsize", xsize); + LuaPushNamedNumber(L, "ysize", ysize); + LuaPushNamedNumber(L, "zsize", zsize); + + LuaPushNamedNumber(L, "id" , tex.GetTextureID()); + LuaPushNamedNumber(L, "target", tex.GetTextureTarget()); + // LuaPushNamedBool (L, "alpha", texInfo.alpha); // FIXME + // LuaPushNamedNumber(L, "type", texInfo.type); return 1; } @@ -4375,6 +4380,131 @@ int LuaOpenGL::CopyToTexture(lua_State* L) } +/*** + * @function gl.CopyImageSubData + * @param srcName string + * @param srcLevel integer + * @param srcX integer + * @param srcY integer + * @param srcZ integer + * @param dstName string + * @param dstLevel integer + * @param dstX integer + * @param dstY integer + * @param dstZ integer + * @param width integer + * @param height integer + * @param depth integer + */ +int LuaOpenGL::CopyImageSubData(lua_State* L) +{ + CheckDrawingEnabled(L, __func__); + + const auto CheckInteger = [L](int index) { + const lua_Number value = luaL_checknumber(L, index); + + if (!std::isfinite(value) || value != std::trunc(value) || value < std::numeric_limits::min() || value > std::numeric_limits::max()) + luaL_argerror(L, index, "integer out of range"); + + return static_cast(value); + }; + + LuaMatTexture srcTex; + + if (!LuaOpenGLUtils::ParseTextureImage(L, srcTex, luaL_checkstring(L, 1))) + luaL_error(L, "gl.CopyImageSubData() invalid source texture"); + + // ParseTextureImage accepts unresolved named textures. + if (srcTex.GetTextureID() == 0) + luaL_error(L, "gl.CopyImageSubData() source texture does not exist"); + + const std::string& dstName = luaL_checkstring(L, 6); + + if (dstName[0] != LuaTextures::prefix) // '!' + luaL_error(L, "gl.CopyImageSubData() can only write to lua textures"); + + const LuaTextures& textures = CLuaHandle::GetActiveTextures(L); + const LuaTextures::Texture* dstTex = textures.GetInfo(dstName); + + if (dstTex == nullptr) + luaL_error(L, "gl.CopyImageSubData() unknown destination texture"); + + const GLint srcLevel = CheckInteger(2); + const GLint srcX = CheckInteger(3); + const GLint srcY = CheckInteger(4); + const GLint srcZ = CheckInteger(5); + const GLint dstLevel = CheckInteger(7); + const GLint dstX = CheckInteger(8); + const GLint dstY = CheckInteger(9); + const GLint dstZ = CheckInteger(10); + const GLsizei width = CheckInteger(11); + const GLsizei height = CheckInteger(12); + const GLsizei depth = CheckInteger(13); + + if (srcLevel < 0 || dstLevel < 0 || srcX < 0 || srcY < 0 || srcZ < 0 || dstX < 0 || dstY < 0 || dstZ < 0 || width <= 0 || height <= 0 || depth <= 0) + luaL_error(L, "gl.CopyImageSubData() negative offset or non-positive size"); + + const auto LevelSize = [](int size, GLint level) { + return (level < std::numeric_limits::digits)? std::max(1, size >> level): 1; + }; + const auto ImageSize = [&LevelSize](GLenum target, int x, int y, int z, GLint level) { + switch (target) { + case GL_TEXTURE_1D: return std::tuple(LevelSize(x, level), 1, 1); + case GL_TEXTURE_2D: return std::tuple(LevelSize(x, level), LevelSize(y, level), 1); + case GL_TEXTURE_2D_ARRAY: return std::tuple(LevelSize(x, level), LevelSize(y, level), z); + case GL_TEXTURE_3D: return std::tuple(LevelSize(x, level), LevelSize(y, level), LevelSize(z, level)); + case GL_TEXTURE_CUBE_MAP: return std::tuple(LevelSize(x, level), LevelSize(y, level), 6); + case GL_TEXTURE_2D_MULTISAMPLE: return std::tuple(x, y, 1); + default: return std::tuple(0, 0, 0); + } + }; + const auto RegionFits = [](GLint offset, GLsizei extent, int size) { + return (size <= 0 || (offset <= size && extent <= size - offset)); + }; + const auto MaxMipLevel = [](GLenum target, int x, int y, int z) { + int size = x; + + if (target != GL_TEXTURE_1D) + size = std::max(size, y); + if (target == GL_TEXTURE_3D) + size = std::max(size, z); + + GLint level = 0; + while (size > 1) { + size >>= 1; + ++level; + } + + return level; + }; + + const auto [srcXSize, srcYSize, srcZSize] = srcTex.GetSize(); + const GLenum srcTarget = srcTex.GetTextureTarget(); + const auto [srcLevelX, srcLevelY, srcLevelZ] = ImageSize(srcTarget, srcXSize, srcYSize, srcZSize, srcLevel); + const auto [dstLevelX, dstLevelY, dstLevelZ] = ImageSize(dstTex->target, dstTex->xsize, dstTex->ysize, dstTex->zsize, dstLevel); + + if ((srcXSize > 0 && srcLevel > MaxMipLevel(srcTarget, srcXSize, srcYSize, srcZSize)) || dstLevel > MaxMipLevel(dstTex->target, dstTex->xsize, dstTex->ysize, dstTex->zsize)) + luaL_error(L, "gl.CopyImageSubData() mip level out of bounds"); + + if ((srcTarget == GL_TEXTURE_2D_MULTISAMPLE && srcLevel != 0) || (dstTex->target == GL_TEXTURE_2D_MULTISAMPLE && dstLevel != 0)) + luaL_error(L, "gl.CopyImageSubData() invalid multisample mip level"); + + if (!RegionFits(srcX, width, srcLevelX) || !RegionFits(srcY, height, srcLevelY) || !RegionFits(srcZ, depth, srcLevelZ)) + luaL_error(L, "gl.CopyImageSubData() source region out of bounds"); + + if (!RegionFits(dstX, width, dstLevelX) || !RegionFits(dstY, height, dstLevelY) || !RegionFits(dstZ, depth, dstLevelZ)) + luaL_error(L, "gl.CopyImageSubData() destination region out of bounds"); + + glCopyImageSubData( + srcTex.GetTextureID(), srcTarget, srcLevel, srcX, srcY, srcZ, + dstTex->id, dstTex->target, dstLevel, dstX, dstY, dstZ, + width, height, depth + ); + + return 0; +} + + // FIXME: obsolete /*** * @function gl.RenderToTexture @@ -4490,13 +4620,13 @@ int LuaOpenGL::ActiveTexture(lua_State* L) /*** - * @function gl.TextEnv + * @function gl.TexEnv * @param target GL * @param pname GL * @param value number */ /*** - * @function gl.TextEnv + * @function gl.TexEnv * @param target GL * @param pname GL * @param r number? (Default: `0.0`) @@ -4538,7 +4668,7 @@ int LuaOpenGL::TexEnv(lua_State* L) * @param texNum integer * @param target GL * @param pname GL - * @param value number + * @param value number? */ /*** * @function gl.MultiTexEnv @@ -4609,7 +4739,7 @@ static void SetTexGenState(GLenum target, bool state) * @function gl.TexGen * @param target GL * @param pname GL - * @param value number + * @param value number? */ /*** * @function gl.TexGen @@ -4669,7 +4799,7 @@ int LuaOpenGL::TexGen(lua_State* L) * @param texNum integer * @param target GL * @param pname GL - * @param value number + * @param value number? */ /*** * @function gl.MultiTexGen @@ -5073,6 +5203,10 @@ int LuaOpenGL::GetEngineAtlasTextures(lua_State* L) { /******************************************************************************/ +/*** + * @function gl.Clear + * @param bits GL any buffer bit mask (e.g. `GL.STENCIL_BUFFER_BIT`). Clears with current/default values. + */ /*** * @function gl.Clear * @param bits GL `GL.DEPTH_BUFFER_BIT` or `GL.STENCIL_BUFFER_BIT`. @@ -5681,22 +5815,22 @@ int LuaOpenGL::PushPopMatrix(lua_State* L) * @function gl.GetMatrixData * @param type GL Matrix type (`GL.PROJECTION`, `GL.MODELVIEW`, `GL.TEXTURE`). * @param index integer Matrix index in range `[1, 16]`. - * @return number The value. + * @return number value The value at the given index. */ /*** * @function gl.GetMatrixData * @param type GL Matrix type (`GL.PROJECTION`, `GL.MODELVIEW`, `GL.TEXTURE`). - * @return Matrix4x4 The matrix. + * @return Matrix4x4 */ /*** * @function gl.GetMatrixData * @param index integer Matrix index in range `[1, 16]`. - * @return number The value. + * @return number value The value at the given index. */ /*** * @function gl.GetMatrixData - * @param name MatrixName The matrix name. - * @return Matrix4x4 The matrix. + * @param name MatrixName + * @return Matrix4x4 */ int LuaOpenGL::GetMatrixData(lua_State* L) { @@ -5845,9 +5979,9 @@ int LuaOpenGL::GetFixedState(lua_State* L) GLint val = 0; glGetIntegerv(key, &val); \ if (toStr) { \ auto strIter = fixedStateEnumToString.find(val); \ - HSTR_PUSH_STRING(L, #key, strIter != fixedStateEnumToString.end() ? strIter->second : fixedStateEnumToStringUnk); \ + LuaPushNamedString(L, #key, strIter != fixedStateEnumToString.end() ? strIter->second : fixedStateEnumToStringUnk); \ } else { \ - HSTR_PUSH_NUMBER(L, #key, val); \ + LuaPushNamedNumber(L, #key, val); \ }; \ } @@ -5891,10 +6025,10 @@ int LuaOpenGL::GetFixedState(lua_State* L) glGetIntegerv(GL_SCISSOR_BOX, rect); lua_createtable(L, 0, 4); - HSTR_PUSH_NUMBER(L, "GL_SCISSOR_BOX_X", rect[0]); - HSTR_PUSH_NUMBER(L, "GL_SCISSOR_BOX_Y", rect[1]); - HSTR_PUSH_NUMBER(L, "GL_SCISSOR_BOX_W", rect[2]); - HSTR_PUSH_NUMBER(L, "GL_SCISSOR_BOX_H", rect[3]); + LuaPushNamedNumber(L, "GL_SCISSOR_BOX_X", rect[0]); + LuaPushNamedNumber(L, "GL_SCISSOR_BOX_Y", rect[1]); + LuaPushNamedNumber(L, "GL_SCISSOR_BOX_W", rect[2]); + LuaPushNamedNumber(L, "GL_SCISSOR_BOX_H", rect[3]); return 2; } break; @@ -5911,10 +6045,10 @@ int LuaOpenGL::GetFixedState(lua_State* L) glGetBooleanv(GL_COLOR_WRITEMASK, mask); lua_createtable(L, 0, 4); - HSTR_PUSH_BOOL(L, "GL_COLOR_WRITEMASK_R", mask[0]); - HSTR_PUSH_BOOL(L, "GL_COLOR_WRITEMASK_G", mask[1]); - HSTR_PUSH_BOOL(L, "GL_COLOR_WRITEMASK_B", mask[2]); - HSTR_PUSH_BOOL(L, "GL_COLOR_WRITEMASK_A", mask[3]); + LuaPushNamedBool(L, "GL_COLOR_WRITEMASK_R", mask[0]); + LuaPushNamedBool(L, "GL_COLOR_WRITEMASK_G", mask[1]); + LuaPushNamedBool(L, "GL_COLOR_WRITEMASK_B", mask[2]); + LuaPushNamedBool(L, "GL_COLOR_WRITEMASK_A", mask[3]); return 1; } break; @@ -5946,7 +6080,7 @@ int LuaOpenGL::GetFixedState(lua_State* L) lua_createtable(L, 0, 2); PushFixedState(GL_ALPHA_TEST_FUNC); - HSTR_PUSH_NUMBER(L, "GL_ALPHA_TEST_REF", alphaRef); + LuaPushNamedNumber(L, "GL_ALPHA_TEST_REF", alphaRef); return 2; } break; @@ -5966,14 +6100,14 @@ int LuaOpenGL::GetFixedState(lua_State* L) glGetFloatv(GL_FOG_END, &fogEnd); lua_createtable(L, 0, 8); - HSTR_PUSH_NUMBER(L, "GL_FOG_COLOR_R", fogColor[0]); - HSTR_PUSH_NUMBER(L, "GL_FOG_COLOR_G", fogColor[1]); - HSTR_PUSH_NUMBER(L, "GL_FOG_COLOR_B", fogColor[2]); - HSTR_PUSH_NUMBER(L, "GL_FOG_COLOR_A", fogColor[3]); + LuaPushNamedNumber(L, "GL_FOG_COLOR_R", fogColor[0]); + LuaPushNamedNumber(L, "GL_FOG_COLOR_G", fogColor[1]); + LuaPushNamedNumber(L, "GL_FOG_COLOR_B", fogColor[2]); + LuaPushNamedNumber(L, "GL_FOG_COLOR_A", fogColor[3]); - HSTR_PUSH_NUMBER(L, "GL_FOG_DENSITY", fogDensity); - HSTR_PUSH_NUMBER(L, "GL_FOG_START", fogStart); - HSTR_PUSH_NUMBER(L, "GL_FOG_END", fogEnd); + LuaPushNamedNumber(L, "GL_FOG_DENSITY", fogDensity); + LuaPushNamedNumber(L, "GL_FOG_START", fogStart); + LuaPushNamedNumber(L, "GL_FOG_END", fogEnd); PushFixedState(GL_FOG_MODE); @@ -6003,8 +6137,8 @@ int LuaOpenGL::GetFixedState(lua_State* L) glGetIntegerv(GL_LINE_STIPPLE_REPEAT, &strippleRepeat); lua_createtable(L, 0, 2); - HSTR_PUSH_NUMBER(L, "GL_LINE_STIPPLE_PATTERN", stripplePattern); - HSTR_PUSH_NUMBER(L, "GL_LINE_STIPPLE_REPEAT", strippleRepeat); + LuaPushNamedNumber(L, "GL_LINE_STIPPLE_PATTERN", stripplePattern); + LuaPushNamedNumber(L, "GL_LINE_STIPPLE_REPEAT", strippleRepeat); return 1; } break; @@ -6024,11 +6158,11 @@ int LuaOpenGL::GetFixedState(lua_State* L) glGetFloatv(GL_POLYGON_OFFSET_UNITS, &offsetDensity); lua_createtable(L, 0, 5); - HSTR_PUSH_BOOL(L, "GL_POLYGON_OFFSET_FILL", glIsEnabled(GL_POLYGON_OFFSET_FILL)); - HSTR_PUSH_BOOL(L, "GL_POLYGON_OFFSET_LINE", glIsEnabled(GL_POLYGON_OFFSET_LINE)); - HSTR_PUSH_BOOL(L, "GL_POLYGON_OFFSET_POINT", glIsEnabled(GL_POLYGON_OFFSET_POINT)); - HSTR_PUSH_NUMBER(L, "GL_POLYGON_OFFSET_FACTOR", offsetFactor); - HSTR_PUSH_NUMBER(L, "GL_POLYGON_OFFSET_UNITS", offsetDensity); + LuaPushNamedBool (L, "GL_POLYGON_OFFSET_FILL", glIsEnabled(GL_POLYGON_OFFSET_FILL)); + LuaPushNamedBool (L, "GL_POLYGON_OFFSET_LINE", glIsEnabled(GL_POLYGON_OFFSET_LINE)); + LuaPushNamedBool (L, "GL_POLYGON_OFFSET_POINT", glIsEnabled(GL_POLYGON_OFFSET_POINT)); + LuaPushNamedNumber(L, "GL_POLYGON_OFFSET_FACTOR", offsetFactor); + LuaPushNamedNumber(L, "GL_POLYGON_OFFSET_UNITS", offsetDensity); return 2; } break; @@ -6045,12 +6179,12 @@ int LuaOpenGL::GetFixedState(lua_State* L) glGetIntegerv(GL_STENCIL_VALUE_MASK, &stencilValueMask); lua_createtable(L, 0, 8); - HSTR_PUSH_NUMBER(L, "GL_STENCIL_WRITEMASK", stencilWriteMask); + LuaPushNamedNumber(L, "GL_STENCIL_WRITEMASK", stencilWriteMask); - HSTR_PUSH_NUMBER(L, "GL_STENCIL_BITS", stencilBits); + LuaPushNamedNumber(L, "GL_STENCIL_BITS", stencilBits); - HSTR_PUSH_NUMBER(L, "GL_STENCIL_VALUE_MASK", stencilValueMask); - HSTR_PUSH_NUMBER(L, "GL_STENCIL_REF", stencilValueMask); + LuaPushNamedNumber(L, "GL_STENCIL_VALUE_MASK", stencilValueMask); + LuaPushNamedNumber(L, "GL_STENCIL_REF", stencilValueMask); PushFixedState(GL_STENCIL_FUNC); @@ -6064,9 +6198,9 @@ int LuaOpenGL::GetFixedState(lua_State* L) GLint stencilBackRef; glGetIntegerv(GL_STENCIL_BACK_REF, &stencilBackRef); - HSTR_PUSH_NUMBER(L, "GL_STENCIL_BACK_WRITEMASK", stencilBackWriteMask); - HSTR_PUSH_NUMBER(L, "GL_STENCIL_BACK_VALUE_MASK", stencilBackValueMask); - HSTR_PUSH_NUMBER(L, "GL_STENCIL_BACK_REF", stencilBackRef); + LuaPushNamedNumber(L, "GL_STENCIL_BACK_WRITEMASK", stencilBackWriteMask); + LuaPushNamedNumber(L, "GL_STENCIL_BACK_VALUE_MASK", stencilBackValueMask); + LuaPushNamedNumber(L, "GL_STENCIL_BACK_REF", stencilBackRef); PushFixedState(GL_STENCIL_BACK_FUNC); } @@ -6119,6 +6253,7 @@ int LuaOpenGL::GetFixedState(lua_State* L) * @function gl.CreateList * @param func fun() * @param ... any Arguments to the function. + * @return integer listID `0` if the list could not be generated or `func` errored. */ int LuaOpenGL::CreateList(lua_State* L) { @@ -6291,7 +6426,7 @@ static void PushPixelData(lua_State* L, int fSize, const float*& data) * @param w 1 * @param h 1 * @param format GL? (Default: `GL.RGBA`) - * @return number ... Color values (color size based on format). + * @return number ... Color value (color size based on format). */ /*** * Get column of pixels. @@ -6301,7 +6436,7 @@ static void PushPixelData(lua_State* L, int fSize, const float*& data) * @param w 1 * @param h integer * @param format GL? (Default: `GL.RGBA`) - * @return number[][] Column of color values (color size based on format). + * @return number[][] colors Column of color values (color size based on format). */ /*** * Get row of pixels. @@ -6311,17 +6446,17 @@ static void PushPixelData(lua_State* L, int fSize, const float*& data) * @param w integer * @param h 1 * @param format GL? (Default: `GL.RGBA`) - * @return number[][] Row of color values (color size based on format). + * @return number[][] colors Row of color values (color size based on format). */ /*** - * Get row of pixels. + * Get columns of pixels. * @function gl.ReadPixels * @param x integer * @param y integer * @param w integer * @param h integer * @param format GL? (Default: `GL.RGBA`) - * @return number[][][] Array of columns of color values (color size based on format). + * @return number[][][] colors Array of columns of color values (color size based on format). */ int LuaOpenGL::ReadPixels(lua_State* L) { @@ -6397,10 +6532,10 @@ int LuaOpenGL::ReadPixels(lua_State* L) /*** * @class SaveImageOptions * @x_helper - * @field alpha boolean (Default: `false`) - * @field yflip boolean (Default: `true`) - * @field grayscale16bit boolean (Default: `false`) - * @field readbuffer GL (Default: current read buffer) + * @field alpha boolean? (Default: `false`) + * @field yflip boolean? (Default: `true`) + * @field grayscale16bit boolean? (Default: `false`) + * @field readbuffer GL? (Default: current read buffer) */ /*** @@ -6606,7 +6741,7 @@ int LuaOpenGL::GetQuery(lua_State* L) /*** * @function gl.GetGlobalTexNames - * @return string[] List of texture names. + * @return string[] texNames List of texture names. */ int LuaOpenGL::GetGlobalTexNames(lua_State* L) { @@ -7081,7 +7216,7 @@ int LuaOpenGL::ObjectLabel(lua_State* L) { * @function gl.PushDebugGroup * @param id integer A numeric identifier for the group, can be any unique number. * @param message string A human-readable string describing the debug group. Will be truncated if longer than driver-specific limit - * @param sourceIsThirdParty boolean Set the source tag, true for GL_DEBUG_SOURCE_THIRD_PARTY, false for GL_DEBUG_SOURCE_APPLICATION. default false + * @param sourceIsThirdParty boolean? Set the source tag, true for GL_DEBUG_SOURCE_THIRD_PARTY, false for GL_DEBUG_SOURCE_APPLICATION. default false * @return nil */ int LuaOpenGL::PushDebugGroup(lua_State* L) { diff --git a/rts/Lua/LuaOpenGL.h b/rts/Lua/LuaOpenGL.h index fbe3597eafa..827fa54c68e 100644 --- a/rts/Lua/LuaOpenGL.h +++ b/rts/Lua/LuaOpenGL.h @@ -231,6 +231,7 @@ class LuaOpenGL { static int DeleteTextureFBO(lua_State* L); static int TextureInfo(lua_State* L); static int CopyToTexture(lua_State* L); + static int CopyImageSubData(lua_State* L); static int RenderToTexture(lua_State* L); static int GenerateMipmap(lua_State* L); static int ActiveTexture(lua_State* L); diff --git a/rts/Lua/LuaParser.cpp b/rts/Lua/LuaParser.cpp index 45c44ebf9e4..6ca8394fc43 100644 --- a/rts/Lua/LuaParser.cpp +++ b/rts/Lua/LuaParser.cpp @@ -501,12 +501,13 @@ void LuaParser::AddString(int key, const std::string& value) int LuaParser::TimeCheck(lua_State* L) { - #if (!defined(UNITSYNC) && !defined(DEDICATED)) if (!lua_isstring(L, 1) || !lua_isfunction(L, 2)) luaL_error(L, "Invalid arguments to TimeCheck('string', func, ...)"); { + #if (!defined(UNITSYNC) && !defined(DEDICATED)) ScopedOnceTimer timer(lua_tostring(L, 1)); + #endif lua_remove(L, 1); @@ -519,9 +520,6 @@ int LuaParser::TimeCheck(lua_State* L) } return lua_gettop(L); - #else - return 0; - #endif } diff --git a/rts/Lua/LuaPathFinder.cpp b/rts/Lua/LuaPathFinder.cpp index 763a53f123f..6d42d9d1063 100644 --- a/rts/Lua/LuaPathFinder.cpp +++ b/rts/Lua/LuaPathFinder.cpp @@ -201,9 +201,9 @@ static int path_gc(lua_State* L) static void CreatePathMetatable(lua_State* L) { luaL_newmetatable(L, "Path"); - HSTR_PUSH_CFUNC(L, "__gc", path_gc); - HSTR_PUSH_CFUNC(L, "__index", path_index); - HSTR_PUSH_CFUNC(L, "__newindex", path_newindex); + LuaPushNamedCFunc(L, "__gc", path_gc); + LuaPushNamedCFunc(L, "__index", path_index); + LuaPushNamedCFunc(L, "__newindex", path_newindex); lua_pop(L, 1); } @@ -211,6 +211,18 @@ static void CreatePathMetatable(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** + * @function Spring.RequestPath + * @param moveID integer|string + * @param startX number + * @param startY number + * @param startZ number + * @param endX number + * @param endY number + * @param endZ number + * @param radius number? + * @return userdata? path + */ int LuaPathFinder::RequestPath(lua_State* L) { const MoveDef* moveDef = nullptr; @@ -250,6 +262,13 @@ int LuaPathFinder::RequestPath(lua_State* L) +/*** + * @function Spring.InitPathNodeCostsArray + * @param overlayIndex integer + * @param sizeX integer + * @param sizeZ integer + * @return boolean success + */ int LuaPathFinder::InitPathNodeCostsArray(lua_State* L) { const unsigned int overlayIndex = luaL_checkint(L, 1); @@ -282,6 +301,11 @@ int LuaPathFinder::InitPathNodeCostsArray(lua_State* L) return 1; } +/*** + * @function Spring.FreePathNodeCostsArray + * @param overlayIndex integer + * @return boolean success + */ int LuaPathFinder::FreePathNodeCostsArray(lua_State* L) { const unsigned int overlayIndex = luaL_checkint(L, 1); @@ -315,6 +339,11 @@ int LuaPathFinder::FreePathNodeCostsArray(lua_State* L) +/*** + * @function Spring.SetPathNodeCosts + * @param overlayIndex integer + * @return boolean success + */ int LuaPathFinder::SetPathNodeCosts(lua_State* L) { const unsigned int overlayIndex = luaL_checkint(L, 1); @@ -339,6 +368,11 @@ int LuaPathFinder::SetPathNodeCosts(lua_State* L) return 1; } +/*** + * @function Spring.GetPathNodeCosts + * @param overlayIndex integer + * @return boolean|table costs + */ int LuaPathFinder::GetPathNodeCosts(lua_State* L) { const unsigned int overlayIndex = luaL_checkint(L, 1); @@ -370,6 +404,13 @@ int LuaPathFinder::GetPathNodeCosts(lua_State* L) +/*** + * @function Spring.SetPathNodeCost + * @param overlayIndex integer + * @param costIndex integer 0-based index in the overlay + * @param cost number + * @return boolean success + */ int LuaPathFinder::SetPathNodeCost(lua_State* L) { const unsigned int overlayIndex = luaL_checkint(L, 1); @@ -400,6 +441,12 @@ int LuaPathFinder::SetPathNodeCost(lua_State* L) return 1; } +/*** + * @function Spring.GetPathNodeCost + * @param nodeX integer Heightmap node X coordinate + * @param nodeZ integer Heightmap node Z coordinate + * @return number cost + */ int LuaPathFinder::GetPathNodeCost(lua_State* L) { const unsigned int hmx = luaL_checkint(L, 1); diff --git a/rts/Lua/LuaRBOs.cpp b/rts/Lua/LuaRBOs.cpp index 520f53796d6..0fc8be6c039 100644 --- a/rts/Lua/LuaRBOs.cpp +++ b/rts/Lua/LuaRBOs.cpp @@ -41,9 +41,9 @@ bool LuaRBOs::PushEntries(lua_State* L) bool LuaRBOs::CreateMetatable(lua_State* L) { luaL_newmetatable(L, "RBO"); - HSTR_PUSH_CFUNC(L, "__gc", meta_gc); - HSTR_PUSH_CFUNC(L, "__index", meta_index); - HSTR_PUSH_CFUNC(L, "__newindex", meta_newindex); + LuaPushNamedCFunc(L, "__gc", meta_gc); + LuaPushNamedCFunc(L, "__index", meta_index); + LuaPushNamedCFunc(L, "__newindex", meta_newindex); lua_pop(L, 1); return true; } @@ -152,7 +152,7 @@ int LuaRBOs::meta_newindex(lua_State* L) * @x_helper * @field target GL * @field format GL - * @field samples number? any number here will result in creation of multisampled RBO + * @field samples integer? any number here will result in creation of multisampled RBO */ /*** diff --git a/rts/Lua/LuaRules.cpp b/rts/Lua/LuaRules.cpp index 19b5ac69707..4bf4590aa9f 100644 --- a/rts/Lua/LuaRules.cpp +++ b/rts/Lua/LuaRules.cpp @@ -70,6 +70,7 @@ int CLuaRules::GetInitSelectTeam() const } + /****************************************************************************** * Lua Rules * @@ -91,11 +92,13 @@ bool CLuaRules::AddUnsyncedCode(lua_State* L) { lua_getglobal(L, "Spring"); + /*** @field Spring.UnitRendering ObjectRenderingTable */ lua_pushliteral(L, "UnitRendering"); lua_createtable(L, 0, 17); LuaObjectRendering::PushEntries(L); lua_rawset(L, -3); + /*** @field Spring.FeatureRendering ObjectRenderingTable */ lua_pushliteral(L, "FeatureRendering"); lua_createtable(L, 0, 17); LuaObjectRendering::PushEntries(L); @@ -114,6 +117,11 @@ bool CLuaRules::AddUnsyncedCode(lua_State* L) // LuaRules Call-Outs // +/*** + * + * @function Script.PermitHelperAIs + * @param permit boolean + */ int CLuaRules::PermitHelperAIs(lua_State* L) { if (!lua_isboolean(L, 1)) { diff --git a/rts/Lua/LuaScream.cpp b/rts/Lua/LuaScream.cpp index 98065386188..d733242b020 100644 --- a/rts/Lua/LuaScream.cpp +++ b/rts/Lua/LuaScream.cpp @@ -22,9 +22,9 @@ bool LuaScream::PushEntries(lua_State* L) bool LuaScream::CreateMetatable(lua_State* L) { luaL_newmetatable(L, "Scream"); - HSTR_PUSH_CFUNC(L, "__gc", meta_gc); - HSTR_PUSH_CFUNC(L, "__index", meta_index); - HSTR_PUSH_CFUNC(L, "__newindex", meta_newindex); + LuaPushNamedCFunc(L, "__gc", meta_gc); + LuaPushNamedCFunc(L, "__index", meta_index); + LuaPushNamedCFunc(L, "__newindex", meta_newindex); lua_pop(L, 1); return true; } diff --git a/rts/Lua/LuaShaders.cpp b/rts/Lua/LuaShaders.cpp index 5109e5d64c3..acaeda87712 100644 --- a/rts/Lua/LuaShaders.cpp +++ b/rts/Lua/LuaShaders.cpp @@ -793,6 +793,7 @@ int LuaShaders::CreateShader(lua_State* L) * * @function gl.DeleteShader * @param shaderID integer + * @return boolean? deleted `nil` if `shaderID` is `nil`. */ int LuaShaders::DeleteShader(lua_State* L) { @@ -953,11 +954,11 @@ int LuaShaders::GetActiveUniforms(lua_State* L) GLint i = 0; for (const auto& [name, au] : prog->activeUniforms) { lua_createtable(L, 0, 5); { - HSTR_PUSH_STRING(L, "name" , name); - HSTR_PUSH_STRING(L, "type" , UniformTypeString(au.type)); - HSTR_PUSH_NUMBER(L, "length" , name.size()); - HSTR_PUSH_NUMBER(L, "size" , au.size); - HSTR_PUSH_NUMBER(L, "location", prog->activeUniformLocations.at(name).location); + LuaPushNamedString(L, "name" , name); + LuaPushNamedString(L, "type" , UniformTypeString(au.type)); + LuaPushNamedNumber(L, "length" , name.size()); + LuaPushNamedNumber(L, "size" , au.size); + LuaPushNamedNumber(L, "location", prog->activeUniformLocations.at(name).location); } lua_rawseti(L, -2, i + 1); ++i; @@ -990,6 +991,17 @@ int LuaShaders::GetUniformLocation(lua_State* L) return 1; } +/*** + * Returns the subroutine index for a shader program. + * + * @function gl.GetSubroutineIndex + * @param shaderID integer + * @param shaderType integer + * @param name string + * @return integer? index + * + * Returns no value when shader support is unavailable or `shaderID` is invalid. + */ int LuaShaders::GetSubroutineIndex(lua_State* L) { if (!IS_GL_FUNCTION_AVAILABLE(glGetSubroutineIndex)) @@ -1038,7 +1050,25 @@ namespace { } } +/*** + * Writes user-defined model uniforms for a unit. + * + * @function gl.SetUnitBufferUniforms + * @param unitID UnitID + * @param values number[] + * @param offset integer? + * @return integer count + */ int LuaShaders::SetUnitBufferUniforms(lua_State* L) { return SetObjectBufferUniforms(L, __func__); } +/*** + * Writes user-defined model uniforms for a feature. + * + * @function gl.SetFeatureBufferUniforms + * @param featureID FeatureID + * @param values number[] + * @param offset integer? + * @return integer count + */ int LuaShaders::SetFeatureBufferUniforms(lua_State* L) { return SetObjectBufferUniforms(L, __func__); } @@ -1051,6 +1081,7 @@ int LuaShaders::SetFeatureBufferUniforms(lua_State* L) { return SetObjectBufferU * shader. Shader must be activated before setting uniforms. * * @function gl.Uniform + * @function gl.UniformFloat Alias of Uniform * @param locationID GL|string uniformName * @param f1 number * @param f2 number? @@ -1292,6 +1323,13 @@ int LuaShaders::UniformMatrix(lua_State* L) return 0; } +/*** + * Selects a subroutine for the active shader program. + * + * @function gl.UniformSubroutine + * @param shaderType integer + * @param index integer + */ int LuaShaders::UniformSubroutine(lua_State* L) { if (!IS_GL_FUNCTION_AVAILABLE(glUniformSubroutinesuiv)) @@ -1313,7 +1351,7 @@ int LuaShaders::UniformSubroutine(lua_State* L) * * Return the GLSL compliant definition of UniformMatricesBuffer(idx=0) or UniformParamsBuffer(idx=1) structure. * - * @param index number + * @param index integer * @return string glslDefinition */ int LuaShaders::GetEngineUniformBufferDef(lua_State* L) @@ -1335,7 +1373,7 @@ int LuaShaders::GetEngineUniformBufferDef(lua_State* L) * * Return the GLSL compliant definition of ModelUniformData structure (per Unit/Feature buffer available on GPU) * - * @param index number + * @param index integer * @return string glslDefinition */ int LuaShaders::GetEngineModelUniformDataDef(lua_State* L) @@ -1353,9 +1391,9 @@ int LuaShaders::GetEngineModelUniformDataDef(lua_State* L) * * Return the current size values of ModelUniformData structure (per Unit/Feature buffer available on GPU) * - * @param index number - * @return number sizeInElements - * @return number sizeInBytesOnCPU + * @param index integer + * @return integer sizeInElements + * @return integer sizeInBytesOnCPU */ int LuaShaders::GetEngineModelUniformDataSize(lua_State* L) @@ -1374,8 +1412,8 @@ int LuaShaders::GetEngineModelUniformDataSize(lua_State* L) * * @function gl.SetGeometryShaderParameter * @param shaderID integer - * @param key number - * @param value number + * @param key GL + * @param value integer * @return nil */ int LuaShaders::SetGeometryShaderParameter(lua_State* L) diff --git a/rts/Lua/LuaSyncedCtrl.cpp b/rts/Lua/LuaSyncedCtrl.cpp index d2fc09845cf..bcb1ba1360e 100644 --- a/rts/Lua/LuaSyncedCtrl.cpp +++ b/rts/Lua/LuaSyncedCtrl.cpp @@ -164,6 +164,7 @@ bool LuaSyncedCtrl::PushEntries(lua_State* L) REGISTER_LUA_CFUNC(AddTeamResource); REGISTER_LUA_CFUNC(UseTeamResource); REGISTER_LUA_CFUNC(SetTeamResource); + REGISTER_LUA_CFUNC(AddTeamResourceExcessStats); REGISTER_LUA_CFUNC(SetTeamShareLevel); REGISTER_LUA_CFUNC(ShareTeamResource); @@ -390,6 +391,7 @@ bool LuaSyncedCtrl::PushEntries(lua_State* L) if (!LuaSyncedMoveCtrl::PushMoveCtrl(L)) return false; + /*** @field Spring.UnitScript UnitScriptTable */ if (!CLuaUnitScript::PushEntries(L)) return false; @@ -885,8 +887,8 @@ static inline bool IsPlayerSynced(const CPlayer* player) /*** Changes the value of the (one-sided) alliance between: firstAllyTeamID -> secondAllyTeamID. * * @function Spring.SetAlly - * @param firstAllyTeamID integer - * @param secondAllyTeamID integer + * @param firstAllyTeamID AllyTeamID + * @param secondAllyTeamID AllyTeamID * @param ally boolean * @return nil */ @@ -908,7 +910,7 @@ int LuaSyncedCtrl::SetAlly(lua_State* L) /*** Changes the start box position of an allyTeam. * * @function Spring.SetAllyTeamStartBox - * @param allyTeamID integer + * @param allyTeamID AllyTeamID * @param xMin number left start box boundary (elmos) * @param zMin number top start box boundary (elmos) * @param xMax number right start box boundary (elmos) @@ -940,8 +942,8 @@ int LuaSyncedCtrl::SetAllyTeamStartBox(lua_State* L) /*** Assigns a player to a team. * * @function Spring.AssignPlayerToTeam - * @param playerID integer - * @param teamID integer + * @param playerID PlayerID + * @param teamID TeamID * @return nil */ int LuaSyncedCtrl::AssignPlayerToTeam(lua_State* L) @@ -969,11 +971,11 @@ int LuaSyncedCtrl::AssignPlayerToTeam(lua_State* L) * If the position argument is outside the team's startbox, the position is clamped. * * @function Spring.SetTeamStartPosition - * @param teamID integer + * @param teamID TeamID * @param x number left position (elmos) * @param y number vertical position (elmos) * @param z number top position (elmos) - * @return boolean true if the position was set, false if the teamID is invalid + * @return boolean success true if the position was set, false if the teamID is invalid */ int LuaSyncedCtrl::SetTeamStartPosition(lua_State* L) { @@ -1002,9 +1004,9 @@ int LuaSyncedCtrl::SetTeamStartPosition(lua_State* L) * Use to mark a player (un)ready in the pregame phase. * * @function Spring.SetPlayerReadyState - * @param playerID integer + * @param playerID PlayerID * @param ready boolean - * @return boolean true if the state was set, false if the playerID was invalid + * @return boolean success true if the state was set, false if the playerID was invalid */ int LuaSyncedCtrl::SetPlayerReadyState(lua_State* L) { @@ -1025,7 +1027,7 @@ int LuaSyncedCtrl::SetPlayerReadyState(lua_State* L) /*** Changes access to global line of sight for a team and its allies. * * @function Spring.SetGlobalLos - * @param allyTeamID integer + * @param allyTeamID AllyTeamID * @param globallos boolean * @return nil */ @@ -1086,7 +1088,7 @@ int LuaSyncedCtrl::SetGodMode(lua_State* L) * * Gaia team cannot be killed. * - * @param teamID integer + * @param teamID TeamID * @return nil */ int LuaSyncedCtrl::KillTeam(lua_State* L) @@ -1114,12 +1116,12 @@ int LuaSyncedCtrl::KillTeam(lua_State* L) /*** Declare game over. * * @function Spring.GameOver - * @param winningAllyTeamIDs integer[] A list of winning ally team IDs. + * @param winningAllyTeamIDs AllyTeamID[] A list of winning ally team IDs. * * Pass multiple winners to declare a draw. * Pass no arguments if undecided (e.g. when dropped from the host). * - * @return integer Number of accepted (valid) ally teams. + * @return integer teams Number of accepted (valid) ally teams. */ int LuaSyncedCtrl::GameOver(lua_State* L) { @@ -1160,7 +1162,7 @@ int LuaSyncedCtrl::GameOver(lua_State* L) /*** Set tidal strength * * @function Spring.SetTidal - * @param strength number + * @param strength number? * @return nil */ int LuaSyncedCtrl::SetTidal(lua_State* L) @@ -1173,8 +1175,8 @@ int LuaSyncedCtrl::SetTidal(lua_State* L) /*** Set wind strength * * @function Spring.SetWind - * @param minStrength number - * @param maxStrength number + * @param minStrength number? + * @param maxStrength number? * @return nil */ int LuaSyncedCtrl::SetWind(lua_State* L) @@ -1187,7 +1189,7 @@ int LuaSyncedCtrl::SetWind(lua_State* L) * Counts as production in post-game graph statistics. * * @function Spring.AddTeamResource - * @param teamID integer + * @param teamID TeamID * @param type ResourceName * @param amount number * @return nil @@ -1225,7 +1227,7 @@ int LuaSyncedCtrl::AddTeamResource(lua_State* L) * Counts as usage in post-game graph statistics. * * @function Spring.UseTeamResource - * @param teamID integer + * @param teamID TeamID * @param type ResourceName Resource type. * @param amount number Amount of resource to use. * @return boolean hadEnough @@ -1236,7 +1238,7 @@ int LuaSyncedCtrl::AddTeamResource(lua_State* L) * Counts as usage in post-game graph statistics. * * @function Spring.UseTeamResource - * @param teamID integer + * @param teamID TeamID * @param amount ResourceUsage * @return boolean hadEnough * True if enough of the resource type(s) were available and was consumed, otherwise false. @@ -1319,7 +1321,7 @@ int LuaSyncedCtrl::UseTeamResource(lua_State* L) /*** * @function Spring.SetTeamResource - * @param teamID integer + * @param teamID TeamID * @param resource ResourceName|StorageName * @param amount number * @return nil @@ -1370,7 +1372,7 @@ int LuaSyncedCtrl::SetTeamResource(lua_State* L) /*** Changes the resource amount for a team beyond which resources aren't stored but transferred to other allied teams if possible. * * @function Spring.SetTeamShareLevel - * @param teamID integer + * @param teamID TeamID * @param type ResourceName * @param amount number * @return nil @@ -1404,6 +1406,52 @@ int LuaSyncedCtrl::SetTeamShareLevel(lua_State* L) } +/*** + * Records resource excess for a team without moving resources. + * + * The engine normally tracks excess, but if you use `gadget:ResourceExcess` + * to handle it manually it's now also up to you to track stats. + * + * @function Spring.AddTeamResourceExcessStats + * @param teamID TeamID + * @param type ResourceName + * @param excess number Amount wasted this tick. + * @return nil + */ +int LuaSyncedCtrl::AddTeamResourceExcessStats(lua_State* L) +{ + const int teamID = luaL_checkint(L, 1); + + if (!teamHandler.IsValidTeam(teamID)) + return 0; + + if (!CanControlTeam(L, teamID)) + return 0; + + CTeam* team = teamHandler.Team(teamID); + + if (team == nullptr) + return 0; + + const char rtype = luaL_checkstring(L, 2)[0]; + if (rtype != 'm' && rtype != 'e') + return 0; + + const bool isMetal = (rtype == 'm'); + const float val = std::max(0.0f, luaL_checkfloat(L, 3)); + + float& resExcess = isMetal ? team->resPrevExcess.metal : team->resPrevExcess.energy; + + TeamStatistics& stats = team->GetCurrentStats(); + float& statExcess = isMetal ? stats.metalExcess : stats.energyExcess; + + resExcess += val; + statExcess += val; + + return 0; +} + + /*** Transfers resources between two teams. * Transfers directly, without involving AllowResourceTransfer callin. * Approximately equivalent to doing Use and Add for the sender and receiver, @@ -1411,8 +1459,8 @@ int LuaSyncedCtrl::SetTeamShareLevel(lua_State* L) * used/produced in end-game statistics graphs. * * @function Spring.ShareTeamResource - * @param teamID_src integer - * @param teamID_recv integer + * @param teamID_src TeamID + * @param teamID_recv TeamID * @param type ResourceName * @param amount number * @return nil @@ -1566,7 +1614,7 @@ void SetRulesParam(lua_State* L, const char* caller, int offset, /*** * @function Spring.SetGameRulesParam * @param paramName string - * @param paramValue ?number|string numeric paramValues in quotes will be converted to number. + * @param paramValue (number|string|boolean)? numeric paramValues in quotes will be converted to number. * @param losAccess losAccess? * @return nil */ @@ -1579,9 +1627,9 @@ int LuaSyncedCtrl::SetGameRulesParam(lua_State* L) /*** * @function Spring.SetTeamRulesParam - * @param teamID integer + * @param teamID TeamID * @param paramName string - * @param paramValue ?number|string numeric paramValues in quotes will be converted to number. + * @param paramValue (number|string|boolean)? numeric paramValues in quotes will be converted to number. * @param losAccess losAccess? * @return nil */ @@ -1597,9 +1645,9 @@ int LuaSyncedCtrl::SetTeamRulesParam(lua_State* L) /*** * @function Spring.SetPlayerRulesParam - * @param playerID integer + * @param playerID PlayerID * @param paramName string - * @param paramValue ?number|string numeric paramValues in quotes will be converted to number. + * @param paramValue (number|string|boolean)? numeric paramValues in quotes will be converted to number. * @param losAccess losAccess? * @return nil */ @@ -1621,9 +1669,9 @@ int LuaSyncedCtrl::SetPlayerRulesParam(lua_State* L) /*** * * @function Spring.SetUnitRulesParam - * @param unitID integer + * @param unitID UnitID * @param paramName string - * @param paramValue ?number|string numeric paramValues in quotes will be converted to number. + * @param paramValue (number|string|boolean)? numeric paramValues in quotes will be converted to number. * @param losAccess losAccess? * @return nil */ @@ -1641,9 +1689,9 @@ int LuaSyncedCtrl::SetUnitRulesParam(lua_State* L) /*** * @function Spring.SetFeatureRulesParam - * @param featureID integer + * @param featureID FeatureID * @param paramName string - * @param paramValue ?number|string numeric paramValues in quotes will be converted to number. + * @param paramValue (number|string|boolean)? numeric paramValues in quotes will be converted to number. * @param losAccess losAccess? * @return nil */ @@ -1705,11 +1753,11 @@ static inline void ParseCobArgs( /*** * @function Spring.CallCOBScript - * @param unitID integer + * @param unitID UnitID * @param funcName integer|string? Function ID or name. * @param retArgs integer Number of values to return. * @param ... any Arguments - * @return number ... + * @return integer ... */ int LuaSyncedCtrl::CallCOBScript(lua_State* L) { @@ -1765,7 +1813,7 @@ int LuaSyncedCtrl::CallCOBScript(lua_State* L) /*** * @function Spring.GetCOBScriptID - * @param unitID integer + * @param unitID UnitID * @param funcName string * @return integer? funcID */ @@ -1813,12 +1861,12 @@ int LuaSyncedCtrl::GetCOBScriptID(lua_State* L) * @param posY number * @param posZ number * @param facing Facing - * @param teamID integer + * @param teamID TeamID? * @param build boolean? (Default: `false`) The unit is created in "being built" state with zero `buildProgress`. * @param flattenGround boolean? (Default: `true`) The unit flattens ground, if it normally does so. - * @param unitID integer? Request a specific unitID. - * @param builderID integer? - * @return integer? unitID The ID of the created unit, or `nil` if the unit could not be created. + * @param unitID UnitID? Request a specific unitID. + * @param builderID UnitID? + * @return UnitID? unitID The ID of the created unit, or `nil` if the unit could not be created. */ int LuaSyncedCtrl::CreateUnit(lua_State* L) { @@ -1905,10 +1953,10 @@ int LuaSyncedCtrl::CreateUnit(lua_State* L) /*** * @function Spring.DestroyUnit * @see Spring.CreateUnit - * @param unitID integer + * @param unitID UnitID * @param selfd boolean? (Default: `false`) makes the unit act like it self-destructed. * @param reclaimed boolean? (Default: `false`) don't show any DeathSequences, don't leave a wreckage. This does not give back the resources to the team! - * @param attackerID integer? + * @param attackerID UnitID? * @param cleanupImmediately boolean? (Default: `false`) stronger version of reclaimed, removes the unit unconditionally and makes its ID available for immediate reuse (otherwise it takes a few frames) * @return nil */ @@ -1948,8 +1996,8 @@ int LuaSyncedCtrl::DestroyUnit(lua_State* L) /*** * @function Spring.TransferUnit - * @param unitID integer - * @param newTeamID integer + * @param unitID UnitID + * @param newTeamID TeamID * @param given boolean? (Default: `true`) if false, the unit is captured. * @param adjustUnitLimit boolean? (Default: `false`) if true, also transfer the limit slot * @return boolean successfulTransfer @@ -2013,9 +2061,9 @@ int LuaSyncedCtrl::TransferUnit(lua_State* L) * - `transferAmnt` must be lower or equal than the origin team current maxunits (can't transfer limit team does not have available) * - `transferAmnt` must be lower than origin team maxunits - currentunitscount (can't transfer limit if origin team would be already over the limit after transfer) * - * @param fromTeamID number - * @param newTeamID number - * @param transferAmnt number + * @param fromTeamID TeamID + * @param newTeamID TeamID + * @param transferAmnt integer * @return boolean successfulTransfer Whether the max unit limit was successfully transferred. */ int LuaSyncedCtrl::TransferTeamMaxUnits(lua_State* L) @@ -2051,7 +2099,7 @@ int LuaSyncedCtrl::TransferTeamMaxUnits(lua_State* L) /*** * @function Spring.SetUnitCosts - * @param unitID integer + * @param unitID UnitID * @param where table keys and values are, respectively and in this order: buildTime=amount, metalCost=amount, energyCost=amount * @return nil */ @@ -2180,7 +2228,7 @@ static bool SetUnitStorageParam(CUnit* unit, const char* name, float value) /*** * @function Spring.SetUnitResourcing - * @param unitID integer + * @param unitID UnitID * @param res string * @param amount number * @return nil @@ -2188,7 +2236,7 @@ static bool SetUnitStorageParam(CUnit* unit, const char* name, float value) /*** * @function Spring.SetUnitResourcing - * @param unitID integer + * @param unitID UnitID * @param res table keys are: "[u|c][u|m][m|e]" unconditional | conditional, use | make, metal | energy. Values are amounts * @return nil */ @@ -2226,14 +2274,14 @@ int LuaSyncedCtrl::SetUnitResourcing(lua_State* L) /*** * @function Spring.SetUnitStorage - * @param unitID integer + * @param unitID UnitID * @param res string * @param amount number */ /*** * @function Spring.SetUnitStorage - * @param unitID integer + * @param unitID UnitID * @param res ResourceUsage keys are: "[m|e]" metal | energy. Values are amounts */ int LuaSyncedCtrl::SetUnitStorage(lua_State* L) @@ -2265,7 +2313,7 @@ int LuaSyncedCtrl::SetUnitStorage(lua_State* L) /*** * @function Spring.SetUnitTooltip - * @param unitID integer + * @param unitID UnitID * @param tooltip string * @return nil */ @@ -2301,7 +2349,7 @@ int LuaSyncedCtrl::SetUnitTooltip(lua_State* L) * Note, if your game's custom shading framework doesn't support reverting into nanoframes * then reverting into nanoframes via the "build" tag will fail to render properly. * - * @param unitID integer + * @param unitID UnitID * @param health number|SetUnitHealthAmounts If a number, sets the units health * to that value. Pass a table to update health, capture progress, paralyze * damage, and build progress. @@ -2362,7 +2410,7 @@ int LuaSyncedCtrl::SetUnitHealth(lua_State* L) /*** * @function Spring.SetUnitMaxHealth - * @param unitID integer + * @param unitID UnitID * @param maxHealth number * @return nil */ @@ -2381,8 +2429,8 @@ int LuaSyncedCtrl::SetUnitMaxHealth(lua_State* L) /*** * @function Spring.SetUnitStockpile - * @param unitID integer - * @param stockpile number? + * @param unitID UnitID + * @param stockpile integer? * @param buildPercent number? * @return nil */ @@ -2523,7 +2571,7 @@ static bool SetSingleUnitWeaponState(lua_State* L, CWeapon* weapon, int index) /*** * * @function Spring.SetUnitUseWeapons - * @param unitID integer + * @param unitID UnitID * @param forceUseWeapons number? * @param allowUseWeapons number? * @return nil @@ -2542,16 +2590,16 @@ int LuaSyncedCtrl::SetUnitUseWeapons(lua_State* L) /*** * @function Spring.SetUnitWeaponState - * @param unitID integer - * @param weaponNum number + * @param unitID UnitID + * @param weaponNum integer * @param states WeaponState * @return nil */ /*** * @function Spring.SetUnitWeaponState - * @param unitID integer - * @param weaponNum number + * @param unitID UnitID + * @param weaponNum integer * @param key string * @param value number * @return nil @@ -2678,15 +2726,15 @@ static int SetSingleDynDamagesKey(lua_State* L, DynDamageArray* damages, int ind /*** * @function Spring.SetUnitWeaponDamages - * @param unitID integer - * @param weaponNum number|"selfDestruct"|"explode" + * @param unitID UnitID + * @param weaponNum integer|"selfDestruct"|"explode" * @param damages WeaponDamages * @return nil */ /*** * @function Spring.SetUnitWeaponDamages - * @param unitID integer - * @param weaponNum number|"selfDestruct"|"explode" + * @param unitID UnitID + * @param weaponNum integer|"selfDestruct"|"explode" * @param key string * @param value number * @return nil @@ -2738,7 +2786,7 @@ int LuaSyncedCtrl::SetUnitWeaponDamages(lua_State* L) /*** @function Spring.SetUnitMaxRange * - * @param unitID integer + * @param unitID UnitID * @param maxRange number * @return nil */ @@ -2758,7 +2806,7 @@ int LuaSyncedCtrl::SetUnitMaxRange(lua_State* L) * @function Spring.SetUnitExperience * @see Spring.AddUnitExperience * @see Spring.GetUnitExperience - * @param unitID integer + * @param unitID UnitID * @param experience number * @return nil */ @@ -2777,7 +2825,7 @@ int LuaSyncedCtrl::SetUnitExperience(lua_State* L) * @function Spring.AddUnitExperience * @see Spring.SetUnitExperience * @see Spring.GetUnitExperience - * @param unitID integer + * @param unitID UnitID * @param deltaExperience number Can be negative to subtract, but the unit will never have negative total afterwards * @return nil */ @@ -2796,7 +2844,7 @@ int LuaSyncedCtrl::AddUnitExperience(lua_State* L) /*** * @function Spring.SetUnitArmored - * @param unitID integer + * @param unitID UnitID * @param armored boolean? * @param armorMultiple number? * @return nil @@ -2888,8 +2936,8 @@ static unsigned char ParseLosBits(lua_State* L, int index, unsigned char bits) * @see Spring.SetUnitLosState * @function Spring.SetUnitLosMask * - * @param unitID integer - * @param allyTeam number + * @param unitID UnitID + * @param allyTeam AllyTeamID * @param losTypes LosTable|LosMask|integer A bitmask of `LosMask` bits or a * table. True bits disable engine updates to visibility. */ @@ -2935,8 +2983,8 @@ int LuaSyncedCtrl::SetUnitLosMask(lua_State* L) * * @see Spring.SetUnitLosMask * @function Spring.SetUnitLosState - * @param unitID integer - * @param allyTeam number + * @param unitID UnitID + * @param allyTeam AllyTeamID * @param losTypes LosTable|LosMask|integer A bitmask of `LosMask` bits or a * table */ @@ -2975,9 +3023,9 @@ int LuaSyncedCtrl::SetUnitLosState(lua_State* L) * - if the boolean is false it takes the default decloak distance for that unitdef, * - if the boolean is true it takes the absolute value of it. * - * @param unitID integer - * @param cloak boolean|number - * @param cloakArg boolean|number + * @param unitID UnitID + * @param cloak (boolean|number)? + * @param cloakArg (boolean|number)? * @return nil */ int LuaSyncedCtrl::SetUnitCloak(lua_State* L) @@ -3012,7 +3060,7 @@ int LuaSyncedCtrl::SetUnitCloak(lua_State* L) /*** * @function Spring.SetUnitStealth - * @param unitID integer + * @param unitID UnitID * @param stealth boolean * @return nil */ @@ -3030,7 +3078,7 @@ int LuaSyncedCtrl::SetUnitStealth(lua_State* L) /*** * @function Spring.SetUnitSonarStealth - * @param unitID integer + * @param unitID UnitID * @param sonarStealth boolean * @return nil */ @@ -3047,7 +3095,7 @@ int LuaSyncedCtrl::SetUnitSonarStealth(lua_State* L) /*** * @function Spring.SetUnitSeismicSignature - * @param unitID integer + * @param unitID UnitID * @param seismicSignature number * @return nil */ @@ -3085,7 +3133,7 @@ int LuaSyncedCtrl::SetUnitLeavesGhost(lua_State* L) /*** * @function Spring.SetUnitAlwaysVisible - * @param unitID integer + * @param unitID UnitID * @param alwaysVisible boolean * @return nil */ @@ -3098,7 +3146,7 @@ int LuaSyncedCtrl::SetUnitAlwaysVisible(lua_State* L) /*** * * @function Spring.SetUnitUseAirLos - * @param unitID integer + * @param unitID UnitID * @param useAirLos boolean * @return nil */ @@ -3110,7 +3158,7 @@ int LuaSyncedCtrl::SetUnitUseAirLos(lua_State* L) /*** * @function Spring.SetUnitMetalExtraction - * @param unitID integer + * @param unitID UnitID * @param depth number corresponds to metal extraction rate * @param range number? similar to "extractsMetal" in unitDefs. * @return nil @@ -3138,8 +3186,8 @@ int LuaSyncedCtrl::SetUnitMetalExtraction(lua_State* L) /*** See also harvestStorage UnitDef tag. * * @function Spring.SetUnitHarvestStorage - * @param unitID integer - * @param metal number + * @param unitID UnitID + * @param metal number? * @return nil */ int LuaSyncedCtrl::SetUnitHarvestStorage(lua_State* L) @@ -3159,9 +3207,9 @@ int LuaSyncedCtrl::SetUnitHarvestStorage(lua_State* L) /*** * * @function Spring.SetUnitBuildParams - * @param unitID integer + * @param unitID UnitID * @param paramName string one of `buildRange`|`buildDistance`|`buildRange3D` - * @param value number|boolean boolean when `paramName` is `buildRange3D`, otherwise number. + * @param value (number|boolean)? boolean when `paramName` is `buildRange3D`, otherwise number. * @return nil */ int LuaSyncedCtrl::SetUnitBuildParams(lua_State* L) @@ -3192,7 +3240,7 @@ int LuaSyncedCtrl::SetUnitBuildParams(lua_State* L) /*** * @function Spring.SetUnitBuildSpeed - * @param builderID integer + * @param builderID UnitID * @param buildSpeed number * @param repairSpeed number? * @param reclaimSpeed number? @@ -3247,7 +3295,7 @@ int LuaSyncedCtrl::SetUnitBuildSpeed(lua_State* L) * This saves a lot of engine calls, by replacing: function script.QueryNanoPiece() return currentpiece end * Use it! * - * @param builderID integer + * @param builderID UnitID * @param pieces table * @return nil * @@ -3303,7 +3351,7 @@ int LuaSyncedCtrl::SetUnitNanoPieces(lua_State* L) /*** * @function Spring.SetUnitBlocking - * @param unitID integer + * @param unitID UnitID * @param isBlocking boolean? If `true` add this unit to the `GroundBlockingMap`, but only if it collides with solid objects (or is being set to collide with the `isSolidObjectCollidable` argument). If `false`, remove this unit from the `GroundBlockingMap`. No change if `nil`. * @param isSolidObjectCollidable boolean? Enable or disable collision with solid objects, or no change if `nil`. * @param isProjectileCollidable boolean? Enable or disable collision with projectiles, or no change if `nil`. @@ -3321,8 +3369,8 @@ int LuaSyncedCtrl::SetUnitBlocking(lua_State* L) /*** * @function Spring.SetUnitCrashing - * @param unitID integer - * @param crashing boolean + * @param unitID UnitID + * @param crashing boolean? * @return boolean success */ int LuaSyncedCtrl::SetUnitCrashing(lua_State* L) { @@ -3356,7 +3404,7 @@ int LuaSyncedCtrl::SetUnitCrashing(lua_State* L) { /*** * @function Spring.SetUnitShieldState - * @param unitID integer + * @param unitID UnitID * @param weaponID integer? (Default: `-1`) * @param enabled boolean? * @param power number? @@ -3394,7 +3442,7 @@ int LuaSyncedCtrl::SetUnitShieldState(lua_State* L) /*** * @function Spring.SetUnitShieldRechargeDelay - * @param unitID integer + * @param unitID UnitID * @param weaponID integer? (optional if the unit only has one shield) * @param rechargeTime number? (in seconds; emulates a regular hit if nil) * @return nil @@ -3431,7 +3479,7 @@ int LuaSyncedCtrl::SetUnitShieldRechargeDelay(lua_State* L) /*** * @function Spring.SetUnitFlanking - * @param unitID integer + * @param unitID UnitID * @param type string "dir"|"minDamage"|"maxDamage"|"moveFactor"|"mode" * @param arg1 number x|minDamage|maxDamage|moveFactor|mode * @param y number? only when type is "dir" @@ -3478,7 +3526,7 @@ int LuaSyncedCtrl::SetUnitFlanking(lua_State* L) /*** * @function Spring.SetUnitPhysicalStateBit - * @param unitID integer + * @param unitID UnitID * @param Physical number[bit] state bit * @return nil */ @@ -3497,8 +3545,8 @@ int LuaSyncedCtrl::SetUnitPhysicalStateBit(lua_State* L) /*** * @function Spring.GetUnitPhysicalState - * @param unitID integer - * @return number Unit's PhysicalState bitmask + * @param unitID UnitID + * @return integer physicalState Unit's PhysicalState bitmask */ int LuaSyncedCtrl::GetUnitPhysicalState(lua_State* L) { @@ -3522,7 +3570,7 @@ int LuaSyncedCtrl::SetUnitFuel(lua_State* L) { return 0; } // FIXME: DELETE ME * * @function Spring.SetUnitNeutral * - * @param unitID integer + * @param unitID UnitID * @param neutral boolean * @return nil|boolean setNeutral */ @@ -3541,23 +3589,25 @@ int LuaSyncedCtrl::SetUnitNeutral(lua_State* L) /*** Defines a unit's target. * * @function Spring.SetUnitTarget - * @param unitID integer - * @param enemyUnitID integer? when nil drops the units current target. + * @param unitID UnitID + * @param enemyUnitID UnitID? when nil drops the units current target. * @param dgun boolean? (Default: `false`) * @param userTarget boolean? (Default: `false`) - * @param weaponNum number? (Default: `-1`) + * @param dontForceTarget boolean? + * @param weaponNum integer? (Default: `-1`) * @return boolean success */ /*** * @function Spring.SetUnitTarget - * @param unitID integer + * @param unitID UnitID * @param x number? when nil or not passed it will drop target and ignore other parameters * @param y number? * @param z number? * @param dgun boolean? (Default: `false`) * @param userTarget boolean? (Default: `false`) - * @param weaponNum number? (Default: `-1`) + * @param dontForceTarget boolean? + * @param weaponNum integer? (Default: `-1`) * @return boolean success */ int LuaSyncedCtrl::SetUnitTarget(lua_State* L) @@ -3620,7 +3670,7 @@ int LuaSyncedCtrl::SetUnitTarget(lua_State* L) /*** * @function Spring.SetUnitMidAndAimPos - * @param unitID integer + * @param unitID UnitID * @param mpX number new middle positionX of unit * @param mpY number new middle positionY of unit * @param mpZ number new middle positionZ of unit @@ -3668,9 +3718,9 @@ int LuaSyncedCtrl::SetUnitMidAndAimPos(lua_State* L) /*** * @function Spring.SetUnitRadiusAndHeight - * @param unitID integer - * @param radius number - * @param height number + * @param unitID UnitID + * @param radius number? + * @param height number? * @return boolean success */ int LuaSyncedCtrl::SetUnitRadiusAndHeight(lua_State* L) @@ -3705,7 +3755,7 @@ int LuaSyncedCtrl::SetUnitRadiusAndHeight(lua_State* L) /*** * @function Spring.SetUnitBuildeeRadius * Sets the unit's radius for when targeted by build, repair, reclaim-type commands. - * @param unitID integer + * @param unitID UnitID * @param build number radius for when targeted by build, repair, reclaim-type commands. * @return nil */ @@ -3725,9 +3775,9 @@ int LuaSyncedCtrl::SetUnitBuildeeRadius(lua_State* L) /*** Changes the pieces hierarchy of a unit by attaching a piece to a new parent. * * @function Spring.SetUnitPieceParent - * @param unitID integer - * @param AlteredPiece number - * @param ParentPiece number + * @param unitID UnitID + * @param AlteredPiece integer + * @param ParentPiece integer * @return nil */ int LuaSyncedCtrl::SetUnitPieceParent(lua_State* L) @@ -3769,10 +3819,10 @@ int LuaSyncedCtrl::SetUnitPieceParent(lua_State* L) * * If any of the first three elements are non-zero, and also blocks all script animations from modifying it until {0, 0, 0} is passed. * - * @param unitID integer - * @param pieceNum number + * @param unitID UnitID + * @param pieceNum integer * @param matrix number[] an array of 16 floats - * @return boolean? valid - if the matrix can be used for the purpose of defining the piece spatial transformation. Blocks the piece animation, if true. + * @return boolean? valid whether the matrix can be used for the purpose of defining the piece spatial transformation. Blocks the piece animation, if true. */ int LuaSyncedCtrl::SetUnitPieceMatrix(lua_State* L) { @@ -3783,16 +3833,16 @@ int LuaSyncedCtrl::SetUnitPieceMatrix(lua_State* L) /*** * @function Spring.SetUnitCollisionVolumeData - * @param unitID integer + * @param unitID UnitID * @param scaleX number * @param scaleY number * @param scaleZ number * @param offsetX number * @param offsetY number * @param offsetZ number - * @param vType number - * @param tType number - * @param Axis number + * @param vType integer + * @param tType integer + * @param Axis integer * @return nil * * enum COLVOL_TYPES { @@ -3823,8 +3873,8 @@ int LuaSyncedCtrl::SetUnitCollisionVolumeData(lua_State* L) /*** * @function Spring.SetUnitPieceCollisionVolumeData - * @param unitID integer - * @param pieceIndex number + * @param unitID UnitID + * @param pieceIndex integer * @param enable boolean * @param scaleX number * @param scaleY number @@ -3832,8 +3882,8 @@ int LuaSyncedCtrl::SetUnitCollisionVolumeData(lua_State* L) * @param offsetX number * @param offsetY number * @param offsetZ number - * @param volumeType number? - * @param primaryAxis number? + * @param volumeType integer? + * @param primaryAxis integer? * @return nil */ int LuaSyncedCtrl::SetUnitPieceCollisionVolumeData(lua_State* L) @@ -3845,8 +3895,8 @@ int LuaSyncedCtrl::SetUnitPieceCollisionVolumeData(lua_State* L) /*** * * @function Spring.SetUnitPieceVisible - * @param unitID integer - * @param pieceIndex number + * @param unitID UnitID + * @param pieceIndex integer * @param visible boolean * @return nil */ @@ -3858,10 +3908,10 @@ int LuaSyncedCtrl::SetUnitPieceVisible(lua_State* L) /*** * @function Spring.SetUnitSensorRadius - * @param unitID integer + * @param unitID UnitID * @param type "los"|"airLos"|"radar"|"sonar"|"seismic"|"radarJammer"|"sonarJammer" - * @param radius number - * @return number? New radius, or `nil` if unit is invalid. + * @param radius integer + * @return integer? newRadius New radius, or `nil` if unit is invalid. */ int LuaSyncedCtrl::SetUnitSensorRadius(lua_State* L) { @@ -3913,14 +3963,14 @@ int LuaSyncedCtrl::SetUnitSensorRadius(lua_State* L) * native "is under cursor" checks and some Lua interfaces. * * @function Spring.SetUnitPosErrorParams - * @param unitID integer - * @param posErrorVectorX number - * @param posErrorVectorY number - * @param posErrorVectorZ number - * @param posErrorDeltaX number - * @param posErrorDeltaY number - * @param posErrorDeltaZ number - * @param nextPosErrorUpdate number? + * @param unitID UnitID + * @param posErrorVectorX number? + * @param posErrorVectorY number? + * @param posErrorVectorZ number? + * @param posErrorDeltaX number? + * @param posErrorDeltaY number? + * @param posErrorDeltaZ number? + * @param nextPosErrorUpdate integer? * @return nil */ int LuaSyncedCtrl::SetUnitPosErrorParams(lua_State* L) @@ -3949,7 +3999,7 @@ int LuaSyncedCtrl::SetUnitPosErrorParams(lua_State* L) /*** Used by default commands to get in build-, attackrange etc. * * @function Spring.SetUnitMoveGoal - * @param unitID integer + * @param unitID UnitID * @param goalX number * @param goalY number * @param goalZ number @@ -3985,7 +4035,7 @@ int LuaSyncedCtrl::SetUnitMoveGoal(lua_State* L) /*** Used in conjunction with Spring.UnitAttach et al. to re-implement old airbase & fuel system in Lua. * * @function Spring.SetUnitLandGoal - * @param unitID integer + * @param unitID UnitID * @param goalX number * @param goalY number * @param goalZ number @@ -4014,7 +4064,8 @@ int LuaSyncedCtrl::SetUnitLandGoal(lua_State* L) /*** * @function Spring.ClearUnitGoal - * @param unitID integer + * @param unitID UnitID + * @param maneuver boolean? * @return nil */ int LuaSyncedCtrl::ClearUnitGoal(lua_State* L) @@ -4031,7 +4082,7 @@ int LuaSyncedCtrl::ClearUnitGoal(lua_State* L) /*** * @function Spring.SetUnitPhysics - * @param unitID integer + * @param unitID UnitID * @param posX number * @param posY number * @param posZ number @@ -4053,7 +4104,7 @@ int LuaSyncedCtrl::SetUnitPhysics(lua_State* L) /*** * @function Spring.SetUnitMass - * @param unitID integer + * @param unitID UnitID * @param mass number * @return nil */ @@ -4068,7 +4119,7 @@ int LuaSyncedCtrl::SetUnitMass(lua_State* L) * * Sets a unit's position in 2D, at terrain height. * - * @param unitID integer + * @param unitID UnitID * @param x number * @param z number * @param floating boolean? (Default: `false`) If true, over water the position is on surface. If false, on seafloor. @@ -4081,7 +4132,7 @@ int LuaSyncedCtrl::SetUnitMass(lua_State* L) * * Sets a unit's position in 3D, at an arbitrary height. * - * @param unitID integer + * @param unitID UnitID * @param x number * @param y number * @param z number @@ -4121,7 +4172,7 @@ int LuaSyncedCtrl::SetUnitPosition(lua_State* L) /*** * @function Spring.SetUnitRotation * Note: PYR order - * @param unitID integer + * @param unitID UnitID * @param pitch number Rotation in X axis * @param yaw number Rotation in Y axis * @param roll number Rotation in Z axis @@ -4141,7 +4192,7 @@ int LuaSyncedCtrl::SetUnitRotation(lua_State* L) * @deprecated It's strongly that you use the overload that accepts * a right direction as `frontDir` alone doesn't define object orientation. * - * @param unitID integer + * @param unitID UnitID * @param frontx number * @param fronty number * @param frontz number @@ -4154,7 +4205,7 @@ int LuaSyncedCtrl::SetUnitRotation(lua_State* L) * * Both vectors will be normalized in the engine. * - * @param unitID integer + * @param unitID UnitID * @param frontx number * @param fronty number * @param frontz number @@ -4194,7 +4245,7 @@ int LuaSyncedCtrl::SetUnitDirection(lua_State* L) * completely upright, new `{upx, upy, upz}` direction will be used as new "up" * vector, the rotation set by "heading" will remain preserved. * - * @param unitID integer + * @param unitID UnitID * @param heading Heading * @param upx number * @param upy number @@ -4210,7 +4261,7 @@ int LuaSyncedCtrl::SetUnitHeadingAndUpDir(lua_State* L) * * @see Spring.SetUnitMoveCtrl for disabling/enabling this control * @function Spring.SetUnitVelocity - * @param unitID integer + * @param unitID UnitID * @param velX number in elmos/frame * @param velY number in elmos/frame * @param velZ number in elmos/frame @@ -4224,7 +4275,7 @@ int LuaSyncedCtrl::SetUnitVelocity(lua_State* L) /*** * * @function Spring.SetFactoryBuggerOff - * @param unitID integer + * @param unitID UnitID * @param buggerOff boolean? * @param offset number? * @param radius number? @@ -4262,11 +4313,11 @@ int LuaSyncedCtrl::SetFactoryBuggerOff(lua_State* L) * @param y number * @param z number? uses ground height when unspecified * @param radius number - * @param teamID integer + * @param teamID TeamID * @param spherical boolean? (Default: `true`) * @param forced boolean? (Default: `true`) - * @param excludeUnitID integer? - * @param excludeUnitDefIDs number[]? + * @param excludeUnitID UnitID? + * @param excludeUnitDefIDs UnitDefID[]? * @return nil */ int LuaSyncedCtrl::BuggerOff(lua_State* L) @@ -4335,11 +4386,11 @@ static std::optional > ParseDamagePa * If health goes below 0 and featureDef is `destructable` the feature will be deleted and * a wreck created. * - * @param featureID integer + * @param featureID FeatureID * @param damage number - * @param paralyze number? (Default: `0`) equals to the paralyzetime in the WeaponDef. - * @param attackerID integer? (Default: `-1`) - * @param weaponID integer? (Default: `-1`) + * @param paralyze integer? (Default: `0`) equals to the paralyzetime in the WeaponDef. + * @param attackerID UnitID? (Default: `-1`) + * @param weaponID WeaponDefID? (Default: `-1`) * @param impulseX number? * @param impulseY number? * @param impulseZ number? @@ -4372,11 +4423,11 @@ int LuaSyncedCtrl::AddFeatureDamage(lua_State* L) /*** * @function Spring.AddUnitDamage * - * @param unitID integer + * @param unitID UnitID * @param damage number - * @param paralyze number? (Default: `0`) equals to the paralyzetime in the WeaponDef. - * @param attackerID integer? (Default: `-1`) - * @param weaponID integer? (Default: `-1`) + * @param paralyze integer? (Default: `0`) equals to the paralyzetime in the WeaponDef. + * @param attackerID UnitID? (Default: `-1`) + * @param weaponID WeaponDefID? (Default: `-1`) * @param impulseX number? * @param impulseY number? * @param impulseZ number? @@ -4408,7 +4459,7 @@ int LuaSyncedCtrl::AddUnitDamage(lua_State* L) /*** * @function Spring.AddUnitImpulse - * @param unitID integer + * @param unitID UnitID * @param x number * @param y number * @param z number @@ -4433,7 +4484,7 @@ int LuaSyncedCtrl::AddUnitImpulse(lua_State* L) /*** * @function Spring.AddUnitSeismicPing - * @param unitID integer + * @param unitID UnitID * @param pindSize number * @return nil */ @@ -4453,7 +4504,7 @@ int LuaSyncedCtrl::AddUnitSeismicPing(lua_State* L) /*** * @function Spring.AddUnitResource - * @param unitID integer + * @param unitID UnitID * @param resource string "m" | "e" * @param amount number * @return nil @@ -4482,7 +4533,7 @@ int LuaSyncedCtrl::AddUnitResource(lua_State* L) /*** * @function Spring.UseUnitResource - * @param unitID integer + * @param unitID UnitID * @param resource ResourceName * @param amount number * @return boolean? okay @@ -4490,7 +4541,7 @@ int LuaSyncedCtrl::AddUnitResource(lua_State* L) /*** * @function Spring.UseUnitResource - * @param unitID integer + * @param unitID UnitID * @param resources ResourceUsage * @return boolean? okay */ @@ -4561,7 +4612,7 @@ int LuaSyncedCtrl::UseUnitResource(lua_State* L) /*** * * @function Spring.AddObjectDecal - * @param unitID integer + * @param unitID UnitID * @return nil */ int LuaSyncedCtrl::AddObjectDecal(lua_State* L) @@ -4578,7 +4629,7 @@ int LuaSyncedCtrl::AddObjectDecal(lua_State* L) /*** * @function Spring.RemoveObjectDecal - * @param unitID integer + * @param unitID UnitID * @return nil */ int LuaSyncedCtrl::RemoveObjectDecal(lua_State* L) @@ -4642,9 +4693,9 @@ int LuaSyncedCtrl::RemoveGrass(lua_State* L) * @param y number * @param z number * @param heading Heading? - * @param teamID integer? - * @param featureID integer? - * @return integer? featureID returns nil if creation was unsuccessful + * @param teamID TeamID? + * @param featureID FeatureID? + * @return FeatureID? featureID returns nil if creation was unsuccessful */ int LuaSyncedCtrl::CreateFeature(lua_State* L) { @@ -4735,7 +4786,7 @@ void LuaSyncedCtrl::DestroyFeatureCommon(lua_State* L, CFeature* feature) /*** * @function Spring.DestroyFeature - * @param featureID integer + * @param featureID FeatureID * @return nil */ int LuaSyncedCtrl::DestroyFeature(lua_State* L) @@ -4753,8 +4804,8 @@ int LuaSyncedCtrl::DestroyFeature(lua_State* L) /*** Feature Control * * @function Spring.TransferFeature - * @param featureID integer - * @param teamID integer + * @param featureID FeatureID + * @param teamID TeamID * @return nil */ int LuaSyncedCtrl::TransferFeature(lua_State* L) @@ -4774,7 +4825,7 @@ int LuaSyncedCtrl::TransferFeature(lua_State* L) /*** * @function Spring.SetFeatureAlwaysVisible - * @param featureID integer + * @param featureID FeatureID * @param enable boolean * @return nil */ @@ -4786,7 +4837,7 @@ int LuaSyncedCtrl::SetFeatureAlwaysVisible(lua_State* L) /*** * * @function Spring.SetFeatureUseAirLos - * @param featureID integer + * @param featureID FeatureID * @param useAirLos boolean * @return nil */ @@ -4798,7 +4849,7 @@ int LuaSyncedCtrl::SetFeatureUseAirLos(lua_State* L) /*** * @function Spring.SetFeatureHealth - * @param featureID integer + * @param featureID FeatureID * @param health number * @param checkDestruction boolean? (Default: `false`) Whether to destroy feature if feature goes below 0 health. * @return nil @@ -4822,7 +4873,7 @@ int LuaSyncedCtrl::SetFeatureHealth(lua_State* L) /*** * * @function Spring.SetFeatureMaxHealth - * @param featureID integer + * @param featureID FeatureID * @param maxHealth number minimum 0.1 * @return nil */ @@ -4841,7 +4892,7 @@ int LuaSyncedCtrl::SetFeatureMaxHealth(lua_State* L) /*** * @function Spring.SetFeatureReclaim - * @param featureID integer + * @param featureID FeatureID * @param reclaimLeft number * @return nil */ @@ -4858,7 +4909,7 @@ int LuaSyncedCtrl::SetFeatureReclaim(lua_State* L) /*** * @function Spring.SetFeatureResources - * @param featureID integer + * @param featureID FeatureID * @param metal number * @param energy number * @param reclaimTime number? @@ -4888,8 +4939,8 @@ int LuaSyncedCtrl::SetFeatureResources(lua_State* L) /*** * @function Spring.SetFeatureResurrect * - * @param featureID integer - * @param unitDef string|integer Can be a number id or a string name, this allows cancelling resurrection by passing `-1`. + * @param featureID FeatureID + * @param unitDef (string|integer)? Can be a number id or a string name, this allows cancelling resurrection by passing `-1`. * @param facing Facing? (Default: `"south"`) * @param progress number? Set the level of progress. * @return nil @@ -4925,7 +4976,7 @@ int LuaSyncedCtrl::SetFeatureResurrect(lua_State* L) * Enable feature movement control. * * @function Spring.SetFeatureMoveCtrl - * @param featureID integer + * @param featureID FeatureID * @param enabled true Enable feature movement. * @param initialVelocityX number? Initial velocity on X axis, or `nil` for no change. * @param initialVelocityY number? Initial velocity on Y axis, or `nil` for no change. @@ -4954,7 +5005,7 @@ int LuaSyncedCtrl::SetFeatureResurrect(lua_State* L) * ``` * * @function Spring.SetFeatureMoveCtrl - * @param featureID integer + * @param featureID FeatureID * @param enabled false Disable feature movement. * @param velocityMaskX number? Lock velocity change in X dimension when not using `MoveCtrl`. `0` to lock, non-zero to allow, or `nil` to for no change. * @param velocityMaskY number? Lock velocity change in Y dimension when not using `MoveCtrl`. `0` to lock, non-zero to allow, or `nil` to for no change. @@ -4998,7 +5049,7 @@ int LuaSyncedCtrl::SetFeatureMoveCtrl(lua_State* L) /*** * @function Spring.SetFeaturePhysics - * @param featureID integer + * @param featureID FeatureID * @param posX number * @param posY number * @param posZ number @@ -5021,7 +5072,7 @@ int LuaSyncedCtrl::SetFeaturePhysics(lua_State* L) /*** * @function Spring.SetFeatureMass - * @param featureID integer + * @param featureID FeatureID * @param mass number * @return nil */ @@ -5033,7 +5084,7 @@ int LuaSyncedCtrl::SetFeatureMass(lua_State* L) /*** * @function Spring.SetFeaturePosition - * @param featureID integer + * @param featureID FeatureID * @param x number * @param y number * @param z number @@ -5060,7 +5111,7 @@ int LuaSyncedCtrl::SetFeaturePosition(lua_State* L) /*** * @function Spring.SetFeatureRotation * Note: PYR order - * @param featureID integer + * @param featureID FeatureID * @param pitch number Rotation in X axis * @param yaw number Rotation in Y axis * @param roll number Rotation in Z axis @@ -5080,7 +5131,7 @@ int LuaSyncedCtrl::SetFeatureRotation(lua_State* L) * @deprecated It's strongly that you use the overload that accepts * a right direction as `frontDir` alone doesn't define object orientation. * - * @param featureID integer + * @param featureID FeatureID * @param frontx number * @param fronty number * @param frontz number @@ -5093,7 +5144,7 @@ int LuaSyncedCtrl::SetFeatureRotation(lua_State* L) * * Both vectors will be normalized in the engine. * - * @param featureID integer + * @param featureID FeatureID * @param frontx number * @param fronty number * @param frontz number @@ -5113,7 +5164,7 @@ int LuaSyncedCtrl::SetFeatureDirection(lua_State* L) * completely upright, new `{upx, upy, upz}` direction will be used as new "up" * vector, the rotation set by "heading" will remain preserved. * - * @param featureID integer + * @param featureID FeatureID * @param heading Heading * @param upx number * @param upy number @@ -5129,7 +5180,7 @@ int LuaSyncedCtrl::SetFeatureHeadingAndUpDir(lua_State* L) * * @see Spring.SetFeatureMoveCtrl for disabling/enabling this control * @function Spring.SetFeatureVelocity - * @param featureID integer + * @param featureID FeatureID * @param velX number in elmos/frame * @param velY number in elmos/frame * @param velZ number in elmos/frame @@ -5142,7 +5193,7 @@ int LuaSyncedCtrl::SetFeatureVelocity(lua_State* L) /*** * @function Spring.SetFeatureBlocking - * @param featureID integer + * @param featureID FeatureID * @param isBlocking boolean? If `true` add this feature to the `GroundBlockingMap`, but only if it collides with solid objects (or is being set to collide with the `isSolidObjectCollidable` argument). If `false`, remove this feature from the `GroundBlockingMap`. No change if `nil`. * @param isSolidObjectCollidable boolean? Enable or disable collision with solid objects, or no change if `nil`. * @param isProjectileCollidable boolean? Enable or disable collision with projectiles, or no change if `nil`. @@ -5160,7 +5211,7 @@ int LuaSyncedCtrl::SetFeatureBlocking(lua_State* L) /*** * @function Spring.SetFeatureNoSelect - * @param featureID integer + * @param featureID FeatureID * @param noSelect boolean * @return nil */ @@ -5181,7 +5232,7 @@ int LuaSyncedCtrl::SetFeatureNoSelect(lua_State* L) * * Check `Spring.SetUnitMidAndAimPos` for further explanation of the arguments. * - * @param featureID integer + * @param featureID FeatureID * @param mpX number * @param mpY number * @param mpZ number @@ -5229,9 +5280,9 @@ int LuaSyncedCtrl::SetFeatureMidAndAimPos(lua_State* L) /*** * @function Spring.SetFeatureRadiusAndHeight - * @param featureID integer - * @param radius number - * @param height number + * @param featureID FeatureID + * @param radius number? + * @param height number? * @return boolean success */ int LuaSyncedCtrl::SetFeatureRadiusAndHeight(lua_State* L) @@ -5267,16 +5318,16 @@ int LuaSyncedCtrl::SetFeatureRadiusAndHeight(lua_State* L) * * Check `Spring.SetUnitCollisionVolumeData` for further explanation of the arguments. * - * @param featureID integer + * @param featureID FeatureID * @param scaleX number * @param scaleY number * @param scaleZ number * @param offsetX number * @param offsetY number * @param offsetZ number - * @param vType number - * @param tType number - * @param Axis number + * @param vType integer + * @param tType integer + * @param Axis integer * @return nil */ int LuaSyncedCtrl::SetFeatureCollisionVolumeData(lua_State* L) @@ -5287,8 +5338,8 @@ int LuaSyncedCtrl::SetFeatureCollisionVolumeData(lua_State* L) /*** * @function Spring.SetFeaturePieceCollisionVolumeData - * @param featureID integer - * @param pieceIndex number + * @param featureID FeatureID + * @param pieceIndex integer * @param enable boolean * @param scaleX number * @param scaleY number @@ -5309,8 +5360,8 @@ int LuaSyncedCtrl::SetFeaturePieceCollisionVolumeData(lua_State* L) /*** * * @function Spring.SetFeaturePieceVisible - * @param featureID integer - * @param pieceIndex number + * @param featureID FeatureID + * @param pieceIndex integer * @param visible boolean * @return nil */ @@ -5323,10 +5374,10 @@ int LuaSyncedCtrl::SetFeaturePieceVisible(lua_State* L) * * @function Spring.SetFeaturePieceMatrix * - * @param featureID integer - * @param pieceIndex number + * @param featureID FeatureID + * @param pieceIndex integer * @param matrix number[] an array of 16 floats - * @return boolean? valid - if the matrix can be used for the purpose of defining the piece spatial transformation + * @return boolean? valid whether the matrix can be used for the purpose of defining the piece spatial transformation */ int LuaSyncedCtrl::SetFeaturePieceMatrix(lua_State* L) { @@ -5342,7 +5393,7 @@ int LuaSyncedCtrl::SetFeaturePieceMatrix(lua_State* L) * Starts or resets an internal feature fire timer, when reaching zero the * feature will be destroyed. * - * @param featureID integer + * @param featureID FeatureID * @param fireTime number in seconds */ int LuaSyncedCtrl::SetFeatureFireTime(lua_State* L) @@ -5377,7 +5428,7 @@ int LuaSyncedCtrl::SetFeatureFireTime(lua_State* L) * * The smoke timer affects both the duration and size of the smoke particles. * - * @param featureID integer + * @param featureID FeatureID * @param smokeTime number in seconds */ int LuaSyncedCtrl::SetFeatureSmokeTime(lua_State* L) @@ -5412,10 +5463,10 @@ int LuaSyncedCtrl::SetFeatureSmokeTime(lua_State* L) * * @function Spring.CreateUnitWreck * - * @param unitID integer + * @param unitID UnitID * @param wreckLevel integer? (Default: `1`) Wreck index to use. * @param doSmoke boolean? (Default: `true`) Wreck emits smoke when `true`. - * @return integer? featureID The wreck featureID, or nil if it couldn't be created or unit doesn't exist. + * @return FeatureID? featureID The wreck featureID, or nil if it couldn't be created or unit doesn't exist. */ int LuaSyncedCtrl::CreateUnitWreck(lua_State* L) { @@ -5442,10 +5493,10 @@ int LuaSyncedCtrl::CreateUnitWreck(lua_State* L) * * @function Spring.CreateFeatureWreck * - * @param featureID integer + * @param featureID FeatureID * @param wreckLevel integer? (Default: `1`) Wreck index to use. * @param doSmoke boolean? (Default: `false`) Wreck emits smoke when `true`. - * @return integer? featureID The wreck featureID, or nil if it couldn't be created or unit doesn't exist. + * @return FeatureID? featureID The wreck featureID, or nil if it couldn't be created or unit doesn't exist. */ int LuaSyncedCtrl::CreateFeatureWreck(lua_State* L) @@ -5475,7 +5526,7 @@ int LuaSyncedCtrl::CreateFeatureWreck(lua_State* L) /*** * @function Spring.SetProjectileAlwaysVisible - * @param projectileID integer + * @param projectileID ProjectileID * @param alwaysVisible boolean * @return nil */ @@ -5488,7 +5539,7 @@ int LuaSyncedCtrl::SetProjectileAlwaysVisible(lua_State* L) /*** * * @function Spring.SetProjectileUseAirLos - * @param projectileID integer + * @param projectileID ProjectileID * @param useAirLos boolean * @return nil */ @@ -5507,8 +5558,8 @@ int LuaSyncedCtrl::SetProjectileUseAirLos(lua_State* L) * * @function Spring.SetProjectileMoveControl * - * @param projectileID integer - * @param enable boolean + * @param projectileID ProjectileID + * @param enable boolean? */ int LuaSyncedCtrl::SetProjectileMoveControl(lua_State* L) { @@ -5526,7 +5577,7 @@ int LuaSyncedCtrl::SetProjectileMoveControl(lua_State* L) /*** Set the position of a projectile * * @function Spring.SetProjectilePosition - * @param projectileID integer + * @param projectileID ProjectileID * @param posX number? (Default: `0`) * @param posY number? (Default: `0`) * @param posZ number? (Default: `0`) @@ -5554,7 +5605,7 @@ int LuaSyncedCtrl::SetProjectilePosition(lua_State* L) * * @see Spring.SetProjectileMoveControl * @function Spring.SetProjectileVelocity - * @param projectileID integer + * @param projectileID ProjectileID * @param velX number in elmos/frame * @param velY number in elmos/frame * @param velZ number in elmos/frame @@ -5566,7 +5617,7 @@ int LuaSyncedCtrl::SetProjectileVelocity(lua_State* L) /*** * @function Spring.SetProjectileCollision - * @param projectileID integer + * @param projectileID ProjectileID */ int LuaSyncedCtrl::SetProjectileCollision(lua_State* L) { @@ -5590,8 +5641,8 @@ int LuaSyncedCtrl::SetProjectileCollision(lua_State* L) /*** Set projectile target (object) * * @function Spring.SetProjectileTarget - * @param projectileID integer - * @param targetID number + * @param projectileID ProjectileID + * @param targetID UnitID|FeatureID|ProjectileID * @param targetType ProjectileTargetType * @return boolean? validTarget */ @@ -5600,7 +5651,7 @@ int LuaSyncedCtrl::SetProjectileCollision(lua_State* L) * * @function Spring.SetProjectileTarget * - * @param projectileID integer + * @param projectileID ProjectileID * @param posX number * @param posY number * @param posZ number @@ -5683,8 +5734,8 @@ int LuaSyncedCtrl::SetProjectileTarget(lua_State* L) /*** Set Time To Live for a projectile * * @function Spring.SetProjectileTimeToLive - * @param projectileID integer - * @param ttl number Remaining time to live in frames + * @param projectileID ProjectileID + * @param ttl integer Remaining time to live in frames */ int LuaSyncedCtrl::SetProjectileTimeToLive(lua_State* L) { @@ -5704,7 +5755,7 @@ int LuaSyncedCtrl::SetProjectileTimeToLive(lua_State* L) /*** * @function Spring.SetProjectileIsIntercepted - * @param projectileID integer + * @param projectileID ProjectileID */ int LuaSyncedCtrl::SetProjectileIsIntercepted(lua_State* L) { @@ -5723,7 +5774,7 @@ int LuaSyncedCtrl::SetProjectileIsIntercepted(lua_State* L) /*** * @function Spring.SetProjectileDamages - * @param unitID integer + * @param projectileID ProjectileID * @param weaponNum integer * @param key string * @param value number @@ -5759,7 +5810,7 @@ int LuaSyncedCtrl::SetProjectileDamages(lua_State* L) /*** * @function Spring.SetProjectileIgnoreTrackingError - * @param projectileID integer + * @param projectileID ProjectileID * @param ignore boolean */ int LuaSyncedCtrl::SetProjectileIgnoreTrackingError(lua_State* L) @@ -5788,7 +5839,7 @@ int LuaSyncedCtrl::SetProjectileIgnoreTrackingError(lua_State* L) /*** * @function Spring.SetProjectileGravity - * @param projectileID integer + * @param projectileID ProjectileID * @param grav number? (Default: `0`) * @return nil */ @@ -5814,8 +5865,8 @@ int LuaSyncedCtrl::SetProjectileSpinVec(lua_State* L) { return 0; } // FIXME: DE * Non passed or nil args don't set params. * * @function Spring.SetPieceProjectileParams - * @param projectileID integer - * @param explosionFlags number? + * @param projectileID ProjectileID + * @param explosionFlags integer? * @param spinAngle number? * @param spinSpeed number? * @param spinVectorX number? @@ -5845,7 +5896,7 @@ int LuaSyncedCtrl::SetPieceProjectileParams(lua_State* L) // /*** * @function Spring.SetProjectileCEG - * @param projectileID integer + * @param projectileID ProjectileID * @param ceg_name string * @return nil */ @@ -5884,7 +5935,7 @@ int LuaSyncedCtrl::SetProjectileCEG(lua_State* L) /*** * @function Spring.UnitFinishCommand - * @param unitID integer + * @param unitID UnitID */ int LuaSyncedCtrl::UnitFinishCommand(lua_State* L) { @@ -5901,7 +5952,7 @@ int LuaSyncedCtrl::UnitFinishCommand(lua_State* L) /*** * @function Spring.GiveOrderToUnit - * @param unitID integer + * @param unitID UnitID * @param cmdID CMD|integer The command ID. * @param params CreateCommandParams? Parameters for the given command. * @param options CreateCommandOptions? @@ -5939,7 +5990,7 @@ int LuaSyncedCtrl::GiveOrderToUnit(lua_State* L) * Give order to multiple units, specified by table keys. * * @function Spring.GiveOrderToUnitMap - * @param unitMap table A table with unit IDs as keys. + * @param unitMap table A table with unit IDs as keys. * @param cmdID CMD|integer The command ID. * @param params CreateCommandParams? Parameters for the given command. * @param options CreateCommandOptions? @@ -5981,7 +6032,7 @@ int LuaSyncedCtrl::GiveOrderToUnitMap(lua_State* L) /*** * * @function Spring.GiveOrderToUnitArray - * @param unitIDs integer[] An array of unit IDs. + * @param unitIDs UnitID[] An array of unit IDs. * @param cmdID CMD|integer The command ID. * @param params CreateCommandParams? Parameters for the given command. * @param options CreateCommandOptions? @@ -6025,7 +6076,7 @@ int LuaSyncedCtrl::GiveOrderToUnitArray(lua_State* L) /*** * * @function Spring.GiveOrderArrayToUnit - * @param unitID integer + * @param unitID UnitID * @param commands CreateCommand[] * @return boolean ordersGiven */ @@ -6063,7 +6114,7 @@ int LuaSyncedCtrl::GiveOrderArrayToUnit(lua_State* L) /*** * @function Spring.GiveOrderArrayToUnitMap - * @param unitMap table A table with unit IDs as keys. + * @param unitMap table A table with unit IDs as keys. * @param commands CreateCommand[] * @return integer unitsOrdered The number of units ordered. */ @@ -6103,7 +6154,7 @@ int LuaSyncedCtrl::GiveOrderArrayToUnitMap(lua_State* L) /*** * @function Spring.GiveOrderArrayToUnitArray - * @param unitIDs integer[] Array of unit IDs. + * @param unitIDs UnitID[] Array of unit IDs. * @param commands CreateCommand[] * @param pairwise boolean? (Default: `false`) When `false`, assign all commands to each unit. * @@ -6486,7 +6537,6 @@ int LuaSyncedCtrl::SetHeightMap(lua_State* L) * ``` * * @param luaFunction function - * @param arg number * @param ... number * @return integer? absTotalHeightMapAmountChanged */ @@ -6981,7 +7031,7 @@ int LuaSyncedCtrl::AddSmoothMesh(lua_State* L) * @param z number * @param height number * @param terraform number? (Default: `1`) - * @return number? The absolute height difference, or `nil` if coordinates are invalid. + * @return number? heightDifference The absolute height difference, or `nil` if coordinates are invalid. */ int LuaSyncedCtrl::SetSmoothMesh(lua_State* L) { @@ -7067,7 +7117,7 @@ int LuaSyncedCtrl::SetSmoothMeshFunc(lua_State* L) * @function Spring.SetMapSquareTerrainType * @param x number * @param z number - * @param newType number + * @param newType integer * @return integer? oldType */ int LuaSyncedCtrl::SetMapSquareTerrainType(lua_State* L) @@ -7095,7 +7145,7 @@ int LuaSyncedCtrl::SetMapSquareTerrainType(lua_State* L) /*** * @function Spring.SetTerrainTypeData - * @param typeIndex number + * @param typeIndex integer * @param speedTanks number? (Default: nil) * @param speedKBOts number? (Default: nil) * @param speedHovers number? (Default: nil) @@ -7142,9 +7192,9 @@ int LuaSyncedCtrl::SetTerrainTypeData(lua_State* L) /*** * @function Spring.SetSquareBuildingMask - * @param x number - * @param z number - * @param mask number + * @param x integer + * @param z integer + * @param mask integer * @return nil * * See also buildingMask unitdef tag. @@ -7174,7 +7224,7 @@ int LuaSyncedCtrl::SetSquareBuildingMask(lua_State* L) /*** * @function Spring.UnitWeaponFire - * @param unitID integer + * @param unitID UnitID * @param weaponID integer * @return nil */ @@ -7197,7 +7247,7 @@ int LuaSyncedCtrl::UnitWeaponFire(lua_State* L) // NB: not permanent /*** * @function Spring.UnitWeaponHoldFire - * @param unitID integer + * @param unitID UnitID * @param weaponID integer * @return nil */ @@ -7228,7 +7278,7 @@ int LuaSyncedCtrl::UnitWeaponHoldFire(lua_State* L) * the normal update rate is set. * * @function Spring.ForceUnitCollisionUpdate - * @param unitID integer + * @param unitID UnitID * @return nil */ int LuaSyncedCtrl::ForceUnitCollisionUpdate(lua_State* L) @@ -7246,10 +7296,10 @@ int LuaSyncedCtrl::ForceUnitCollisionUpdate(lua_State* L) /*** * * @function Spring.UnitAttach - * @param transporterID integer - * @param passengerID integer - * @param pieceNum number - * @param force boolean + * @param transporterID UnitID + * @param passengerID UnitID + * @param pieceNum integer + * @param force boolean? * @return nil */ int LuaSyncedCtrl::UnitAttach(lua_State* L) @@ -7287,7 +7337,7 @@ int LuaSyncedCtrl::UnitAttach(lua_State* L) /*** * @function Spring.UnitDetach - * @param passengerID integer + * @param passengerID UnitID * @return nil */ int LuaSyncedCtrl::UnitDetach(lua_State* L) @@ -7309,7 +7359,7 @@ int LuaSyncedCtrl::UnitDetach(lua_State* L) /*** * @function Spring.UnitDetachFromAir - * @param passengerID integer + * @param passengerID UnitID * @return nil */ int LuaSyncedCtrl::UnitDetachFromAir(lua_State* L) @@ -7342,8 +7392,8 @@ int LuaSyncedCtrl::UnitDetachFromAir(lua_State* L) /*** Disables collisions between the two units to allow colvol intersection during the approach. * * @function Spring.SetUnitLoadingTransport - * @param passengerID integer - * @param transportID integer + * @param passengerID UnitID + * @param transportID UnitID * @return nil */ int LuaSyncedCtrl::SetUnitLoadingTransport(lua_State* L) @@ -7371,29 +7421,29 @@ int LuaSyncedCtrl::SetUnitLoadingTransport(lua_State* L) /*** * @class ProjectileParams * @x_helper - * @field pos xyz - * @field speed xyz - * @field spread xyz - * @field error xyz - * @field end xyz - * @field owner integer - * @field team integer - * @field ttl number - * @field gravity number - * @field tracking number - * @field maxRange number - * @field startAlpha number - * @field endAlpha number - * @field model string - * @field cegTag string + * @field pos xyz? + * @field speed xyz? + * @field spread xyz? + * @field error xyz? + * @field end xyz? + * @field owner UnitID? + * @field team TeamID? + * @field ttl number? + * @field gravity number? + * @field tracking number? + * @field maxRange number? + * @field startAlpha number? + * @field endAlpha number? + * @field model string? + * @field cegTag string? */ /*** * * @function Spring.SpawnProjectile - * @param weaponDefID integer + * @param weaponDefID WeaponDefID * @param projectileParams ProjectileParams - * @return integer? projectileID + * @return ProjectileID? projectileID */ int LuaSyncedCtrl::SpawnProjectile(lua_State* L) { @@ -7413,7 +7463,7 @@ int LuaSyncedCtrl::SpawnProjectile(lua_State* L) /*** Silently removes projectiles (no explosion). * * @function Spring.DeleteProjectile - * @param projectileID integer + * @param projectileID ProjectileID * @return nil */ int LuaSyncedCtrl::DeleteProjectile(lua_State* L) @@ -7547,18 +7597,18 @@ static int SetExplosionParam(lua_State* L, CExplosionParams& params, DamageArray * * @class ExplosionParams * @x_helper - * @field weaponDef number - * @field owner number - * @field hitUnit number - * @field hitFeature number - * @field craterAreaOfEffect number - * @field damageAreaOfEffect number - * @field edgeEffectiveness number - * @field explosionSpeed number - * @field gfxMod number - * @field impactOnly boolean - * @field ignoreOwner boolean - * @field damageGround boolean + * @field weaponDef WeaponDefID? + * @field owner UnitID? + * @field hitUnit UnitID? + * @field hitFeature FeatureID? + * @field craterAreaOfEffect number? + * @field damageAreaOfEffect number? + * @field edgeEffectiveness number? + * @field explosionSpeed number? + * @field gfxMod number? + * @field impactOnly boolean? + * @field ignoreOwner boolean? + * @field damageGround boolean? */ /*** @@ -7569,7 +7619,7 @@ static int SetExplosionParam(lua_State* L, CExplosionParams& params, DamageArray * @param dirX number? (Default: `0`) * @param dirY number? (Default: `0`) * @param dirZ number? (Default: `0`) - * @param explosionParams ExplosionParams + * @param explosionParams ExplosionParams? * @return nil */ int LuaSyncedCtrl::SpawnExplosion(lua_State* L) @@ -7677,7 +7727,7 @@ int LuaSyncedCtrl::SpawnCEG(lua_State* L) /*** Equal to the UnitScript versions of EmitSFX, but takes position and direction arguments (in either unit- or piece-space) instead of a piece index. * * @function Spring.SpawnSFX - * @param unitID integer? (Default: `0`) + * @param unitID UnitID? (Default: `0`) * @param sfxID integer? (Default: `0`) * @param posX number? (Default: `0`) * @param posY number? (Default: `0`) @@ -7766,7 +7816,7 @@ int LuaSyncedCtrl::SetExperienceGrade(lua_State* L) /*** * * @function Spring.SetRadarErrorParams - * @param allyTeamID integer + * @param allyTeamID AllyTeamID * @param allyteamErrorSize number * @param baseErrorSize number? * @param baseErrorMult number? @@ -7900,7 +7950,7 @@ static bool ParseCommandDescription(lua_State* L, int table, /*** * @function Spring.EditUnitCmdDesc - * @param unitID integer + * @param unitID UnitID * @param cmdDescID integer * @param cmdArray CommandDescription */ @@ -7934,7 +7984,7 @@ int LuaSyncedCtrl::EditUnitCmdDesc(lua_State* L) * Insert a command description at a specific index. * * @function Spring.InsertUnitCmdDesc - * @param unitID integer + * @param unitID UnitID * @param index integer * @param cmdDesc CommandDescription */ @@ -7942,7 +7992,7 @@ int LuaSyncedCtrl::EditUnitCmdDesc(lua_State* L) * Insert a command description at the last position. * * @function Spring.InsertUnitCmdDesc - * @param unitID integer + * @param unitID UnitID * @param cmdDesc CommandDescription */ int LuaSyncedCtrl::InsertUnitCmdDesc(lua_State* L) @@ -7981,7 +8031,7 @@ int LuaSyncedCtrl::InsertUnitCmdDesc(lua_State* L) /*** * @function Spring.RemoveUnitCmdDesc - * @param unitID integer + * @param unitID UnitID * @param cmdDescID integer? */ int LuaSyncedCtrl::RemoveUnitCmdDesc(lua_State* L) diff --git a/rts/Lua/LuaSyncedCtrl.h b/rts/Lua/LuaSyncedCtrl.h index 84239a079af..3b59dcae8cc 100644 --- a/rts/Lua/LuaSyncedCtrl.h +++ b/rts/Lua/LuaSyncedCtrl.h @@ -52,6 +52,7 @@ class LuaSyncedCtrl static int AddTeamResource(lua_State* L); static int UseTeamResource(lua_State* L); static int SetTeamResource(lua_State* L); + static int AddTeamResourceExcessStats(lua_State* L); static int SetTeamShareLevel(lua_State* L); static int ShareTeamResource(lua_State* L); diff --git a/rts/Lua/LuaSyncedMoveCtrl.cpp b/rts/Lua/LuaSyncedMoveCtrl.cpp index aa81f219657..264ea4e1bd7 100644 --- a/rts/Lua/LuaSyncedMoveCtrl.cpp +++ b/rts/Lua/LuaSyncedMoveCtrl.cpp @@ -139,7 +139,7 @@ static inline DerivedMoveType* ParseDerivedMoveType(lua_State* L, const char* ca /*** * @function MoveCtrl.IsEnabled - * @param unitID integer + * @param unitID UnitID * @return boolean? isEnabled */ int LuaSyncedMoveCtrl::IsEnabled(lua_State* L) @@ -156,7 +156,7 @@ int LuaSyncedMoveCtrl::IsEnabled(lua_State* L) /*** * @function MoveCtrl.Enable - * @param unitID integer + * @param unitID UnitID */ int LuaSyncedMoveCtrl::Enable(lua_State* L) { @@ -172,7 +172,7 @@ int LuaSyncedMoveCtrl::Enable(lua_State* L) /*** * @function MoveCtrl.Disable - * @param unitID integer + * @param unitID UnitID */ int LuaSyncedMoveCtrl::Disable(lua_State* L) { @@ -190,7 +190,7 @@ int LuaSyncedMoveCtrl::Disable(lua_State* L) /*** * @function MoveCtrl.SetTag - * @param unitID integer + * @param unitID UnitID * @param tag integer */ int LuaSyncedMoveCtrl::SetTag(lua_State* L) @@ -208,6 +208,7 @@ int LuaSyncedMoveCtrl::SetTag(lua_State* L) /*** * @function MoveCtrl.GetTag * @param tag integer? + * @return integer? tag `nil` if the unit is not using a script move type. */ int LuaSyncedMoveCtrl::GetTag(lua_State* L) { @@ -227,7 +228,7 @@ int LuaSyncedMoveCtrl::GetTag(lua_State* L) /*** * @function MoveCtrl.SetProgressState - * @param unitID integer + * @param unitID UnitID * @param state * | 0 # Done * | 1 # Active @@ -277,7 +278,7 @@ int LuaSyncedMoveCtrl::SetProgressState(lua_State* L) /*** * @function MoveCtrl.SetExtrapolate - * @param unitID integer + * @param unitID UnitID * @param extrapolate boolean */ int LuaSyncedMoveCtrl::SetExtrapolate(lua_State* L) @@ -296,7 +297,7 @@ int LuaSyncedMoveCtrl::SetExtrapolate(lua_State* L) /*** * @function MoveCtrl.SetPhysics - * @param unitID integer + * @param unitID UnitID * @param posX number Position X component. * @param posY number Position Y component. * @param posZ number Position Z component. @@ -327,7 +328,7 @@ int LuaSyncedMoveCtrl::SetPhysics(lua_State* L) /*** * @function MoveCtrl.SetPosition - * @param unitID integer + * @param unitID UnitID * @param posX number Position X component. * @param posY number Position Y component. * @param posZ number Position Z component. @@ -350,7 +351,7 @@ int LuaSyncedMoveCtrl::SetPosition(lua_State* L) /*** * @function MoveCtrl.SetVelocity - * @param unitID integer + * @param unitID UnitID * @param velX number Velocity X component. * @param velY number Velocity Y component. * @param velZ number Velocity Z component. @@ -373,7 +374,7 @@ int LuaSyncedMoveCtrl::SetVelocity(lua_State* L) /*** * @function MoveCtrl.SetRelativeVelocity - * @param unitID integer + * @param unitID UnitID * @param relVelX number Relative velocity X component. * @param relVelY number Relative velocity Y component. * @param relVelZ number Relative velocity Z component. @@ -396,7 +397,7 @@ int LuaSyncedMoveCtrl::SetRelativeVelocity(lua_State* L) /*** * @function MoveCtrl.SetRotation - * @param unitID integer + * @param unitID UnitID * @param rotX number Rotation X component. * @param rotY number Rotation Y component. * @param rotZ number Rotation Z component. @@ -430,7 +431,7 @@ int LuaSyncedMoveCtrl::SetRotationOffset(lua_State* L) /*** * @function MoveCtrl.SetRotationVelocity - * @param unitID integer + * @param unitID UnitID * @param rotVelX number Rotation velocity X component. * @param rotVelY number Rotation velocity Y component. * @param rotVelZ number Rotation velocity Z component. @@ -452,7 +453,7 @@ int LuaSyncedMoveCtrl::SetRotationVelocity(lua_State* L) /*** * @function MoveCtrl.SetHeading - * @param unitID integer + * @param unitID UnitID * @param heading Heading */ int LuaSyncedMoveCtrl::SetHeading(lua_State* L) @@ -473,7 +474,7 @@ int LuaSyncedMoveCtrl::SetHeading(lua_State* L) /*** * @function MoveCtrl.SetTrackSlope - * @param unitID integer + * @param unitID UnitID * @param trackSlope boolean */ int LuaSyncedMoveCtrl::SetTrackSlope(lua_State* L) @@ -490,7 +491,7 @@ int LuaSyncedMoveCtrl::SetTrackSlope(lua_State* L) /*** * @function MoveCtrl.SetTrackGround - * @param unitID integer + * @param unitID UnitID * @param trackGround boolean */ int LuaSyncedMoveCtrl::SetTrackGround(lua_State* L) @@ -507,7 +508,7 @@ int LuaSyncedMoveCtrl::SetTrackGround(lua_State* L) /*** * @function MoveCtrl.SetTrackLimits - * @param unitID integer + * @param unitID UnitID * @param trackLimits boolean */ int LuaSyncedMoveCtrl::SetTrackLimits(lua_State* L) @@ -524,7 +525,7 @@ int LuaSyncedMoveCtrl::SetTrackLimits(lua_State* L) /*** * @function MoveCtrl.SetGroundOffset - * @param unitID integer + * @param unitID UnitID * @param groundOffset number */ int LuaSyncedMoveCtrl::SetGroundOffset(lua_State* L) @@ -541,7 +542,7 @@ int LuaSyncedMoveCtrl::SetGroundOffset(lua_State* L) /*** * @function MoveCtrl.SetGravity - * @param unitID integer + * @param unitID UnitID * @param gravityFactor number */ int LuaSyncedMoveCtrl::SetGravity(lua_State* L) @@ -558,7 +559,7 @@ int LuaSyncedMoveCtrl::SetGravity(lua_State* L) /*** * @function MoveCtrl.SetDrag - * @param unitID integer + * @param unitID UnitID * @param drag number */ int LuaSyncedMoveCtrl::SetDrag(lua_State* L) @@ -575,7 +576,7 @@ int LuaSyncedMoveCtrl::SetDrag(lua_State* L) /*** * @function MoveCtrl.SetWindFactor - * @param unitID integer + * @param unitID UnitID * @param windFactor number */ int LuaSyncedMoveCtrl::SetWindFactor(lua_State* L) @@ -592,7 +593,7 @@ int LuaSyncedMoveCtrl::SetWindFactor(lua_State* L) /*** * @function MoveCtrl.SetLimits - * @param unitID integer + * @param unitID UnitID * @param minX number Minimum position X component. * @param minY number Minimum position Y component. * @param minZ number Minimum position Z component. @@ -617,7 +618,7 @@ int LuaSyncedMoveCtrl::SetLimits(lua_State* L) /*** * @function MoveCtrl.SetNoBlocking - * @param unitID integer + * @param unitID UnitID * @param noBlocking boolean */ int LuaSyncedMoveCtrl::SetNoBlocking(lua_State* L) @@ -640,7 +641,7 @@ int LuaSyncedMoveCtrl::SetSlopeStop(lua_State* L) { return 0; } /*** * @function MoveCtrl.SetCollideStop - * @param unitID integer + * @param unitID UnitID * @param collideStop boolean */ int LuaSyncedMoveCtrl::SetCollideStop(lua_State* L) @@ -657,7 +658,7 @@ int LuaSyncedMoveCtrl::SetCollideStop(lua_State* L) /*** * @function MoveCtrl.SetLimitsStop - * @param unitID integer + * @param unitID UnitID * @param limitsStop boolean */ int LuaSyncedMoveCtrl::SetLimitsStop(lua_State* L) @@ -727,15 +728,15 @@ static inline bool SetMoveTypeValue(lua_State* L, AMoveType* moveType, int keyId * * Overload 1: * @param boolean - * @return number numAssignedValues + * @return integer numAssignedValues * * Overload 2: * @param number - * @return number numAssignedValues + * @return integer numAssignedValues * * Overload 3: * @param boolean - * @return number numAssignedValues + * @return integer numAssignedValues */ static int SetMoveTypeData(lua_State* L, AMoveType* moveType, const char* caller) { @@ -796,13 +797,13 @@ static int SetMoveTypeData(lua_State* L, AMoveType* moveType, const char* caller /*** * @function MoveCtrl.SetGunshipMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param moveType HoverAirMoveType - * @return number numAssignedValues + * @return integer numAssignedValues */ /*** * @function MoveCtrl.SetGunshipMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param key * | GenericMoveTypeBooleanKey * | "collide" @@ -811,11 +812,11 @@ static int SetMoveTypeData(lua_State* L, AMoveType* moveType, const char* caller * | "useSmoothMesh" * | "bankingAllowed" * @param value boolean - * @return number numAssignedValues + * @return integer numAssignedValues */ /*** * @function MoveCtrl.SetGunshipMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param key * | GenericMoveTypeNumberKey * | "wantedHeight" @@ -827,7 +828,7 @@ static int SetMoveTypeData(lua_State* L, AMoveType* moveType, const char* caller * | "currentPitch" * | "maxDrift" * @param value number - * @return number numAssignedValues + * @return integer numAssignedValues */ int LuaSyncedMoveCtrl::SetGunshipMoveTypeData(lua_State* L) { @@ -858,24 +859,24 @@ int LuaSyncedMoveCtrl::SetGunshipMoveTypeData(lua_State* L) /*** * @function MoveCtrl.SetAirMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param moveType StrafeAirMoveType - * @return number numAssignedValues + * @return integer numAssignedValues */ /*** * @function MoveCtrl.SetAirMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param key * | GenericMoveTypeBooleanKey * | "collide" * | "useSmoothMesh" * | "loopbackAttack" * @param value boolean - * @return number numAssignedValues + * @return integer numAssignedValues */ /*** * @function MoveCtrl.SetAirMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param key * | GenericMoveTypeNumberKey * | "wantedHeight" @@ -892,15 +893,15 @@ int LuaSyncedMoveCtrl::SetGunshipMoveTypeData(lua_State* L) * | "attackSafetyDistance" * | "myGravity" * @param value number - * @return number numAssignedValues + * @return integer numAssignedValues */ /*** * @function MoveCtrl.SetAirMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param key * | "maneuverBlockTime" * @param value integer - * @return number numAssignedValues + * @return integer numAssignedValues */ int LuaSyncedMoveCtrl::SetAirMoveTypeData(lua_State* L) { @@ -927,24 +928,24 @@ int LuaSyncedMoveCtrl::SetAirMoveTypeData(lua_State* L) /*** * @function MoveCtrl.SetGroundMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param moveType GroundMoveType - * @return number numAssignedValues + * @return integer numAssignedValues */ /*** * @function MoveCtrl.SetGroundMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param key * | GenericMoveTypeBooleanKey * | "atGoal" * | "atEndOfPath" * | "pushResistant" * @param value boolean - * @return number numAssignedValues + * @return integer numAssignedValues */ /*** * @function MoveCtrl.SetGroundMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param key * | GenericMoveTypeNumberKey * | "turnRate" @@ -957,15 +958,15 @@ int LuaSyncedMoveCtrl::SetAirMoveTypeData(lua_State* L) * | "maxReverseSpeed" * | "sqSkidSpeedMult" * @param value number - * @return number numAssignedValues + * @return integer numAssignedValues */ /*** * @function MoveCtrl.SetGroundMoveTypeData - * @param unitID integer + * @param unitID UnitID * @param key * | "minScriptChangeHeading" * @param value integer - * @return number numAssignedValues + * @return integer numAssignedValues */ int LuaSyncedMoveCtrl::SetGroundMoveTypeData(lua_State* L) { @@ -979,7 +980,7 @@ int LuaSyncedMoveCtrl::SetGroundMoveTypeData(lua_State* L) /*** * @function MoveCtrl.SetMoveDef - * @param unitID integer + * @param unitID UnitID * @param moveDef integer|string Name or path type of the MoveDef. * @return boolean success `true` if the `MoveDef` was set, `false` if `unitID` or `moveDef` were invalid, or if the unit does not support a `MoveDef`. */ diff --git a/rts/Lua/LuaSyncedRead.cpp b/rts/Lua/LuaSyncedRead.cpp index 40b25741b52..4c71083fb10 100644 --- a/rts/Lua/LuaSyncedRead.cpp +++ b/rts/Lua/LuaSyncedRead.cpp @@ -91,6 +91,7 @@ using std::max; static const LuaHashString hs_n("n"); + /****************************************************************************** * Synced Read * @@ -99,10 +100,13 @@ static const LuaHashString hs_n("n"); bool LuaSyncedRead::PushEntries(lua_State* L) { - // allegiance constants + /*** @field Spring.ALL_UNITS integer */ LuaPushNamedNumber(L, "ALL_UNITS", LuaUtils::AllUnits); + /*** @field Spring.MY_UNITS integer */ LuaPushNamedNumber(L, "MY_UNITS", LuaUtils::MyUnits); + /*** @field Spring.ALLY_UNITS integer */ LuaPushNamedNumber(L, "ALLY_UNITS", LuaUtils::AllyUnits); + /*** @field Spring.ENEMY_UNITS integer */ LuaPushNamedNumber(L, "ENEMY_UNITS", LuaUtils::EnemyUnits); // READ routines, sync safe @@ -399,6 +403,8 @@ bool LuaSyncedRead::PushEntries(lua_State* L) REGISTER_LUA_CFUNC(TraceRayGroundInDirection); REGISTER_LUA_CFUNC(TraceRayGroundBetweenPositions); + REGISTER_LUA_CFUNC(TraceRayInDirection); + REGISTER_LUA_CFUNC(TraceRayBetweenPositions); REGISTER_LUA_CFUNC(GetRadarErrorParams); @@ -765,7 +771,6 @@ static int GetRulesParam(lua_State* L, const char* caller, int index, * @section gamestates ******************************************************************************/ - /*** * * @function Spring.IsCheatingEnabled @@ -837,7 +842,7 @@ int LuaSyncedRead::IsNoCostEnabled(lua_State* L) * * @function Spring.GetGlobalLos * - * @param teamID integer? + * @param teamID TeamID? * * @return boolean enabled */ @@ -904,8 +909,8 @@ int LuaSyncedRead::IsGameOver(lua_State* L) * * @function Spring.GetGameFrame * - * @return number t1 frameNum % dayFrames - * @return number t2 frameNum / dayFrames + * @return integer t1 frameNum % dayFrames + * @return integer t2 frameNum / dayFrames */ int LuaSyncedRead::GetGameFrame(lua_State* L) { @@ -1005,7 +1010,7 @@ int LuaSyncedRead::GetGameRulesParams(lua_State* L) * * @function Spring.GetTeamRulesParams * - * @param teamID integer + * @param teamID TeamID * * @return RulesParams rulesParams map with rules names as key and values as values */ @@ -1031,7 +1036,7 @@ int LuaSyncedRead::GetTeamRulesParams(lua_State* L) * * @function Spring.GetPlayerRulesParams * - * @param playerID integer + * @param playerID PlayerID * * @return RulesParams rulesParams map with rules names as key and values as values */ @@ -1100,7 +1105,7 @@ static int GetUnitRulesParamLosMask(lua_State* L, const CUnit* unit) * * @function Spring.GetUnitRulesParams * - * @param unitID integer + * @param unitID UnitID * * @return RulesParams rulesParams map with rules names as key and values as values */ @@ -1118,7 +1123,7 @@ int LuaSyncedRead::GetUnitRulesParams(lua_State* L) * * @function Spring.GetFeatureRulesParams * - * @param featureID integer + * @param featureID FeatureID * * @return RulesParams rulesParams map with rules names as key and values as values */ @@ -1154,9 +1159,9 @@ int LuaSyncedRead::GetFeatureRulesParams(lua_State* L) * * @function Spring.GetGameRulesParam * - * @param ruleRef number|string the rule index or name + * @param name string rules-parameter key (only this argument is read) * - * @return number?|string value + * @return number|boolean|string|nil value */ int LuaSyncedRead::GetGameRulesParam(lua_State* L) { @@ -1169,7 +1174,7 @@ int LuaSyncedRead::GetGameRulesParam(lua_State* L) * * @function Spring.GetTeamRulesParam * - * @param teamID integer + * @param teamID TeamID * @param ruleRef number|string the rule index or name * * @return number|string|nil value @@ -1197,7 +1202,7 @@ int LuaSyncedRead::GetTeamRulesParam(lua_State* L) * * @function Spring.GetPlayerRulesParam * - * @param playerID integer + * @param playerID PlayerID * @param ruleRef number|string the rule index or name * * @return number|string|nil value @@ -1228,7 +1233,7 @@ int LuaSyncedRead::GetPlayerRulesParam(lua_State* L) * * @function Spring.GetUnitRulesParam * - * @param unitID integer + * @param unitID UnitID * @param ruleRef number|string the rule index or name * * @return number|string|nil value @@ -1247,7 +1252,7 @@ int LuaSyncedRead::GetUnitRulesParam(lua_State* L) * * @function Spring.GetFeatureRulesParam * - * @param featureID integer + * @param featureID FeatureID * @param ruleRef number|string the rule index or name * * @return number|string|nil value @@ -1383,7 +1388,7 @@ int LuaSyncedRead::GetModOptions(lua_State* L) * @param x number * @param z number * - * @return number heading + * @return integer heading */ int LuaSyncedRead::GetHeadingFromVector(lua_State* L) { @@ -1399,7 +1404,7 @@ int LuaSyncedRead::GetHeadingFromVector(lua_State* L) * * @function Spring.GetVectorFromHeading * - * @param heading number + * @param heading integer * * @return number x * @return number z @@ -1415,7 +1420,7 @@ int LuaSyncedRead::GetVectorFromHeading(lua_State* L) /*** * @function Spring.GetFacingFromHeading - * @param heading number + * @param heading integer * @return FacingInteger facing */ int LuaSyncedRead::GetFacingFromHeading(lua_State* L) @@ -1427,7 +1432,7 @@ int LuaSyncedRead::GetFacingFromHeading(lua_State* L) /*** * @function Spring.GetHeadingFromFacing * @param facing FacingInteger - * @return number heading + * @return integer heading */ int LuaSyncedRead::GetHeadingFromFacing(lua_State* L) { @@ -1527,7 +1532,7 @@ int LuaSyncedRead::GetSideData(lua_State* L) * * @function Spring.GetGaiaTeamID * - * @return integer teamID + * @return TeamID teamID */ int LuaSyncedRead::GetGaiaTeamID(lua_State* L) { @@ -1543,7 +1548,7 @@ int LuaSyncedRead::GetGaiaTeamID(lua_State* L) * * @function Spring.GetAllyTeamStartBox * - * @param allyID integer + * @param allyID AllyTeamID * * @return number? xMin * @return number? zMin @@ -1575,7 +1580,7 @@ int LuaSyncedRead::GetAllyTeamStartBox(lua_State* L) * * @function Spring.GetTeamStartPosition * - * @param teamID integer + * @param teamID TeamID * * @return number? x * @return number? y @@ -1602,7 +1607,7 @@ int LuaSyncedRead::GetTeamStartPosition(lua_State* L) /*** * * @function Spring.GetMapStartPositions - * @return float3[] array of positions indexed by teamID + * @return float3[] startPositions array of positions indexed by teamID */ int LuaSyncedRead::GetMapStartPositions(lua_State* L) { @@ -1628,7 +1633,7 @@ int LuaSyncedRead::GetMapStartPositions(lua_State* L) /*** * * @function Spring.GetAllyTeamList - * @return integer[] allyTeamIDs + * @return AllyTeamID[] allyTeamIDs */ int LuaSyncedRead::GetAllyTeamList(lua_State* L) { @@ -1650,15 +1655,15 @@ int LuaSyncedRead::GetAllyTeamList(lua_State* L) * * @function Spring.GetTeamList * @param allyTeamID -1|nil (Default: `-1`) - * @return number[] teamIDs List of team IDs. + * @return TeamID[] teamIDs List of team IDs. */ /*** * Get team IDs in a specific ally team. * * @function Spring.GetTeamList - * @param allyTeamID integer The ally team ID to filter teams by. A value less than 0 will return all teams. - * @return number[]? teamIDs List of team IDs or `nil` if `allyTeamID` is invalid. + * @param allyTeamID AllyTeamID The ally team ID to filter teams by. A value less than 0 will return all teams. + * @return TeamID[]? teamIDs List of team IDs or `nil` if `allyTeamID` is invalid. */ int LuaSyncedRead::GetTeamList(lua_State* L) { @@ -1695,9 +1700,9 @@ int LuaSyncedRead::GetTeamList(lua_State* L) /*** * * @function Spring.GetPlayerList - * @param teamID integer? (Default: `-1`) to filter by when >= 0 + * @param teamID TeamID? (Default: `-1`) to filter by when >= 0 * @param active boolean? (Default: `false`) whether to filter only active teams - * @return number[]? list of playerIDs + * @return PlayerID[]? playerIDs List of playerIDs. */ int LuaSyncedRead::GetPlayerList(lua_State* L) { @@ -1749,14 +1754,14 @@ int LuaSyncedRead::GetPlayerList(lua_State* L) /*** * * @function Spring.GetTeamInfo - * @param teamID integer + * @param teamID TeamID * @param getTeamKeys boolean? (Default: `true`) whether to return the customTeamKeys table - * @return integer? teamID - * @return number leader + * @return TeamID? teamID + * @return PlayerID leader * @return number isDead * @return number hasAI * @return string side - * @return number allyTeam + * @return AllyTeamID allyTeam * @return number incomeMultiplier * @return table customTeamKeys when getTeamKeys is true, otherwise nil */ @@ -1800,8 +1805,8 @@ int LuaSyncedRead::GetTeamInfo(lua_State* L) /*** * * @function Spring.GetTeamAllyTeamID - * @param teamID integer - * @return integer? allyTeamID + * @param teamID TeamID + * @return AllyTeamID? allyTeamID */ int LuaSyncedRead::GetTeamAllyTeamID(lua_State* L) { @@ -1821,7 +1826,7 @@ int LuaSyncedRead::GetTeamAllyTeamID(lua_State* L) /*** * * @function Spring.GetTeamResources - * @param teamID integer + * @param teamID TeamID * @param resource ResourceName * @return number? currentLevel The current amount of the resource that the team has in storage at this moment * @return number storage The maximum storage capacity for the resource. @@ -1880,13 +1885,13 @@ int LuaSyncedRead::GetTeamResources(lua_State* L) /*** * * @function Spring.GetTeamUnitStats - * @param teamID integer - * @return number? killed - * @return number died - * @return number capturedBy - * @return number capturedFrom - * @return number received - * @return number sent + * @param teamID TeamID + * @return integer? killed + * @return integer died + * @return integer capturedBy + * @return integer capturedFrom + * @return integer received + * @return integer sent */ int LuaSyncedRead::GetTeamUnitStats(lua_State* L) { @@ -1915,7 +1920,7 @@ int LuaSyncedRead::GetTeamUnitStats(lua_State* L) /*** * * @function Spring.GetTeamResourceStats - * @param teamID integer + * @param teamID TeamID * @param resource ResourceName * @return number? used * @return number produced @@ -1968,7 +1973,7 @@ int LuaSyncedRead::GetTeamResourceStats(lua_State* L) * Returns a team's damage stats. Note that all damage is counted, * including self-inflicted and unconfirmed out-of-sight. * - * @param teamID integer + * @param teamID TeamID * @return number damageDealt * @return number damageReceived */ @@ -1995,8 +2000,8 @@ int LuaSyncedRead::GetTeamDamageStats(lua_State* L) /*** * @class TeamStats * @x_helper - * @field time number - * @field frame number + * @field time integer + * @field frame integer * @field metalUsed number * @field metalProduced number * @field metalExcess number @@ -2020,16 +2025,16 @@ int LuaSyncedRead::GetTeamDamageStats(lua_State* L) /*** * Get the number of history entries. * @function Spring.GetTeamStatsHistory - * @param teamID integer + * @param teamID TeamID * @return integer? historyCount The number of history entries, or `nil` if unable to resolve team. */ /*** * Get team stats history. * @function Spring.GetTeamStatsHistory - * @param teamID integer + * @param teamID TeamID * @param startIndex integer * @param endIndex integer? (Default: startIndex) - * @return TeamStats[] The team stats history, or `nil` if unable to resolve team. + * @return TeamStats[] teamStatsHistory The team stats history, or `nil` if unable to resolve team. */ int LuaSyncedRead::GetTeamStatsHistory(lua_State* L) { @@ -2078,35 +2083,35 @@ int LuaSyncedRead::GetTeamStatsHistory(lua_State* L) // the `stats.frame` var indicates the frame when a new entry needs to get added, // for the most recent stats entry this lies obviously in the future, // so we just output the current frame here - HSTR_PUSH_NUMBER(L, "time", gs->GetLuaSimFrame() / GAME_SPEED); - HSTR_PUSH_NUMBER(L, "frame", gs->GetLuaSimFrame()); + LuaPushNamedNumber(L, "time", gs->GetLuaSimFrame() / GAME_SPEED); + LuaPushNamedNumber(L, "frame", gs->GetLuaSimFrame()); } else { - HSTR_PUSH_NUMBER(L, "time", stats.frame / GAME_SPEED); - HSTR_PUSH_NUMBER(L, "frame", stats.frame); + LuaPushNamedNumber(L, "time", stats.frame / GAME_SPEED); + LuaPushNamedNumber(L, "frame", stats.frame); } - HSTR_PUSH_NUMBER(L, "metalUsed", stats.metalUsed); - HSTR_PUSH_NUMBER(L, "metalProduced", stats.metalProduced); - HSTR_PUSH_NUMBER(L, "metalExcess", stats.metalExcess); - HSTR_PUSH_NUMBER(L, "metalReceived", stats.metalReceived); - HSTR_PUSH_NUMBER(L, "metalSent", stats.metalSent); - - HSTR_PUSH_NUMBER(L, "energyUsed", stats.energyUsed); - HSTR_PUSH_NUMBER(L, "energyProduced", stats.energyProduced); - HSTR_PUSH_NUMBER(L, "energyExcess", stats.energyExcess); - HSTR_PUSH_NUMBER(L, "energyReceived", stats.energyReceived); - HSTR_PUSH_NUMBER(L, "energySent", stats.energySent); - - HSTR_PUSH_NUMBER(L, "damageDealt", stats.damageDealt); - HSTR_PUSH_NUMBER(L, "damageReceived", stats.damageReceived); - - HSTR_PUSH_NUMBER(L, "unitsProduced", stats.unitsProduced); - HSTR_PUSH_NUMBER(L, "unitsDied", stats.unitsDied); - HSTR_PUSH_NUMBER(L, "unitsReceived", stats.unitsReceived); - HSTR_PUSH_NUMBER(L, "unitsSent", stats.unitsSent); - HSTR_PUSH_NUMBER(L, "unitsCaptured", stats.unitsCaptured); - HSTR_PUSH_NUMBER(L, "unitsOutCaptured", stats.unitsOutCaptured); - HSTR_PUSH_NUMBER(L, "unitsKilled", stats.unitsKilled); + LuaPushNamedNumber(L, "metalUsed", stats.metalUsed); + LuaPushNamedNumber(L, "metalProduced", stats.metalProduced); + LuaPushNamedNumber(L, "metalExcess", stats.metalExcess); + LuaPushNamedNumber(L, "metalReceived", stats.metalReceived); + LuaPushNamedNumber(L, "metalSent", stats.metalSent); + + LuaPushNamedNumber(L, "energyUsed", stats.energyUsed); + LuaPushNamedNumber(L, "energyProduced", stats.energyProduced); + LuaPushNamedNumber(L, "energyExcess", stats.energyExcess); + LuaPushNamedNumber(L, "energyReceived", stats.energyReceived); + LuaPushNamedNumber(L, "energySent", stats.energySent); + + LuaPushNamedNumber(L, "damageDealt", stats.damageDealt); + LuaPushNamedNumber(L, "damageReceived", stats.damageReceived); + + LuaPushNamedNumber(L, "unitsProduced", stats.unitsProduced); + LuaPushNamedNumber(L, "unitsDied", stats.unitsDied); + LuaPushNamedNumber(L, "unitsReceived", stats.unitsReceived); + LuaPushNamedNumber(L, "unitsSent", stats.unitsSent); + LuaPushNamedNumber(L, "unitsCaptured", stats.unitsCaptured); + LuaPushNamedNumber(L, "unitsOutCaptured", stats.unitsOutCaptured); + LuaPushNamedNumber(L, "unitsKilled", stats.unitsKilled); } lua_rawseti(L, -2, count++); } @@ -2119,7 +2124,7 @@ int LuaSyncedRead::GetTeamStatsHistory(lua_State* L) /*** * * @function Spring.GetTeamLuaAI - * @param teamID integer + * @param teamID TeamID * @return string */ int LuaSyncedRead::GetTeamLuaAI(lua_State* L) @@ -2154,9 +2159,9 @@ int LuaSyncedRead::GetTeamLuaAI(lua_State* L) * Also returns the current unit count for readable teams as the 2nd value. * * @function Spring.GetTeamMaxUnits - * @param teamID integer - * @return number maxUnits - * @return number? currentUnits + * @param teamID TeamID + * @return integer maxUnits + * @return integer? currentUnits */ int LuaSyncedRead::GetTeamMaxUnits(lua_State* L) { @@ -2177,17 +2182,17 @@ int LuaSyncedRead::GetTeamMaxUnits(lua_State* L) /*** * * @function Spring.GetPlayerInfo - * @param playerID integer + * @param playerID PlayerID * @param getPlayerOpts boolean? (Default: `true`) whether to return custom player options * @return string name * @return boolean active * @return boolean spectator - * @return integer teamID - * @return integer allyTeamID + * @return TeamID teamID + * @return AllyTeamID allyTeamID * @return number pingTime * @return number cpuUsage * @return string country - * @return number rank + * @return integer rank * @return boolean hasSkirmishAIsInTeam * @return {[string]: string} playerOpts when playerOpts is true * @return boolean desynced @@ -2242,8 +2247,8 @@ int LuaSyncedRead::GetPlayerInfo(lua_State* L) /*** Returns unit controlled by player on FPS mode * * @function Spring.GetPlayerControlledUnit - * @param playerID integer - * @return number? + * @param playerID PlayerID + * @return UnitID? */ int LuaSyncedRead::GetPlayerControlledUnit(lua_State* L) { @@ -2278,10 +2283,10 @@ int LuaSyncedRead::GetPlayerControlledUnit(lua_State* L) /*** * * @function Spring.GetAIInfo - * @param teamID integer + * @param teamID TeamID * @return integer skirmishAIID * @return string name - * @return integer hostingPlayerID + * @return PlayerID hostingPlayerID * @return string shortName When synced `"SYNCED_NOSHORTNAME"`, otherwise the AI shortname or `"UNKNOWN"`. * @return string version When synced `"SYNCED_NOVERSION"`, otherwise the AI version or `"UNKNOWN"`. * @return table options @@ -2309,8 +2314,8 @@ int LuaSyncedRead::GetAIInfo(lua_State* L) // no unsynced Skirmish AI info for synchronized scripts if (CLuaHandle::GetHandleSynced(L)) { - HSTR_PUSH(L, "SYNCED_NOSHORTNAME"); - HSTR_PUSH(L, "SYNCED_NOVERSION"); + LuaPushString(L, "SYNCED_NOSHORTNAME"); + LuaPushString(L, "SYNCED_NOVERSION"); lua_newtable(L); } else if (skirmishAIHandler.IsLocalSkirmishAI(skirmishAIId)) { lua_pushsstring(L, aiData->shortName); @@ -2324,8 +2329,8 @@ int LuaSyncedRead::GetAIInfo(lua_State* L) lua_rawset(L, -3); } } else { - HSTR_PUSH(L, "UNKNOWN"); - HSTR_PUSH(L, "UNKNOWN"); + LuaPushString(L, "UNKNOWN"); + LuaPushString(L, "UNKNOWN"); lua_newtable(L); } numVals += 3; @@ -2337,7 +2342,7 @@ int LuaSyncedRead::GetAIInfo(lua_State* L) /*** * * @function Spring.GetAllyTeamInfo - * @param allyTeamID integer + * @param allyTeamID AllyTeamID * @return table? */ int LuaSyncedRead::GetAllyTeamInfo(lua_State* L) @@ -2363,8 +2368,8 @@ int LuaSyncedRead::GetAllyTeamInfo(lua_State* L) /*** * * @function Spring.AreTeamsAllied - * @param teamID1 number - * @param teamID2 number + * @param teamID1 TeamID + * @param teamID2 TeamID * @return boolean? */ int LuaSyncedRead::AreTeamsAllied(lua_State* L) @@ -2383,8 +2388,8 @@ int LuaSyncedRead::AreTeamsAllied(lua_State* L) /*** * * @function Spring.ArePlayersAllied - * @param playerID1 number - * @param playerID2 number + * @param playerID1 PlayerID + * @param playerID2 PlayerID * @return boolean? */ int LuaSyncedRead::ArePlayersAllied(lua_State* L) @@ -2428,7 +2433,7 @@ int LuaSyncedRead::ArePlayersAllied(lua_State* L) * * @see UnsyncedRead.GetVisibleUnits * - * @return number[] unitIDs + * @return UnitID[] unitIDs */ int LuaSyncedRead::GetAllUnits(lua_State* L) { @@ -2457,8 +2462,8 @@ int LuaSyncedRead::GetAllUnits(lua_State* L) /*** * * @function Spring.GetTeamUnits - * @param teamID integer - * @return number[]? unitIDs + * @param teamID TeamID + * @return UnitID[]? unitIDs */ int LuaSyncedRead::GetTeamUnits(lua_State* L) { @@ -2557,8 +2562,8 @@ static inline void InsertSearchUnitDefs(const UnitDef* ud, bool allied) /*** * * @function Spring.GetTeamUnitsSorted - * @param teamID integer - * @return table unitsByDef A table where keys are unitDefIDs and values are unitIDs + * @param teamID TeamID + * @return table unitsByDef A table where keys are unitDefIDs and values are arrays of unitIDs */ int LuaSyncedRead::GetTeamUnitsSorted(lua_State* L) { @@ -2629,7 +2634,7 @@ int LuaSyncedRead::GetTeamUnitsSorted(lua_State* L) } if (!gtuObjectIDs.empty()) { - HSTR_PUSH(L, "unknown"); + LuaPushString(L, "unknown"); defCount += 1; unitCount = 1; @@ -2653,8 +2658,8 @@ int LuaSyncedRead::GetTeamUnitsSorted(lua_State* L) /*** * * @function Spring.GetTeamUnitsCounts - * @param teamID integer - * @return table? countByUnit A table where keys are unitDefIDs and values are counts. + * @param teamID TeamID + * @return table? countByUnit A table where keys are unitDefIDs and values are counts. */ int LuaSyncedRead::GetTeamUnitsCounts(lua_State* L) { @@ -2723,7 +2728,7 @@ int LuaSyncedRead::GetTeamUnitsCounts(lua_State* L) defCount++; } if (unknownCount > 0) { - HSTR_PUSH_NUMBER(L, "unknown", unknownCount); + LuaPushNamedNumber(L, "unknown", unknownCount); defCount++; } @@ -2736,9 +2741,9 @@ int LuaSyncedRead::GetTeamUnitsCounts(lua_State* L) /*** * * @function Spring.GetTeamUnitsByDefs - * @param teamID integer - * @param unitDefIDs number|number[] - * @return number[]? unitIDs + * @param teamID TeamID + * @param unitDefIDs UnitDefID|UnitDefID[] + * @return UnitID[]? unitIDs */ int LuaSyncedRead::GetTeamUnitsByDefs(lua_State* L) { @@ -2812,9 +2817,9 @@ int LuaSyncedRead::GetTeamUnitsByDefs(lua_State* L) /*** * * @function Spring.GetTeamUnitDefCount - * @param teamID integer - * @param unitDefID integer - * @return number? count + * @param teamID TeamID + * @param unitDefID UnitDefID + * @return integer? count */ int LuaSyncedRead::GetTeamUnitDefCount(lua_State* L) { @@ -2873,8 +2878,8 @@ int LuaSyncedRead::GetTeamUnitDefCount(lua_State* L) /*** * * @function Spring.GetTeamUnitCount - * @param teamID integer - * @return number? count + * @param teamID TeamID + * @return integer? count */ int LuaSyncedRead::GetTeamUnitCount(lua_State* L) { @@ -2980,8 +2985,8 @@ static void GetFilteredUnits(lua_State *L, int allegiance, const std::vector& features) { * @param zmin number * @param xmax number * @param zmax number - * @return number[] featureIDs + * @return FeatureID[] featureIDs */ int LuaSyncedRead::GetFeaturesInRectangle(lua_State* L) { @@ -3483,7 +3490,7 @@ int LuaSyncedRead::GetFeaturesInRectangle(lua_State* L) * @param y number * @param z number * @param radius number - * @return number[] featureIDs + * @return FeatureID[] featureIDs */ int LuaSyncedRead::GetFeaturesInSphere(lua_State* L) { @@ -3507,8 +3514,8 @@ int LuaSyncedRead::GetFeaturesInSphere(lua_State* L) * @param x number * @param z number * @param radius number - * @param allegiance number? - * @return number[] featureIDs + * @param allegiance integer? + * @return FeatureID[] featureIDs */ int LuaSyncedRead::GetFeaturesInCylinder(lua_State* L) { @@ -3574,7 +3581,7 @@ static void GetProjectilesLuaTable(lua_State* L, const std::vector * @function Spring.GetAllProjectiles * @param excludeWeaponProjectiles boolean? (Default: `false`) * @param excludePieceProjectiles boolean? (Default: `false`) - * @return number[] projectileIDs + * @return ProjectileID[] projectileIDs */ int LuaSyncedRead::GetAllProjectiles(lua_State* L) { @@ -3594,7 +3601,7 @@ int LuaSyncedRead::GetAllProjectiles(lua_State* L) * @param zmax number * @param excludeWeaponProjectiles boolean? (Default: `false`) * @param excludePieceProjectiles boolean? (Default: `false`) - * @return number[] projectileIDs + * @return ProjectileID[] projectileIDs */ int LuaSyncedRead::GetProjectilesInRectangle(lua_State* L) { @@ -3624,7 +3631,7 @@ int LuaSyncedRead::GetProjectilesInRectangle(lua_State* L) * @param radius number * @param excludeWeaponProjectiles boolean? (Default: false) * @param excludePieceProjectiles boolean? (Default: false) - * @return number[] projectileIDs + * @return ProjectileID[] projectileIDs */ int LuaSyncedRead::GetProjectilesInSphere(lua_State* L) { @@ -3652,7 +3659,7 @@ int LuaSyncedRead::GetProjectilesInSphere(lua_State* L) * Dead units are not valid. * * @function Spring.ValidUnitID - * @param unitID integer + * @param unitID UnitID * @return boolean */ int LuaSyncedRead::ValidUnitID(lua_State* L) @@ -3665,12 +3672,12 @@ int LuaSyncedRead::ValidUnitID(lua_State* L) /*** * @class UnitState * @x_helper - * @field firestate number - * @field movestate number - * @field repeat boolean - * @field cloak boolean - * @field active boolean - * @field trajectory boolean + * @field firestate integer + * @field movestate integer + * @field repeat boolean? + * @field cloak boolean? + * @field active boolean? + * @field trajectory boolean? * @field autoland boolean? * @field autorepairlevel number? * @field loopbackattack boolean? @@ -3678,10 +3685,27 @@ int LuaSyncedRead::ValidUnitID(lua_State* L) /*** + * + * Gets various states for the given unit. * * @function Spring.GetUnitStates - * @param unitID integer - * @return UnitState + * @param unitID UnitID + * @param retTable false Return a table instead of multiple values. Defaults to `true` + * @param binState true Include binary state (activated, etc)? Defaults to `retTable` + * @param amtState true Include Air/Hover MoveType state if available? Defaults to `retTable` + * @return number fireState + * @return number moveState + * @return number autorepairlevel `-1` if not set + * @return boolean repeat + * @return boolean cloak + * @return boolean active + * @return boolean trajectory + * @return boolean? autoLand + * @return boolean? loopbackAttack + * @overload fun(unitID: UnitID, retTable: false, binState: false?, amtState: false?): number, number, number + * @overload fun(unitID: UnitID, retTable: false, binState: true, amtState: false?): number, number, number, boolean, boolean, boolean, boolean + * @overload fun(unitID: UnitID, retTable: false, binState: false?, amtState: true): number, number, number, boolean?, boolean? + * @overload fun(unitID: UnitID, retTable: true?, binState: boolean?, amtState: boolean?): UnitState */ int LuaSyncedRead::GetUnitStates(lua_State* L) { @@ -3736,16 +3760,16 @@ int LuaSyncedRead::GetUnitStates(lua_State* L) lua_createtable(L, 0, 9); { - HSTR_PUSH_NUMBER(L, "firestate", unit->fireState); - HSTR_PUSH_NUMBER(L, "movestate", unit->moveState); - HSTR_PUSH_NUMBER(L, "autorepairlevel", (mCAI != nullptr)? mCAI->repairBelowHealth: -1.0f); + LuaPushNamedNumber(L, "firestate", unit->fireState); + LuaPushNamedNumber(L, "movestate", unit->moveState); + LuaPushNamedNumber(L, "autorepairlevel", (mCAI != nullptr)? mCAI->repairBelowHealth: -1.0f); } if (binState) { - HSTR_PUSH_BOOL(L, "repeat", unit->commandAI->repeatOrders); - HSTR_PUSH_BOOL(L, "cloak", unit->wantCloak); - HSTR_PUSH_BOOL(L, "active", unit->activated); - HSTR_PUSH_BOOL(L, "trajectory", unit->useHighTrajectory); + LuaPushNamedBool(L, "repeat", unit->commandAI->repeatOrders); + LuaPushNamedBool(L, "cloak", unit->wantCloak); + LuaPushNamedBool(L, "active", unit->activated); + LuaPushNamedBool(L, "trajectory", unit->useHighTrajectory); } if (amtState) { @@ -3753,14 +3777,14 @@ int LuaSyncedRead::GetUnitStates(lua_State* L) const CStrafeAirMoveType* sAMT = nullptr; if ((hAMT = dynamic_cast(mt)) != nullptr) { - HSTR_PUSH_BOOL(L, "autoland", hAMT->autoLand); - HSTR_PUSH_BOOL(L, "loopbackattack", false); + LuaPushNamedBool(L, "autoland", hAMT->autoLand); + LuaPushNamedBool(L, "loopbackattack", false); return 1; } if ((sAMT = dynamic_cast(mt)) != nullptr) { - HSTR_PUSH_BOOL(L, "autoland", sAMT->autoLand); - HSTR_PUSH_BOOL(L, "loopbackattack", sAMT->loopbackAttack); + LuaPushNamedBool(L, "autoland", sAMT->autoLand); + LuaPushNamedBool(L, "loopbackattack", sAMT->loopbackAttack); return 1; } } @@ -3773,7 +3797,7 @@ int LuaSyncedRead::GetUnitStates(lua_State* L) /*** * * @function Spring.GetUnitArmored - * @param unitID integer + * @param unitID UnitID * @return boolean? armored * @return number armorMultiple */ @@ -3792,7 +3816,7 @@ int LuaSyncedRead::GetUnitArmored(lua_State* L) /*** * * @function Spring.GetUnitIsActive - * @param unitID integer + * @param unitID UnitID * @return boolean? isActive */ int LuaSyncedRead::GetUnitIsActive(lua_State* L) @@ -3809,7 +3833,7 @@ int LuaSyncedRead::GetUnitIsActive(lua_State* L) /*** * * @function Spring.GetUnitIsCloaked - * @param unitID integer + * @param unitID UnitID * @return boolean? isCloaked */ int LuaSyncedRead::GetUnitIsCloaked(lua_State* L) @@ -3826,7 +3850,7 @@ int LuaSyncedRead::GetUnitIsCloaked(lua_State* L) /*** * * @function Spring.GetUnitSeismicSignature - * @param unitID integer + * @param unitID UnitID * @return number? seismicSignature */ int LuaSyncedRead::GetUnitSeismicSignature(lua_State* L) @@ -3858,7 +3882,7 @@ int LuaSyncedRead::GetUnitLeavesGhost(lua_State* L) /*** * * @function Spring.GetUnitSelfDTime - * @param unitID integer + * @param unitID UnitID * @return integer? selfDTime */ int LuaSyncedRead::GetUnitSelfDTime(lua_State* L) @@ -3875,7 +3899,7 @@ int LuaSyncedRead::GetUnitSelfDTime(lua_State* L) /*** * * @function Spring.GetUnitStockpile - * @param unitID integer + * @param unitID UnitID * @return integer? numStockpiled * @return integer? numStockpileQued * @return number? buildPercent @@ -3899,9 +3923,9 @@ int LuaSyncedRead::GetUnitStockpile(lua_State* L) /*** * * @function Spring.GetUnitSensorRadius - * @param unitID integer + * @param unitID UnitID * @param type string one of los, airLos, radar, sonar, seismic, radarJammer, sonarJammer - * @return number? radius + * @return integer? radius */ int LuaSyncedRead::GetUnitSensorRadius(lua_State* L) { @@ -3942,15 +3966,15 @@ int LuaSyncedRead::GetUnitSensorRadius(lua_State* L) /*** * * @function Spring.GetUnitPosErrorParams - * @param unitID integer - * @param allyTeamID integer? + * @param unitID UnitID + * @param allyTeamID AllyTeamID? * @return number? posErrorVectorX * @return number posErrorVectorY * @return number posErrorVectorZ * @return number posErrorDeltaX * @return number posErrorDeltaY * @return number posErrorDeltaZ - * @return number nextPosErrorUpdatebaseErrorMult + * @return integer nextPosErrorUpdate * @return boolean posErrorBit */ int LuaSyncedRead::GetUnitPosErrorParams(lua_State* L) @@ -3979,7 +4003,7 @@ int LuaSyncedRead::GetUnitPosErrorParams(lua_State* L) /*** * * @function Spring.GetUnitTooltip - * @param unitID integer + * @param unitID UnitID * @return string? */ int LuaSyncedRead::GetUnitTooltip(lua_State* L) @@ -4019,8 +4043,8 @@ int LuaSyncedRead::GetUnitTooltip(lua_State* L) /*** * * @function Spring.GetUnitDefID - * @param unitID integer - * @return number? + * @param unitID UnitID + * @return UnitDefID? */ int LuaSyncedRead::GetUnitDefID(lua_State* L) { @@ -4049,10 +4073,10 @@ int LuaSyncedRead::GetUnitDefID(lua_State* L) * numerical ID is not too useful so you can use the name, but this * may get deprecated at some point. * -* @param unitID integer +* @param unitID UnitID * -* @return integer|boolean|nil moveDefID -* @return string? moveDefName +* @return integer|false|nil moveDefID +* @return string|nil moveDefName */ int LuaSyncedRead::GetUnitMoveDefID(lua_State* L) @@ -4077,8 +4101,8 @@ int LuaSyncedRead::GetUnitMoveDefID(lua_State* L) /*** * * @function Spring.GetUnitTeam - * @param unitID integer - * @return number? + * @param unitID UnitID + * @return TeamID? */ int LuaSyncedRead::GetUnitTeam(lua_State* L) { @@ -4094,8 +4118,8 @@ int LuaSyncedRead::GetUnitTeam(lua_State* L) /*** * * @function Spring.GetUnitAllyTeam - * @param unitID integer - * @return number? + * @param unitID UnitID + * @return AllyTeamID? */ int LuaSyncedRead::GetUnitAllyTeam(lua_State* L) { @@ -4115,7 +4139,7 @@ int LuaSyncedRead::GetUnitAllyTeam(lua_State* L) * Note that a "neutral" unit can belong to any ally-team (ally, enemy, Gaia). * To check if a unit is Gaia, check its owner team. * - * @param unitID integer + * @param unitID UnitID * @return boolean? */ int LuaSyncedRead::GetUnitNeutral(lua_State* L) @@ -4132,7 +4156,7 @@ int LuaSyncedRead::GetUnitNeutral(lua_State* L) /*** * * @function Spring.GetUnitHealth - * @param unitID integer + * @param unitID UnitID * @return number? health * @return number maxHealth * @return number paralyzeDamage @@ -4171,7 +4195,7 @@ int LuaSyncedRead::GetUnitHealth(lua_State* L) /*** * * @function Spring.GetUnitIsDead - * @param unitID integer + * @param unitID UnitID * @return boolean? */ int LuaSyncedRead::GetUnitIsDead(lua_State* L) @@ -4194,7 +4218,7 @@ int LuaSyncedRead::GetUnitIsDead(lua_State* L) * Use other callouts to differentiate them if you need to. * * @function Spring.GetUnitIsStunned - * @param unitID integer + * @param unitID UnitID * @return boolean? stunnedOrBuilt unit is disabled * @return boolean stunned unit is either stunned via EMP or being transported by a non-fireplatform * @return boolean beingBuilt unit is under construction @@ -4215,7 +4239,7 @@ int LuaSyncedRead::GetUnitIsStunned(lua_State* L) /*** * * @function Spring.GetUnitIsBeingBuilt - * @param unitID integer + * @param unitID UnitID * @return boolean beingBuilt * @return number buildProgress */ @@ -4233,7 +4257,7 @@ int LuaSyncedRead::GetUnitIsBeingBuilt(lua_State* L) /*** * * @function Spring.GetUnitResources - * @param unitID integer + * @param unitID UnitID * @return number? metalMake * @return number metalUse * @return number energyMake @@ -4254,9 +4278,9 @@ int LuaSyncedRead::GetUnitResources(lua_State* L) /*** * @function Spring.GetUnitStorage - * @param unitID integer - * @return number Unit's metal storage - * @return number Unit's energy storage + * @param unitID UnitID + * @return number metalStorage Unit's metal storage + * @return number energyStorage Unit's energy storage */ int LuaSyncedRead::GetUnitStorage(lua_State* L) { @@ -4272,7 +4296,7 @@ int LuaSyncedRead::GetUnitStorage(lua_State* L) /*** * @function Spring.GetUnitCosts - * @param unitID integer + * @param unitID UnitID * @return number? buildTime * @return number metalCost * @return number energyCost @@ -4298,7 +4322,7 @@ int LuaSyncedRead::GetUnitCosts(lua_State* L) /*** * @function Spring.GetUnitCostTable - * @param unitID integer + * @param unitID UnitID * @return ResourceCost? cost The cost of the unit, or `nil` if invalid. * @return number? buildTime The build time the unit, or `nil` if invalid. */ @@ -4322,7 +4346,7 @@ int LuaSyncedRead::GetUnitCostTable(lua_State* L) /*** * * @function Spring.GetUnitMetalExtraction - * @param unitID integer + * @param unitID UnitID * @return number? metalExtraction */ int LuaSyncedRead::GetUnitMetalExtraction(lua_State* L) @@ -4342,7 +4366,7 @@ int LuaSyncedRead::GetUnitMetalExtraction(lua_State* L) /*** * * @function Spring.GetUnitExperience - * @param unitID integer + * @param unitID UnitID * @return number xp [0.0; +∞) * @return number limXp [0.0; 1.0) as experience approaches infinity */ @@ -4361,7 +4385,7 @@ int LuaSyncedRead::GetUnitExperience(lua_State* L) /*** * * @function Spring.GetUnitHeight - * @param unitID integer + * @param unitID UnitID * @return number? */ int LuaSyncedRead::GetUnitHeight(lua_State* L) @@ -4378,7 +4402,7 @@ int LuaSyncedRead::GetUnitHeight(lua_State* L) /*** * * @function Spring.GetUnitRadius - * @param unitID integer + * @param unitID UnitID * @return number? */ int LuaSyncedRead::GetUnitRadius(lua_State* L) @@ -4395,7 +4419,7 @@ int LuaSyncedRead::GetUnitRadius(lua_State* L) * * @function Spring.GetUnitBuildeeRadius * Gets the unit's radius for when targeted by build, repair, reclaim-type commands. - * @param unitID integer + * @param unitID UnitID * @return number? */ int LuaSyncedRead::GetUnitBuildeeRadius(lua_State* L) @@ -4411,7 +4435,7 @@ int LuaSyncedRead::GetUnitBuildeeRadius(lua_State* L) /*** * * @function Spring.GetUnitMass - * @param unitID integer + * @param unitID UnitID * @return number? */ int LuaSyncedRead::GetUnitMass(lua_State* L) @@ -4422,7 +4446,7 @@ int LuaSyncedRead::GetUnitMass(lua_State* L) /*** * * @function Spring.GetUnitPosition - * @param unitID integer + * @param unitID UnitID * @param midPos boolean? (Default: `false`) return midpoint as well * @param aimPos boolean? (Default: `false`) return aimpoint as well * @return number? basePointX @@ -4445,7 +4469,7 @@ int LuaSyncedRead::GetUnitPosition(lua_State* L) * @function Spring.GetUnitBasePosition * The same as `Spring.GetUnitPosition`, but without the optional midpoint calculations. * @see Spring.GetUnitPosition - * @param unitID integer + * @param unitID UnitID * @return number? posX * @return number? posY * @return number? posZ @@ -4459,7 +4483,7 @@ int LuaSyncedRead::GetUnitBasePosition(lua_State* L) /*** * * @function Spring.GetUnitVectors - * @param unitID integer + * @param unitID UnitID * @return float3? front * @return float3 up * @return float3 right @@ -4488,7 +4512,7 @@ int LuaSyncedRead::GetUnitVectors(lua_State* L) * * @function Spring.GetUnitRotation * Note: PYR order - * @param unitID integer + * @param unitID UnitID * @return number pitch Rotation in X axis * @return number yaw Rotation in Y axis * @return number roll Rotation in Z axis @@ -4502,7 +4526,7 @@ int LuaSyncedRead::GetUnitRotation(lua_State* L) /*** * * @function Spring.GetUnitDirection - * @param unitID integer + * @param unitID UnitID * @return number frontDirX * @return number frontDirY * @return number frontDirZ @@ -4539,7 +4563,7 @@ int LuaSyncedRead::GetUnitDirection(lua_State* L) /*** * * @function Spring.GetUnitHeading - * @param unitID integer + * @param unitID UnitID * @param convertToRadians boolean? (Default: `false`) * @return number heading */ @@ -4562,7 +4586,7 @@ int LuaSyncedRead::GetUnitHeading(lua_State* L) /*** * * @function Spring.GetUnitVelocity - * @param unitID integer + * @param unitID UnitID */ int LuaSyncedRead::GetUnitVelocity(lua_State* L) { @@ -4573,7 +4597,8 @@ int LuaSyncedRead::GetUnitVelocity(lua_State* L) /*** * * @function Spring.GetUnitBuildFacing - * @param unitID integer + * @param unitID UnitID + * @return FacingInteger? buildFacing facing of footprint, `0` - `3` */ int LuaSyncedRead::GetUnitBuildFacing(lua_State* L) { @@ -4592,8 +4617,8 @@ int LuaSyncedRead::GetUnitBuildFacing(lua_State* L) * * Works for both mobile builders and factories. * - * @param unitID integer - * @return integer buildeeUnitID or nil + * @param unitID UnitID + * @return UnitID? buildeeUnitID */ int LuaSyncedRead::GetUnitIsBuilding(lua_State* L) { @@ -4689,7 +4714,7 @@ static int GetFactoryWorkerTask(lua_State* L, const CFactory *factory) * The possible commands returned are repair, reclaim, resurrect, capture, restore, * and build commands (negative buildee unitDefID). * - * @param unitID integer + * @param unitID UnitID * @return integer cmdID of the relevant command * @return integer targetID if applicable (all except RESTORE) */ @@ -4712,8 +4737,8 @@ int LuaSyncedRead::GetUnitWorkerTask(lua_State* L) * * @function Spring.GetUnitEffectiveBuildRange * Useful for setting move goals manually. - * @param unitID integer - * @param buildeeDefID integer or nil + * @param unitID UnitID + * @param buildeeDefID UnitDefID? * @return number effectiveBuildRange counted to the center of prospective buildee; buildRange if buildee nil */ int LuaSyncedRead::GetUnitEffectiveBuildRange(lua_State* L) @@ -4767,7 +4792,8 @@ int LuaSyncedRead::GetUnitEffectiveBuildRange(lua_State* L) /*** * * @function Spring.GetUnitCurrentBuildPower - * @param unitID integer + * @param unitID UnitID + * @return number? buildPower `nil` if the unit is neither a builder nor a factory. */ int LuaSyncedRead::GetUnitCurrentBuildPower(lua_State* L) { @@ -4803,7 +4829,7 @@ int LuaSyncedRead::GetUnitCurrentBuildPower(lua_State* L) * * Checks resources being carried internally by the unit. * - * @param unitID integer + * @param unitID UnitID * @return number storedMetal * @return number maxStoredMetal * @return number storedEnergy @@ -4825,7 +4851,11 @@ int LuaSyncedRead::GetUnitHarvestStorage(lua_State* L) /*** * * @function Spring.GetUnitBuildParams - * @param unitID integer + * @param unitID UnitID + * @param paramName "buildRange"|"buildDistance"|"buildRange3D" The build param to get + * @return number|boolean|nil value number for `"buildRange"` or `"buildDistance"`, + * boolean for `"buildRange3D"`, otherwise `nil` for unrecognized paramName or not + * allied builder unit. */ int LuaSyncedRead::GetUnitBuildParams(lua_State* L) { @@ -4862,7 +4892,7 @@ int LuaSyncedRead::GetUnitBuildParams(lua_State* L) * Checks if a builder is in build stance, i.e. can create nanoframes. * Returns nil for non-builders. * - * @param unitID integer + * @param unitID UnitID * @return boolean inBuildStance */ int LuaSyncedRead::GetUnitInBuildStance(lua_State* L) @@ -4892,7 +4922,7 @@ int LuaSyncedRead::GetUnitInBuildStance(lua_State* L) * Only works on builders and factories, returns nil (NOT empty table) * for other units. * - * @param unitID integer + * @param unitID UnitID * @return integer[] pieceArray */ int LuaSyncedRead::GetUnitNanoPieces(lua_State* L) @@ -4943,8 +4973,8 @@ int LuaSyncedRead::GetUnitNanoPieces(lua_State* L) * Returns the unit ID of the transport, if any. * Returns nil if the unit is not being transported. * - * @param unitID integer - * @return integer? transportUnitID + * @param unitID UnitID + * @return UnitID? transportUnitID */ int LuaSyncedRead::GetUnitTransporter(lua_State* L) { @@ -4964,8 +4994,8 @@ int LuaSyncedRead::GetUnitTransporter(lua_State* L) * Get units being transported * * @function Spring.GetUnitIsTransporting - * @param unitID integer - * @return integer[]? transporteeArray + * @param unitID UnitID + * @return UnitID[]? transporteeArray * An array of unitIDs being transported by this unit, or `nil` if not a transport. */ int LuaSyncedRead::GetUnitIsTransporting(lua_State* L) @@ -4992,9 +5022,9 @@ int LuaSyncedRead::GetUnitIsTransporting(lua_State* L) /*** * * @function Spring.GetUnitShieldState - * @param unitID integer - * @param weaponNum number? Optional if the unit has just one shield - * @return number isEnabled Warning, number not boolean. 0 or 1 + * @param unitID UnitID + * @param weaponNum integer? Optional if the unit has just one shield + * @return integer isEnabled Warning, number not boolean. 0 or 1 * @return number currentPower */ int LuaSyncedRead::GetUnitShieldState(lua_State* L) @@ -5024,7 +5054,22 @@ int LuaSyncedRead::GetUnitShieldState(lua_State* L) /*** * * @function Spring.GetUnitFlanking - * @param unitID integer + * + * When called without a key, returns every value. When called with one of + * `"mode"`, `"moveFactor"`, `"minDamage"` or `"maxDamage"` returns just that + * single number, and with `"dir"` returns just `dirX`, `dirY`, `dirZ`. + * + * @param unitID UnitID + * @return number mode + * @return number moveFactor + * @return number minDamage + * @return number maxDamage + * @return number dirX + * @return number dirY + * @return number dirZ + * @return number mobility The amount of mobility the unit has collected up to now. + * @overload fun(unitID: integer, param: "mode"|"moveFactor"|"minDamage"|"maxDamage"): number + * @overload fun(unitID: integer, param: "dir"): number, number, number */ int LuaSyncedRead::GetUnitFlanking(lua_State* L) { @@ -5088,7 +5133,7 @@ int LuaSyncedRead::GetUnitFlanking(lua_State* L) * By default this is the highest among the unit's weapon ranges (hence name), * but can be changed dynamically. Also note that unarmed units ignore this. * - * @param unitID integer + * @param unitID UnitID * @return number maxRange */ int LuaSyncedRead::GetUnitMaxRange(lua_State* L) @@ -5136,9 +5181,9 @@ int LuaSyncedRead::GetUnitMaxRange(lua_State* L) * The state "salvoError" is an exception and returns a table: {x, y, z}, * which represents the inaccuracy error of the ongoing burst. * - * @param unitID integer - * @param weaponNum number - * @param stateName string + * @param unitID UnitID + * @param weaponNum integer + * @param stateName string? * @return number stateValue */ int LuaSyncedRead::GetUnitWeaponState(lua_State* L) @@ -5322,7 +5367,7 @@ static inline int PushDamagesKey(lua_State* L, const DynDamageArray& damages, in /*** * * @function Spring.GetUnitWeaponDamages - * @param unitID integer + * @param unitID UnitID */ int LuaSyncedRead::GetUnitWeaponDamages(lua_State* L) { @@ -5361,7 +5406,14 @@ int LuaSyncedRead::GetUnitWeaponDamages(lua_State* L) /*** * * @function Spring.GetUnitWeaponVectors - * @param unitID integer + * @param unitID UnitID + * @param weaponNum integer 1-indexed weapon number + * @return number? posX + * @return number posY + * @return number posZ + * @return number dirX + * @return number dirY + * @return number dirZ */ int LuaSyncedRead::GetUnitWeaponVectors(lua_State* L) { @@ -5401,7 +5453,20 @@ int LuaSyncedRead::GetUnitWeaponVectors(lua_State* L) /*** * * @function Spring.GetUnitWeaponTryTarget - * @param unitID integer + * @param unitID UnitID + * @param weaponNum integer + * @param targetID UnitID + * @return boolean canTarget + */ +/*** + * + * @function Spring.GetUnitWeaponTryTarget + * @param unitID UnitID + * @param weaponNum integer + * @param posX number + * @param posY number + * @param posZ number + * @return boolean canTarget */ int LuaSyncedRead::GetUnitWeaponTryTarget(lua_State* L) { @@ -5446,7 +5511,20 @@ int LuaSyncedRead::GetUnitWeaponTryTarget(lua_State* L) /*** * * @function Spring.GetUnitWeaponTestTarget - * @param unitID integer + * @param unitID UnitID + * @param weaponID integer weapon number (1-based Lua index) + * @param targetUnitID UnitID enemy unit to test (when fewer than five arguments) + * @return boolean validTarget + */ +/*** + * + * @function Spring.GetUnitWeaponTestTarget + * @param unitID UnitID + * @param weaponID integer weapon number (1-based Lua index) + * @param targetX number world X to test (with `targetY`, `targetZ`; used when at least five arguments are passed) + * @param targetY number + * @param targetZ number + * @return boolean validTarget */ int LuaSyncedRead::GetUnitWeaponTestTarget(lua_State* L) { @@ -5484,7 +5562,20 @@ int LuaSyncedRead::GetUnitWeaponTestTarget(lua_State* L) /*** * * @function Spring.GetUnitWeaponTestRange - * @param unitID integer + * @param unitID UnitID + * @param weaponNum integer + * @param targetID UnitID + * @return boolean inRange + */ +/*** + * + * @function Spring.GetUnitWeaponTestRange + * @param unitID UnitID + * @param weaponNum integer + * @param posX number + * @param posY number + * @param posZ number + * @return boolean inRange */ int LuaSyncedRead::GetUnitWeaponTestRange(lua_State* L) { @@ -5522,7 +5613,44 @@ int LuaSyncedRead::GetUnitWeaponTestRange(lua_State* L) /*** * * @function Spring.GetUnitWeaponHaveFreeLineOfFire - * @param unitID integer + * @param unitID UnitID + * @param weaponNum integer + * @param targetID UnitID + * @return boolean haveFreeLineOfFire + */ +/*** + * + * @function Spring.GetUnitWeaponHaveFreeLineOfFire + * @param unitID UnitID + * @param weaponNum integer + * @param srcPosX number + * @param srcPosY number + * @param srcPosZ number + * @return boolean haveFreeLineOfFire + */ +/*** + * + * @function Spring.GetUnitWeaponHaveFreeLineOfFire + * @param unitID UnitID + * @param weaponNum integer + * @param srcPosX number + * @param srcPosY number + * @param srcPosZ number + * @param targetID UnitID + * @return boolean haveFreeLineOfFire + */ +/*** + * + * @function Spring.GetUnitWeaponHaveFreeLineOfFire + * @param unitID UnitID + * @param weaponNum integer + * @param srcPosX number + * @param srcPosY number + * @param srcPosZ number + * @param tgtPosX number + * @param tgtPosY number + * @param tgtPosZ number + * @return boolean haveFreeLineOfFire */ int LuaSyncedRead::GetUnitWeaponHaveFreeLineOfFire(lua_State* L) { @@ -5588,7 +5716,12 @@ int LuaSyncedRead::GetUnitWeaponHaveFreeLineOfFire(lua_State* L) /*** * * @function Spring.GetUnitWeaponCanFire - * @param unitID integer + * @param unitID UnitID + * @param weaponNum integer + * @param ignoreAngleGood boolean? + * @param ignoreTargetType boolean? + * @param ignoreRequestedDir boolean? + * @return boolean canFire */ int LuaSyncedRead::GetUnitWeaponCanFire(lua_State* L) { @@ -5625,7 +5758,7 @@ int LuaSyncedRead::GetUnitWeaponCanFire(lua_State* L) * that weapons can aim individually unless slaved. * * @function Spring.GetUnitWeaponTarget - * @param unitID integer + * @param unitID UnitID * @param weaponNum integer * @return 0 TargetType none * @return boolean isUserTarget @@ -5637,11 +5770,11 @@ int LuaSyncedRead::GetUnitWeaponCanFire(lua_State* L) * that weapons can aim individually unless slaved. * * @function Spring.GetUnitWeaponTarget - * @param unitID integer + * @param unitID UnitID * @param weaponNum integer * @return 1 TargetType unit * @return boolean isUserTarget - * @return integer targetUnitID + * @return UnitID targetUnitID */ /*** * Checks a weapon's target @@ -5650,7 +5783,7 @@ int LuaSyncedRead::GetUnitWeaponCanFire(lua_State* L) * that weapons can aim individually unless slaved. * * @function Spring.GetUnitWeaponTarget - * @param unitID integer + * @param unitID UnitID * @param weaponNum integer * @return 2 TargetType position * @return boolean isUserTarget @@ -5663,11 +5796,11 @@ int LuaSyncedRead::GetUnitWeaponCanFire(lua_State* L) * that weapons can aim individually unless slaved. * * @function Spring.GetUnitWeaponTarget - * @param unitID integer + * @param unitID UnitID * @param weaponNum integer * @return 3 TargetType projectileID * @return boolean isUserTarget - * @return integer targetProjectileId + * @return ProjectileID targetProjectileId */ int LuaSyncedRead::GetUnitWeaponTarget(lua_State* L) { @@ -5728,7 +5861,7 @@ int LuaSyncedRead::GetUnitFuel(lua_State* L) { lua_pushnumber(L, 0.0f); return 1 /*** * * @function Spring.GetUnitEstimatedPath - * @param unitID integer + * @param unitID UnitID */ int LuaSyncedRead::GetUnitEstimatedPath(lua_State* L) { @@ -5748,7 +5881,8 @@ int LuaSyncedRead::GetUnitEstimatedPath(lua_State* L) /*** * * @function Spring.GetUnitLastAttacker - * @param unitID integer + * @param unitID UnitID + * @return UnitID? attackerUnitID `nil` if the unit has no last attacker or the attacker is not visible. */ int LuaSyncedRead::GetUnitLastAttacker(lua_State* L) { @@ -5768,7 +5902,7 @@ int LuaSyncedRead::GetUnitLastAttacker(lua_State* L) /*** * * @function Spring.GetUnitLastAttackedPiece - * @param unitID integer + * @param unitID UnitID */ int LuaSyncedRead::GetUnitLastAttackedPiece(lua_State* L) { @@ -5778,7 +5912,7 @@ int LuaSyncedRead::GetUnitLastAttackedPiece(lua_State* L) /*** * * @function Spring.GetUnitCollisionVolumeData - * @param unitID integer + * @param unitID UnitID */ int LuaSyncedRead::GetUnitCollisionVolumeData(lua_State* L) { @@ -5790,6 +5924,25 @@ int LuaSyncedRead::GetUnitCollisionVolumeData(lua_State* L) return LuaUtils::PushColVolData(L, &unit->collisionVolume); } +/*** + * + * @function Spring.GetUnitPieceCollisionVolumeData + * @param unitID UnitID + * @param pieceIndex integer 1-based local-model piece index + * @return number? scaleX + * @return number? scaleY + * @return number? scaleZ + * @return number? offsetX + * @return number? offsetY + * @return number? offsetZ + * @return integer? volumeType + * @return integer? testType + * @return integer? primaryAxis + * @return boolean? disabled + * + * Returns no values when `unitID` is invalid or not in line of sight, or when + * `pieceIndex` is invalid. + */ int LuaSyncedRead::GetUnitPieceCollisionVolumeData(lua_State* L) { return (PushPieceCollisionVolumeData(L, ParseInLosUnit(L, __func__, 1))); @@ -5799,8 +5952,8 @@ int LuaSyncedRead::GetUnitPieceCollisionVolumeData(lua_State* L) /*** * * @function Spring.GetUnitSeparation - * @param unitID1 number - * @param unitID2 number + * @param unitID1 UnitID + * @param unitID2 UnitID * @param direction boolean? (Default: `false`) to subtract from, default unitID1 - unitID2 * @param subtractRadii boolean? (Default: `false`) whether units radii should be subtracted from the total * @return number? @@ -5841,7 +5994,10 @@ int LuaSyncedRead::GetUnitSeparation(lua_State* L) /*** * * @function Spring.GetUnitFeatureSeparation - * @param unitID integer + * @param unitID UnitID + * @param featureID FeatureID + * @param flat boolean? (Default: `false`) if true, XZ (2D) distance; otherwise 3D distance + * @return number distance */ int LuaSyncedRead::GetUnitFeatureSeparation(lua_State* L) { @@ -5891,7 +6047,7 @@ int LuaSyncedRead::GetUnitFeatureSeparation(lua_State* L) /*** * * @function Spring.GetUnitDefDimensions - * @param unitDefID integer + * @param unitDefID UnitDefID * @return UnitDefDimensions? dimensions */ int LuaSyncedRead::GetUnitDefDimensions(lua_State* L) @@ -5908,17 +6064,17 @@ int LuaSyncedRead::GetUnitDefDimensions(lua_State* L) const S3DModel& m = *model; const float3& mid = model->relMidPos; lua_createtable(L, 0, 11); - HSTR_PUSH_NUMBER(L, "height", m.height); - HSTR_PUSH_NUMBER(L, "radius", m.radius); - HSTR_PUSH_NUMBER(L, "midx", mid.x); - HSTR_PUSH_NUMBER(L, "minx", m.mins.x); - HSTR_PUSH_NUMBER(L, "maxx", m.maxs.x); - HSTR_PUSH_NUMBER(L, "midy", mid.y); - HSTR_PUSH_NUMBER(L, "miny", m.mins.y); - HSTR_PUSH_NUMBER(L, "maxy", m.maxs.y); - HSTR_PUSH_NUMBER(L, "midz", mid.z); - HSTR_PUSH_NUMBER(L, "minz", m.mins.z); - HSTR_PUSH_NUMBER(L, "maxz", m.maxs.z); + LuaPushNamedNumber(L, "height", m.height); + LuaPushNamedNumber(L, "radius", m.radius); + LuaPushNamedNumber(L, "midx", mid.x); + LuaPushNamedNumber(L, "minx", m.mins.x); + LuaPushNamedNumber(L, "maxx", m.maxs.x); + LuaPushNamedNumber(L, "midy", mid.y); + LuaPushNamedNumber(L, "miny", m.mins.y); + LuaPushNamedNumber(L, "maxy", m.maxs.y); + LuaPushNamedNumber(L, "midz", mid.z); + LuaPushNamedNumber(L, "minz", m.mins.z); + LuaPushNamedNumber(L, "maxz", m.maxs.z); return 1; } @@ -5926,6 +6082,7 @@ int LuaSyncedRead::GetUnitDefDimensions(lua_State* L) /*** * * @function Spring.GetCEGID + * @return integer cegID */ int LuaSyncedRead::GetCEGID(lua_State* L) { @@ -5937,7 +6094,7 @@ int LuaSyncedRead::GetCEGID(lua_State* L) /*** * * @function Spring.GetUnitBlocking - * @param unitID integer + * @param unitID UnitID * @return boolean? isBlocking * @return boolean isSolidObjectCollidable * @return boolean isProjectileCollidable @@ -5955,7 +6112,7 @@ int LuaSyncedRead::GetUnitBlocking(lua_State* L) /*** * * @function Spring.GetUnitMoveTypeData - * @param unitID integer + * @param unitID UnitID */ int LuaSyncedRead::GetUnitMoveTypeData(lua_State* L) { @@ -5966,49 +6123,49 @@ int LuaSyncedRead::GetUnitMoveTypeData(lua_State* L) AMoveType* amt = unit->moveType; lua_createtable(L, 0, 26); - HSTR_PUSH_NUMBER(L, "maxSpeed", amt->GetMaxSpeed() * GAME_SPEED); - HSTR_PUSH_NUMBER(L, "maxWantedSpeed", amt->GetMaxWantedSpeed() * GAME_SPEED); - HSTR_PUSH_NUMBER(L, "goalx", amt->goalPos.x); - HSTR_PUSH_NUMBER(L, "goaly", amt->goalPos.y); - HSTR_PUSH_NUMBER(L, "goalz", amt->goalPos.z); + LuaPushNamedNumber(L, "maxSpeed", amt->GetMaxSpeed() * GAME_SPEED); + LuaPushNamedNumber(L, "maxWantedSpeed", amt->GetMaxWantedSpeed() * GAME_SPEED); + LuaPushNamedNumber(L, "goalx", amt->goalPos.x); + LuaPushNamedNumber(L, "goaly", amt->goalPos.y); + LuaPushNamedNumber(L, "goalz", amt->goalPos.z); switch (amt->progressState) { case AMoveType::Done: - HSTR_PUSH_CSTRING(L, "progressState", "done"); + LuaPushNamedString(L, "progressState", "done"); break; case AMoveType::Active: - HSTR_PUSH_CSTRING(L, "progressState", "active"); + LuaPushNamedString(L, "progressState", "active"); break; case AMoveType::Failed: - HSTR_PUSH_CSTRING(L, "progressState", "failed"); + LuaPushNamedString(L, "progressState", "failed"); break; } const CGroundMoveType* groundmt = dynamic_cast(unit->moveType); if (groundmt != nullptr) { - HSTR_PUSH_CSTRING(L, "name", "ground"); + LuaPushNamedString(L, "name", "ground"); - HSTR_PUSH_NUMBER(L, "turnRate", groundmt->GetTurnRate()); - HSTR_PUSH_NUMBER(L, "accRate", groundmt->GetAccRate()); - HSTR_PUSH_NUMBER(L, "decRate", groundmt->GetDecRate()); + LuaPushNamedNumber(L, "turnRate", groundmt->GetTurnRate()); + LuaPushNamedNumber(L, "accRate", groundmt->GetAccRate()); + LuaPushNamedNumber(L, "decRate", groundmt->GetDecRate()); - HSTR_PUSH_NUMBER(L, "maxReverseSpeed", groundmt->GetMaxReverseSpeed() * GAME_SPEED); - HSTR_PUSH_NUMBER(L, "wantedSpeed", groundmt->GetWantedSpeed() * GAME_SPEED); - HSTR_PUSH_NUMBER(L, "currentSpeed", groundmt->GetCurrentSpeed() * GAME_SPEED); + LuaPushNamedNumber(L, "maxReverseSpeed", groundmt->GetMaxReverseSpeed() * GAME_SPEED); + LuaPushNamedNumber(L, "wantedSpeed", groundmt->GetWantedSpeed() * GAME_SPEED); + LuaPushNamedNumber(L, "currentSpeed", groundmt->GetCurrentSpeed() * GAME_SPEED); - HSTR_PUSH_NUMBER(L, "goalRadius", groundmt->GetGoalRadius()); + LuaPushNamedNumber(L, "goalRadius", groundmt->GetGoalRadius()); - HSTR_PUSH_NUMBER(L, "currwaypointx", (groundmt->GetCurrWayPoint()).x); - HSTR_PUSH_NUMBER(L, "currwaypointy", (groundmt->GetCurrWayPoint()).y); - HSTR_PUSH_NUMBER(L, "currwaypointz", (groundmt->GetCurrWayPoint()).z); - HSTR_PUSH_NUMBER(L, "nextwaypointx", (groundmt->GetNextWayPoint()).x); - HSTR_PUSH_NUMBER(L, "nextwaypointy", (groundmt->GetNextWayPoint()).y); - HSTR_PUSH_NUMBER(L, "nextwaypointz", (groundmt->GetNextWayPoint()).z); + LuaPushNamedNumber(L, "currwaypointx", (groundmt->GetCurrWayPoint()).x); + LuaPushNamedNumber(L, "currwaypointy", (groundmt->GetCurrWayPoint()).y); + LuaPushNamedNumber(L, "currwaypointz", (groundmt->GetCurrWayPoint()).z); + LuaPushNamedNumber(L, "nextwaypointx", (groundmt->GetNextWayPoint()).x); + LuaPushNamedNumber(L, "nextwaypointy", (groundmt->GetNextWayPoint()).y); + LuaPushNamedNumber(L, "nextwaypointz", (groundmt->GetNextWayPoint()).z); - HSTR_PUSH_NUMBER(L, "requestedSpeed", 0.0f); + LuaPushNamedNumber(L, "requestedSpeed", 0.0f); - HSTR_PUSH_NUMBER(L, "pathFailures", 0); + LuaPushNamedNumber(L, "pathFailures", 0); return 1; } @@ -6016,62 +6173,62 @@ int LuaSyncedRead::GetUnitMoveTypeData(lua_State* L) const CHoverAirMoveType* hAMT = dynamic_cast(unit->moveType); if (hAMT != nullptr) { - HSTR_PUSH_CSTRING(L, "name", "gunship"); + LuaPushNamedString(L, "name", "gunship"); - HSTR_PUSH_NUMBER(L, "wantedHeight", hAMT->wantedHeight); - HSTR_PUSH_BOOL(L, "collide", hAMT->collide); - HSTR_PUSH_BOOL(L, "useSmoothMesh", hAMT->useSmoothMesh); + LuaPushNamedNumber(L, "wantedHeight", hAMT->wantedHeight); + LuaPushNamedBool(L, "collide", hAMT->collide); + LuaPushNamedBool(L, "useSmoothMesh", hAMT->useSmoothMesh); switch (hAMT->aircraftState) { case AAirMoveType::AIRCRAFT_LANDED: - HSTR_PUSH_CSTRING(L, "aircraftState", "landed"); + LuaPushNamedString(L, "aircraftState", "landed"); break; case AAirMoveType::AIRCRAFT_FLYING: - HSTR_PUSH_CSTRING(L, "aircraftState", "flying"); + LuaPushNamedString(L, "aircraftState", "flying"); break; case AAirMoveType::AIRCRAFT_LANDING: - HSTR_PUSH_CSTRING(L, "aircraftState", "landing"); + LuaPushNamedString(L, "aircraftState", "landing"); break; case AAirMoveType::AIRCRAFT_CRASHING: - HSTR_PUSH_CSTRING(L, "aircraftState", "crashing"); + LuaPushNamedString(L, "aircraftState", "crashing"); break; case AAirMoveType::AIRCRAFT_TAKEOFF: - HSTR_PUSH_CSTRING(L, "aircraftState", "takeoff"); + LuaPushNamedString(L, "aircraftState", "takeoff"); break; case AAirMoveType::AIRCRAFT_HOVERING: - HSTR_PUSH_CSTRING(L, "aircraftState", "hovering"); + LuaPushNamedString(L, "aircraftState", "hovering"); break; }; switch (hAMT->flyState) { case CHoverAirMoveType::FLY_CRUISING: - HSTR_PUSH_CSTRING(L, "flyState", "cruising"); + LuaPushNamedString(L, "flyState", "cruising"); break; case CHoverAirMoveType::FLY_CIRCLING: - HSTR_PUSH_CSTRING(L, "flyState", "circling"); + LuaPushNamedString(L, "flyState", "circling"); break; case CHoverAirMoveType::FLY_ATTACKING: - HSTR_PUSH_CSTRING(L, "flyState", "attacking"); + LuaPushNamedString(L, "flyState", "attacking"); break; case CHoverAirMoveType::FLY_LANDING: - HSTR_PUSH_CSTRING(L, "flyState", "landing"); + LuaPushNamedString(L, "flyState", "landing"); break; } - HSTR_PUSH_NUMBER(L, "goalDistance", hAMT->goalDistance); + LuaPushNamedNumber(L, "goalDistance", hAMT->goalDistance); - HSTR_PUSH_BOOL(L, "bankingAllowed", hAMT->bankingAllowed); - HSTR_PUSH_NUMBER(L, "currentBank", hAMT->currentBank); - HSTR_PUSH_NUMBER(L, "currentPitch", hAMT->currentPitch); + LuaPushNamedBool (L, "bankingAllowed", hAMT->bankingAllowed); + LuaPushNamedNumber(L, "currentBank", hAMT->currentBank); + LuaPushNamedNumber(L, "currentPitch", hAMT->currentPitch); - HSTR_PUSH_NUMBER(L, "turnRate", hAMT->turnRate); - HSTR_PUSH_NUMBER(L, "accRate", hAMT->accRate); - HSTR_PUSH_NUMBER(L, "decRate", hAMT->decRate); - HSTR_PUSH_NUMBER(L, "altitudeRate", hAMT->altitudeRate); + LuaPushNamedNumber(L, "turnRate", hAMT->turnRate); + LuaPushNamedNumber(L, "accRate", hAMT->accRate); + LuaPushNamedNumber(L, "decRate", hAMT->decRate); + LuaPushNamedNumber(L, "altitudeRate", hAMT->altitudeRate); - HSTR_PUSH_NUMBER(L, "brakeDistance", -1.0f); // DEPRECATED - HSTR_PUSH_BOOL(L, "dontLand", hAMT->GetAllowLanding()); - HSTR_PUSH_NUMBER(L, "maxDrift", hAMT->maxDrift); + LuaPushNamedNumber(L, "brakeDistance", -1.0f); // DEPRECATED + LuaPushNamedBool (L, "dontLand", hAMT->GetAllowLanding()); + LuaPushNamedNumber(L, "maxDrift", hAMT->maxDrift); return 1; } @@ -6079,42 +6236,42 @@ int LuaSyncedRead::GetUnitMoveTypeData(lua_State* L) const CStrafeAirMoveType* sAMT = dynamic_cast(unit->moveType); if (sAMT != nullptr) { - HSTR_PUSH_CSTRING(L, "name", "airplane"); + LuaPushNamedString(L, "name", "airplane"); switch (sAMT->aircraftState) { case AAirMoveType::AIRCRAFT_LANDED: - HSTR_PUSH_CSTRING(L, "aircraftState", "landed"); + LuaPushNamedString(L, "aircraftState", "landed"); break; case AAirMoveType::AIRCRAFT_FLYING: - HSTR_PUSH_CSTRING(L, "aircraftState", "flying"); + LuaPushNamedString(L, "aircraftState", "flying"); break; case AAirMoveType::AIRCRAFT_LANDING: - HSTR_PUSH_CSTRING(L, "aircraftState", "landing"); + LuaPushNamedString(L, "aircraftState", "landing"); break; case AAirMoveType::AIRCRAFT_CRASHING: - HSTR_PUSH_CSTRING(L, "aircraftState", "crashing"); + LuaPushNamedString(L, "aircraftState", "crashing"); break; case AAirMoveType::AIRCRAFT_TAKEOFF: - HSTR_PUSH_CSTRING(L, "aircraftState", "takeoff"); + LuaPushNamedString(L, "aircraftState", "takeoff"); break; case AAirMoveType::AIRCRAFT_HOVERING: - HSTR_PUSH_CSTRING(L, "aircraftState", "hovering"); + LuaPushNamedString(L, "aircraftState", "hovering"); break; }; - HSTR_PUSH_NUMBER(L, "wantedHeight", sAMT->wantedHeight); - HSTR_PUSH_BOOL(L, "collide", sAMT->collide); - HSTR_PUSH_BOOL(L, "useSmoothMesh", sAMT->useSmoothMesh); + LuaPushNamedNumber(L, "wantedHeight", sAMT->wantedHeight); + LuaPushNamedBool (L, "collide", sAMT->collide); + LuaPushNamedBool (L, "useSmoothMesh", sAMT->useSmoothMesh); - HSTR_PUSH_NUMBER(L, "myGravity", sAMT->myGravity); + LuaPushNamedNumber(L, "myGravity", sAMT->myGravity); - HSTR_PUSH_NUMBER(L, "maxBank", sAMT->maxBank); - HSTR_PUSH_NUMBER(L, "maxPitch", sAMT->maxBank); - HSTR_PUSH_NUMBER(L, "turnRadius", sAMT->turnRadius); + LuaPushNamedNumber(L, "maxBank", sAMT->maxBank); + LuaPushNamedNumber(L, "maxPitch", sAMT->maxBank); + LuaPushNamedNumber(L, "turnRadius", sAMT->turnRadius); - HSTR_PUSH_NUMBER(L, "maxAcc", sAMT->accRate); - HSTR_PUSH_NUMBER(L, "maxAileron", sAMT->maxAileron); - HSTR_PUSH_NUMBER(L, "maxElevator", sAMT->maxElevator); - HSTR_PUSH_NUMBER(L, "maxRudder", sAMT->maxRudder); + LuaPushNamedNumber(L, "maxAcc", sAMT->accRate); + LuaPushNamedNumber(L, "maxAileron", sAMT->maxAileron); + LuaPushNamedNumber(L, "maxElevator", sAMT->maxElevator); + LuaPushNamedNumber(L, "maxRudder", sAMT->maxRudder); return 1; } @@ -6122,18 +6279,18 @@ int LuaSyncedRead::GetUnitMoveTypeData(lua_State* L) const CStaticMoveType* staticmt = dynamic_cast(unit->moveType); if (staticmt != nullptr) { - HSTR_PUSH_CSTRING(L, "name", "static"); + LuaPushNamedString(L, "name", "static"); return 1; } const CScriptMoveType* scriptmt = dynamic_cast(unit->moveType); if (scriptmt != nullptr) { - HSTR_PUSH_CSTRING(L, "name", "script"); + LuaPushNamedString(L, "name", "script"); return 1; } - HSTR_PUSH_CSTRING(L, "name", "unknown"); + LuaPushNamedString(L, "name", "unknown"); return 1; } @@ -6152,14 +6309,14 @@ static void PackCommand(lua_State* L, const Command& cmd) { lua_createtable(L, 0, 4); - HSTR_PUSH_NUMBER(L, "id", cmd.GetID()); + LuaPushNamedNumber(L, "id", cmd.GetID()); // t["params"] = {[1] = param1, ...} LuaUtils::PushCommandParamsTable(L, cmd, true); // t["options"] = {key1 = val1, ...} LuaUtils::PushCommandOptionsTable(L, cmd, true); - HSTR_PUSH_NUMBER(L, "tag", cmd.GetTag()); + LuaPushNamedNumber(L, "tag", cmd.GetTag()); } @@ -6193,7 +6350,7 @@ static void PackCommandQueue(lua_State* L, const CCommandQueue& commands, size_t * * @function Spring.GetUnitCurrentCommand * - * @param unitID integer unitID when invalid this function returns nil. + * @param unitID UnitID unitID when invalid this function returns nil. * @param cmdIndex integer? (Default: `0`) Command index to get. If negative will count from the end of the queue, e.g. -1 will be the last command. * @return CMD? cmdID * @return integer|CommandOptionBit|nil options @@ -6244,7 +6401,7 @@ int LuaSyncedRead::GetUnitCurrentCommand(lua_State* L) * * Same as `Spring.GetCommandQueue` * - * @param unitID integer + * @param unitID UnitID * @param count integer Maximum amount of commands to return, `-1` returns all commands. * @return Command[] commands */ @@ -6254,9 +6411,9 @@ int LuaSyncedRead::GetUnitCurrentCommand(lua_State* L) * @deprecated This overload is deprecated, use `Spring.GetUnitCommandCount(unitId)` instead. * @function Spring.GetUnitCommands * - * @param unitID integer + * @param unitID UnitID * @param count 0 Returns the number of commands in the units queue. - * @return integer The number of commands in the unit queue. + * @return integer cmdCount The number of commands in the unit queue. */ int LuaSyncedRead::GetUnitCommands(lua_State* L) { @@ -6289,7 +6446,7 @@ int LuaSyncedRead::GetUnitCommands(lua_State* L) * * @function Spring.GetFactoryCommands * - * @param unitID integer + * @param unitID UnitID * @param count integer Maximum amount of commands to return, `-1` returns all commands. * @return Command[] commands * @@ -6302,9 +6459,9 @@ int LuaSyncedRead::GetUnitCommands(lua_State* L) * @deprecated This overload is deprecated, use `Spring.GetFactoryCommandCount(unitId)` instead. * @function Spring.GetFactoryCommands * - * @param unitID integer + * @param unitID UnitID * @param count 0 Returns the number of commands in the factory queue. - * @return integer The number of commands in the factory queue. + * @return integer cmdCount The number of commands in the factory queue. * * @see Spring.GetFactoryCommandCount for replacement function. */ @@ -6340,8 +6497,8 @@ int LuaSyncedRead::GetFactoryCommands(lua_State* L) /*** Get the number of commands in a unit's queue. * * @function Spring.GetUnitCommandCount - * @param unitID integer - * @return integer The number of commands in the unit's queue. + * @param unitID UnitID + * @return integer cmdCount The number of commands in the unit's queue. */ int LuaSyncedRead::GetUnitCommandCount(lua_State* L) { @@ -6363,8 +6520,8 @@ int LuaSyncedRead::GetUnitCommandCount(lua_State* L) /*** Get the number of commands in a factory queue. * * @function Spring.GetFactoryCommandCount - * @param unitID integer - * @return integer The number of commands in the factory queue. + * @param unitID UnitID + * @return integer cmdCount The number of commands in the factory queue. * * @see Spring.GetFactoryCommands to get the factory commands. * @see Spring.GetFactoryCounts to get command counts grouped by cmdID. @@ -6393,7 +6550,13 @@ int LuaSyncedRead::GetFactoryCommandCount(lua_State* L) /*** * * @function Spring.GetFactoryBuggerOff - * @param unitID integer + * @param unitID UnitID + * @return boolean? boPerform `nil` if the unit does not exist or is not a factory. + * @return number boOffset + * @return number boRadius + * @return number boRelHeading + * @return boolean boSherical + * @return boolean boForced */ int LuaSyncedRead::GetFactoryBuggerOff(lua_State* L) { @@ -6473,7 +6636,7 @@ static void PackFactoryCounts(lua_State* L, /*** Gets the build queue of a factory * * @function Spring.GetFactoryCounts - * @param unitID integer + * @param unitID UnitID * @param count integer? (Default: `-1`) Number of commands to retrieve, `-1` for all. * @param addCmds boolean? (Default: `false`) Retrieve commands other than buildunit * @@ -6514,7 +6677,7 @@ int LuaSyncedRead::GetFactoryCounts(lua_State* L) * * Same as `Spring.GetUnitCommands` * - * @param unitID integer + * @param unitID UnitID * @param count integer Number of commands to return, `-1` returns all commands, `0` returns command count. * @return Command[] commands */ @@ -6526,7 +6689,7 @@ int LuaSyncedRead::GetFactoryCounts(lua_State* L) * * Same as `Spring.GetUnitCommands` * - * @param unitID integer + * @param unitID UnitID * @param count 0 Returns the number of commands in the units queue. * @return integer cmdCount The number of commands in the unit queue. * @@ -6614,8 +6777,8 @@ static int PackBuildQueue(lua_State* L, bool canBuild, const char* caller) /*** Returns the build queue * * @function Spring.GetFullBuildQueue - * @param unitID integer - * @return table? buildqueue indexed by unitDefID with count values + * @param unitID UnitID + * @return table? buildqueue indexed by unitDefID with count values */ int LuaSyncedRead::GetFullBuildQueue(lua_State* L) { @@ -6626,8 +6789,8 @@ int LuaSyncedRead::GetFullBuildQueue(lua_State* L) /*** Returns the build queue cleaned of things the unit can't build itself * * @function Spring.GetRealBuildQueue - * @param unitID integer - * @return table? buildqueue indexed by unitDefID with count values + * @param unitID UnitID + * @return table? buildqueue indexed by unitDefID with count values */ int LuaSyncedRead::GetRealBuildQueue(lua_State* L) { @@ -6641,7 +6804,7 @@ int LuaSyncedRead::GetRealBuildQueue(lua_State* L) /*** * * @function Spring.GetUnitCmdDescs - * @param unitID integer + * @param unitID UnitID */ int LuaSyncedRead::GetUnitCmdDescs(lua_State* L) { @@ -6680,7 +6843,7 @@ int LuaSyncedRead::GetUnitCmdDescs(lua_State* L) /*** * * @function Spring.FindUnitCmdDesc - * @param unitID integer + * @param unitID UnitID * @param cmdID integer * @return integer? */ @@ -6709,7 +6872,7 @@ int LuaSyncedRead::FindUnitCmdDesc(lua_State* L) /*** * * @function Spring.ValidFeatureID - * @param featureID integer + * @param featureID FeatureID * @return boolean */ int LuaSyncedRead::ValidFeatureID(lua_State* L) @@ -6721,7 +6884,7 @@ int LuaSyncedRead::ValidFeatureID(lua_State* L) /*** * @function Spring.GetAllFeatures - * @return integer[] featureIDs + * @return FeatureID[] featureIDs */ int LuaSyncedRead::GetAllFeatures(lua_State* L) { @@ -6750,8 +6913,8 @@ int LuaSyncedRead::GetAllFeatures(lua_State* L) /*** * * @function Spring.GetFeatureDefID - * @param featureID integer - * @return number? + * @param featureID FeatureID + * @return FeatureDefID? */ int LuaSyncedRead::GetFeatureDefID(lua_State* L) { @@ -6767,8 +6930,8 @@ int LuaSyncedRead::GetFeatureDefID(lua_State* L) /*** * * @function Spring.GetFeatureTeam - * @param featureID integer - * @return number? + * @param featureID FeatureID + * @return TeamID? */ int LuaSyncedRead::GetFeatureTeam(lua_State* L) { @@ -6788,8 +6951,8 @@ int LuaSyncedRead::GetFeatureTeam(lua_State* L) /*** * * @function Spring.GetFeatureAllyTeam - * @param featureID integer - * @return number? + * @param featureID FeatureID + * @return AllyTeamID? */ int LuaSyncedRead::GetFeatureAllyTeam(lua_State* L) { @@ -6805,7 +6968,7 @@ int LuaSyncedRead::GetFeatureAllyTeam(lua_State* L) /*** * * @function Spring.GetFeatureHealth - * @param featureID integer + * @param featureID FeatureID * @return number? health * @return number defHealth * @return number resurrectProgress @@ -6826,7 +6989,7 @@ int LuaSyncedRead::GetFeatureHealth(lua_State* L) /*** * * @function Spring.GetFeatureHeight - * @param featureID integer + * @param featureID FeatureID * @return number? */ int LuaSyncedRead::GetFeatureHeight(lua_State* L) @@ -6843,7 +7006,7 @@ int LuaSyncedRead::GetFeatureHeight(lua_State* L) /*** * * @function Spring.GetFeatureRadius - * @param featureID integer + * @param featureID FeatureID * @return number? */ int LuaSyncedRead::GetFeatureRadius(lua_State* L) @@ -6859,7 +7022,7 @@ int LuaSyncedRead::GetFeatureRadius(lua_State* L) /*** * * @function Spring.GetFeatureMass - * @param featureID integer + * @param featureID FeatureID * @return number? */ int LuaSyncedRead::GetFeatureMass(lua_State* L) @@ -6870,7 +7033,7 @@ int LuaSyncedRead::GetFeatureMass(lua_State* L) /*** * * @function Spring.GetFeaturePosition - * @param featureID integer + * @param featureID FeatureID * @return number? x * @return number? y * @return number? z @@ -6884,8 +7047,8 @@ int LuaSyncedRead::GetFeaturePosition(lua_State* L) /*** * * @function Spring.GetFeatureSeparation - * @param featureID1 number - * @param featureID2 number + * @param featureID1 FeatureID + * @param featureID2 FeatureID * @param direction boolean? (Default: `false`) to subtract from, default featureID1 - featureID2 * @return number? */ @@ -6917,7 +7080,7 @@ int LuaSyncedRead::GetFeatureSeparation(lua_State* L) * * @function Spring.GetFeatureRotation * Note: PYR order - * @param featureID integer + * @param featureID FeatureID * @return number? pitch Rotation in X axis * @return number? yaw Rotation in Y axis * @return number? roll Rotation in Z axis @@ -6934,7 +7097,7 @@ int LuaSyncedRead::GetFeatureRotation(lua_State* L) /*** * * @function Spring.GetFeatureDirection - * @param featureID integer + * @param featureID FeatureID * @return number? frontDirX * @return number? frontDirY * @return number? frontDirZ @@ -6976,7 +7139,7 @@ int LuaSyncedRead::GetFeatureDirection(lua_State* L) * * @function Spring.GetFeatureVelocity * Returns nil if no feature found with ID. - * @param featureID integer + * @param featureID FeatureID * @return number? x * @return number? y * @return number? z @@ -6991,7 +7154,8 @@ int LuaSyncedRead::GetFeatureVelocity(lua_State* L) /*** * * @function Spring.GetFeatureHeading - * @param featureID integer + * @param featureID FeatureID + * @return number? heading */ int LuaSyncedRead::GetFeatureHeading(lua_State* L) { @@ -7007,7 +7171,7 @@ int LuaSyncedRead::GetFeatureHeading(lua_State* L) /*** * * @function Spring.GetFeatureResources - * @param featureID integer + * @param featureID FeatureID * @return number? metal * @return number defMetal * @return number energy @@ -7034,7 +7198,7 @@ int LuaSyncedRead::GetFeatureResources(lua_State* L) /*** * * @function Spring.GetFeatureBlocking - * @param featureID integer + * @param featureID FeatureID * @return boolean? isBlocking * @return boolean? isSolidObjectCollidable * @return boolean? isProjectileCollidable @@ -7052,7 +7216,7 @@ int LuaSyncedRead::GetFeatureBlocking(lua_State* L) /*** * * @function Spring.GetFeatureNoSelect - * @param featureID integer + * @param featureID FeatureID * @return boolean? */ int LuaSyncedRead::GetFeatureNoSelect(lua_State* L) @@ -7071,7 +7235,7 @@ int LuaSyncedRead::GetFeatureNoSelect(lua_State* L) * * @function Spring.GetFeatureResurrect * Returns nil if no feature found with ID. - * @param featureID integer + * @param featureID FeatureID * @return string|""|nil featureDefName * @return FacingInteger buildFacing facing of footprint, 0 - 3 */ @@ -7096,9 +7260,9 @@ int LuaSyncedRead::GetFeatureResurrect(lua_State* L) /*** * * @function Spring.GetFeatureLastAttackedPiece - * @param featureID integer - * @return string|""|nil Last hit piece name - * @return integer? frame it was last hit on, nil when featureID is not valid + * @param featureID FeatureID + * @return string|""|nil pieceName Last hit piece name + * @return integer? frame frame it was last hit on, `nil` when featureID is not valid */ int LuaSyncedRead::GetFeatureLastAttackedPiece(lua_State* L) { @@ -7126,7 +7290,7 @@ int LuaSyncedRead::GetFeatureLastAttackedPiece(lua_State* L) /*** * * @function Spring.GetFeatureCollisionVolumeData - * @param featureID integer + * @param featureID FeatureID * @return CollisionVolumeData? */ int LuaSyncedRead::GetFeatureCollisionVolumeData(lua_State* L) @@ -7142,7 +7306,7 @@ int LuaSyncedRead::GetFeatureCollisionVolumeData(lua_State* L) /*** * * @function Spring.GetFeaturePieceCollisionVolumeData - * @param featureID integer + * @param featureID FeatureID * @return CollisionVolumeData? */ int LuaSyncedRead::GetFeaturePieceCollisionVolumeData(lua_State* L) @@ -7155,7 +7319,7 @@ int LuaSyncedRead::GetFeaturePieceCollisionVolumeData(lua_State* L) * * @function Spring.GetFeatureFireTime * - * @param featureID integer + * @param featureID FeatureID * @return number? fireTime in seconds, nil when featureID is invalid. */ int LuaSyncedRead::GetFeatureFireTime(lua_State* L) @@ -7174,7 +7338,7 @@ int LuaSyncedRead::GetFeatureFireTime(lua_State* L) * * @function Spring.GetFeatureSmokeTime * - * @param featureID integer + * @param featureID FeatureID * @return number? smokeTime in seconds, nil when featureID is invalid. */ int LuaSyncedRead::GetFeatureSmokeTime(lua_State* L) @@ -7199,7 +7363,7 @@ int LuaSyncedRead::GetFeatureSmokeTime(lua_State* L) /*** * * @function Spring.GetProjectilePosition - * @param projectileID integer + * @param projectileID ProjectileID * @return number? posX * @return number? posY * @return number? posZ @@ -7220,7 +7384,7 @@ int LuaSyncedRead::GetProjectilePosition(lua_State* L) /*** * * @function Spring.GetProjectileDirection - * @param projectileID integer + * @param projectileID ProjectileID * @return number? dirX * @return number? dirY * @return number? dirZ @@ -7241,7 +7405,7 @@ int LuaSyncedRead::GetProjectileDirection(lua_State* L) /*** * * @function Spring.GetProjectileVelocity - * @param projectileID integer + * @param projectileID ProjectileID * @return number? velX * @return number? velY * @return number? velZ @@ -7256,7 +7420,7 @@ int LuaSyncedRead::GetProjectileVelocity(lua_State* L) /*** * * @function Spring.GetProjectileGravity - * @param projectileID integer + * @param projectileID ProjectileID * @return number? */ int LuaSyncedRead::GetProjectileGravity(lua_State* L) @@ -7275,8 +7439,8 @@ int LuaSyncedRead::GetProjectileGravity(lua_State* L) /*** * * @function Spring.GetPieceProjectileParams - * @param projectileID integer - * @return number? explosionFlags encoded bitwise with SHATTER = 1, EXPLODE = 2, EXPLODE_ON_HIT = 2, FALL = 4, SMOKE = 8, FIRE = 16, NONE = 32, NO_CEG_TRAIL = 64, NO_HEATCLOUD = 128 + * @param projectileID ProjectileID + * @return integer? explosionFlags encoded bitwise with SHATTER = 1, EXPLODE = 2, EXPLODE_ON_HIT = 2, FALL = 4, SMOKE = 8, FIRE = 16, NONE = 32, NO_CEG_TRAIL = 64, NO_HEATCLOUD = 128 * @return number spinAngle * @return number spinSpeed * @return number spinVectorX @@ -7305,13 +7469,13 @@ int LuaSyncedRead::GetPieceProjectileParams(lua_State* L) /*** * * @function Spring.GetProjectileTarget - * @param projectileID integer - * @return number? targetTypeInt where + * @param projectileID ProjectileID + * @return integer? targetTypeInt where * string.byte('g') := GROUND * string.byte('u') := UNIT * string.byte('f') := FEATURE * string.byte('p') := PROJECTILE - * @return number|float3 target targetID or targetPos when targetTypeInt == string.byte('g') + * @return UnitID|FeatureID|ProjectileID|float3 target targetID or targetPos when targetTypeInt == string.byte('g') */ int LuaSyncedRead::GetProjectileTarget(lua_State* L) { @@ -7357,7 +7521,7 @@ int LuaSyncedRead::GetProjectileTarget(lua_State* L) /*** * * @function Spring.GetProjectileIsIntercepted - * @param projectileID integer + * @param projectileID ProjectileID * @return boolean? */ int LuaSyncedRead::GetProjectileIsIntercepted(lua_State* L) @@ -7377,8 +7541,8 @@ int LuaSyncedRead::GetProjectileIsIntercepted(lua_State* L) /*** * * @function Spring.GetProjectileTimeToLive - * @param projectileID integer - * @return number? + * @param projectileID ProjectileID + * @return integer? */ int LuaSyncedRead::GetProjectileTimeToLive(lua_State* L) { @@ -7397,8 +7561,8 @@ int LuaSyncedRead::GetProjectileTimeToLive(lua_State* L) /*** * * @function Spring.GetProjectileOwnerID - * @param projectileID integer - * @return number? + * @param projectileID ProjectileID + * @return UnitID? */ int LuaSyncedRead::GetProjectileOwnerID(lua_State* L) { @@ -7419,8 +7583,8 @@ int LuaSyncedRead::GetProjectileOwnerID(lua_State* L) /*** * * @function Spring.GetProjectileTeamID - * @param projectileID integer - * @return number? + * @param projectileID ProjectileID + * @return TeamID? */ int LuaSyncedRead::GetProjectileTeamID(lua_State* L) { @@ -7440,8 +7604,8 @@ int LuaSyncedRead::GetProjectileTeamID(lua_State* L) /*** * * @function Spring.GetProjectileAllyTeamID - * @param projectileID integer - * @return number? + * @param projectileID ProjectileID + * @return AllyTeamID? */ int LuaSyncedRead::GetProjectileAllyTeamID(lua_State* L) { @@ -7461,7 +7625,7 @@ int LuaSyncedRead::GetProjectileAllyTeamID(lua_State* L) /*** * * @function Spring.GetProjectileType - * @param projectileID integer + * @param projectileID ProjectileID * @return boolean? weapon * @return boolean piece */ @@ -7482,8 +7646,8 @@ int LuaSyncedRead::GetProjectileType(lua_State* L) * * @function Spring.GetProjectileDefID * - * @param projectileID integer - * @return number? + * @param projectileID ProjectileID + * @return WeaponDefID? */ int LuaSyncedRead::GetProjectileDefID(lua_State* L) { @@ -7507,7 +7671,7 @@ int LuaSyncedRead::GetProjectileDefID(lua_State* L) /*** Returns the name of the model piece from which a piece projectile was spawned. Returns nil for other projectiles including weapons * * @function Spring.GetPieceProjectileName - * @param projectileID integer + * @param projectileID ProjectileID * @return string? pieceName */ int LuaSyncedRead::GetPieceProjectileName(lua_State* L) @@ -7532,7 +7696,7 @@ int LuaSyncedRead::GetPieceProjectileName(lua_State* L) /*** * * @function Spring.GetProjectileDamages - * @param projectileID integer + * @param projectileID ProjectileID * @param tag string one of: * "paralyzeDamageTime" * "impulseFactor" @@ -7722,9 +7886,9 @@ int LuaSyncedRead::GetGroundNormal(lua_State* L) * @function Spring.GetGroundInfo * @param x number * @param z number - * @return number ix - * @return number iz - * @return number terrainTypeIndex + * @return integer ix + * @return integer iz + * @return integer terrainTypeIndex * @return string name * @return number metalExtraction * @return number hardness @@ -7791,6 +7955,12 @@ static void ParseMapCoords(lua_State* L, const char* caller, /*** * * @function Spring.GetGroundBlocked + * @param x number world x coordinate (or xMin when using 4-arg form) + * @param z number world z coordinate (or zMin when using 4-arg form) + * @param x2 number? world xMax (4-arg rectangle form) + * @param z2 number? world zMax (4-arg rectangle form) + * @return string? objectType `"feature"` or `"unit"` + * @return ObjectID? objectID the feature or unit ID */ int LuaSyncedRead::GetGroundBlocked(lua_State* L) { @@ -7807,7 +7977,7 @@ int LuaSyncedRead::GetGroundBlocked(lua_State* L) const CFeature* feature = dynamic_cast(s); if (feature != nullptr) { if (LuaUtils::IsFeatureVisible(L, feature)) { - HSTR_PUSH(L, "feature"); + LuaPushString(L, "feature"); lua_pushnumber(L, feature->id); return 2; } @@ -7818,7 +7988,7 @@ int LuaSyncedRead::GetGroundBlocked(lua_State* L) const CUnit* unit = dynamic_cast(s); if (unit != nullptr) { if (CLuaHandle::GetHandleFullRead(L) || (unit->losStatus[CLuaHandle::GetHandleReadAllyTeam(L)] & LOS_INLOS)) { - HSTR_PUSH(L, "unit"); + LuaPushString(L, "unit"); lua_pushnumber(L, unit->id); return 2; } @@ -7854,8 +8024,8 @@ int LuaSyncedRead::GetGroundExtremes(lua_State* L) /*** * * @function Spring.GetTerrainTypeData - * @param terrainTypeInfo number - * @return number index + * @param terrainTypeInfo integer + * @return integer index * @return string name * @return number hardness * @return number tankSpeed @@ -7918,7 +8088,7 @@ int LuaSyncedRead::GetSmoothMeshHeight(lua_State* L) /*** * * @function Spring.TestMoveOrder - * @param unitDefID integer + * @param unitDefID UnitDefID * @param posX number * @param posY number * @param posZ number @@ -7982,13 +8152,13 @@ int LuaSyncedRead::TestMoveOrder(lua_State* L) /*** * @function Spring.TestBuildOrder - * @param unitDefID integer + * @param unitDefID UnitDefID * @param x number * @param y number * @param z number * @param facing Facing * @return BuildOrderBlockedStatus blocking - * @return integer? featureID A reclaimable feature in the way. + * @return FeatureID? featureID A reclaimable feature in the way. */ int LuaSyncedRead::TestBuildOrder(lua_State* L) { @@ -8033,7 +8203,7 @@ int LuaSyncedRead::TestBuildOrder(lua_State* L) /*** Snaps a position to the building grid * * @function Spring.Pos2BuildPos - * @param unitDefID integer + * @param unitDefID UnitDefID * @param posX number * @param posY number * @param posZ number @@ -8062,8 +8232,8 @@ int LuaSyncedRead::Pos2BuildPos(lua_State* L) /*** * * @function Spring.ClosestBuildPos - * @param teamID integer - * @param unitDefID integer + * @param teamID TeamID + * @param unitDefID UnitDefID * @param posX number * @param posY number * @param posZ number @@ -8133,7 +8303,7 @@ static int GetEffectiveLosAllyTeam(lua_State* L, int arg) * @param posX number * @param posY number * @param posZ number - * @param allyTeamID integer? + * @param allyTeamID AllyTeamID? * @return boolean inLosOrRadar * @return boolean inLos * @return boolean inRadar @@ -8173,7 +8343,7 @@ int LuaSyncedRead::GetPositionLosState(lua_State* L) * @param posX number * @param posY number * @param posZ number - * @param allyTeamID integer? + * @param allyTeamID AllyTeamID? * @return boolean */ int LuaSyncedRead::IsPosInLos(lua_State* L) @@ -8199,7 +8369,7 @@ int LuaSyncedRead::IsPosInLos(lua_State* L) * @param posX number * @param posY number * @param posZ number - * @param allyTeamID integer? + * @param allyTeamID AllyTeamID? * @return boolean */ int LuaSyncedRead::IsPosInRadar(lua_State* L) @@ -8225,7 +8395,7 @@ int LuaSyncedRead::IsPosInRadar(lua_State* L) * @param posX number * @param posY number * @param posZ number - * @param allyTeamID integer? + * @param allyTeamID AllyTeamID? * @return boolean */ int LuaSyncedRead::IsPosInAirLos(lua_State* L) @@ -8247,8 +8417,8 @@ int LuaSyncedRead::IsPosInAirLos(lua_State* L) /*** Get unit los state (bitmask) * * @function Spring.GetUnitLosState - * @param unitID integer - * @param allyTeamID integer? + * @param unitID UnitID + * @param allyTeamID AllyTeamID? * @param raw true Return a bitmask. * @return LosMask|integer? bitmask A bitmask of `LosMask` bits */ @@ -8256,9 +8426,9 @@ int LuaSyncedRead::IsPosInAirLos(lua_State* L) /*** Get unit los state (table) * * @function Spring.GetUnitLosState - * @param unitID integer - * @param allyTeamID integer? - * @param raw false? Return a table. + * @param unitID UnitID + * @param allyTeamID AllyTeamID? + * @param raw false? (Default: `false`) Return a table. * @return table<"los"|"radar"|"typed",boolean>? los A table of LOS state names as keys and booleans as values, or `nil` if `unitID` is invalid. */ int LuaSyncedRead::GetUnitLosState(lua_State* L) @@ -8291,13 +8461,13 @@ int LuaSyncedRead::GetUnitLosState(lua_State* L) lua_createtable(L, 0, 3); if (losStatus & LOS_INLOS) { - HSTR_PUSH_BOOL(L, "los", true); + LuaPushNamedBool(L, "los", true); } if (losStatus & LOS_INRADAR) { - HSTR_PUSH_BOOL(L, "radar", true); + LuaPushNamedBool(L, "radar", true); } if ((losStatus & LOS_INLOS) || isTyped) { - HSTR_PUSH_BOOL(L, "typed", true); + LuaPushNamedBool(L, "typed", true); } return 1; } @@ -8306,8 +8476,8 @@ int LuaSyncedRead::GetUnitLosState(lua_State* L) /*** * * @function Spring.IsUnitInLos - * @param unitID integer - * @param allyTeamID integer + * @param unitID UnitID + * @param allyTeamID AllyTeamID? defaults to the calling widget/gadget's ally team * @return boolean inLos */ int LuaSyncedRead::IsUnitInLos(lua_State* L) @@ -8330,8 +8500,8 @@ int LuaSyncedRead::IsUnitInLos(lua_State* L) /*** * * @function Spring.IsUnitInAirLos - * @param unitID integer - * @param allyTeamID integer + * @param unitID UnitID + * @param allyTeamID AllyTeamID? defaults to the calling widget/gadget's ally team * @return boolean inAirLos */ int LuaSyncedRead::IsUnitInAirLos(lua_State* L) @@ -8354,8 +8524,8 @@ int LuaSyncedRead::IsUnitInAirLos(lua_State* L) /*** * * @function Spring.IsUnitInRadar - * @param unitID integer - * @param allyTeamID integer + * @param unitID UnitID + * @param allyTeamID AllyTeamID? defaults to the calling widget/gadget's ally team * @return boolean inRadar */ int LuaSyncedRead::IsUnitInRadar(lua_State* L) @@ -8378,8 +8548,8 @@ int LuaSyncedRead::IsUnitInRadar(lua_State* L) /*** * * @function Spring.IsUnitInJammer - * @param unitID integer - * @param allyTeamID integer + * @param unitID UnitID + * @param allyTeamID AllyTeamID * @return boolean inJammer */ int LuaSyncedRead::IsUnitInJammer(lua_State* L) @@ -8539,10 +8709,10 @@ static int GetSolidObjectPieceList(lua_State* L, const CSolidObject* o) static int GetSolidObjectPieceInfoHelper(lua_State* L, const S3DModelPiece& op) { lua_createtable(L, 0, 7); - HSTR_PUSH_STRING(L, "name", op.name); - HSTR_PUSH_STRING(L, "parent", ((op.parent != nullptr) ? op.parent->name : "[null]")); + LuaPushNamedString(L, "name", op.name); + LuaPushNamedString(L, "parent", ((op.parent != nullptr) ? op.parent->name : "[null]")); - HSTR_PUSH(L, "children"); + LuaPushString(L, "children"); lua_createtable(L, op.children.size(), 0); for (size_t c = 0; c < op.children.size(); c++) { lua_pushsstring(L, op.children[c]->name); @@ -8550,11 +8720,9 @@ static int GetSolidObjectPieceInfoHelper(lua_State* L, const S3DModelPiece& op) } lua_rawset(L, -3); - HSTR_PUSH(L, "isEmpty"); - lua_pushboolean(L, !op.HasGeometryData()); - lua_rawset(L, -3); + LuaPushNamedBool(L, "isEmpty", !op.HasGeometryData()); - HSTR_PUSH(L, "min"); + LuaPushString(L, "min"); lua_createtable(L, 3, 0); { lua_pushnumber(L, op.mins.x); lua_rawseti(L, -2, 1); lua_pushnumber(L, op.mins.y); lua_rawseti(L, -2, 2); @@ -8562,7 +8730,7 @@ static int GetSolidObjectPieceInfoHelper(lua_State* L, const S3DModelPiece& op) } lua_rawset(L, -3); - HSTR_PUSH(L, "max"); + LuaPushString(L, "max"); lua_createtable(L, 3, 0); { lua_pushnumber(L, op.maxs.x); lua_rawseti(L, -2, 1); lua_pushnumber(L, op.maxs.y); lua_rawseti(L, -2, 2); @@ -8570,7 +8738,7 @@ static int GetSolidObjectPieceInfoHelper(lua_State* L, const S3DModelPiece& op) } lua_rawset(L, -3); - HSTR_PUSH(L, "offset"); + LuaPushString(L, "offset"); lua_createtable(L, 3, 0); { lua_pushnumber(L, op.offset.x); lua_rawseti(L, -2, 1); lua_pushnumber(L, op.offset.y); lua_rawseti(L, -2, 2); @@ -8679,7 +8847,7 @@ static int GetSolidObjectPieceMatrix(lua_State* L, const CSolidObject* o) * * @function Spring.GetModelRootPiece * @param modelName string - * @return number index of the root piece + * @return integer index of the root piece */ int LuaSyncedRead::GetModelRootPiece(lua_State* L) { return ::GetModelRootPiece(L, luaL_optsstring(L, 1, "")); @@ -8689,7 +8857,7 @@ int LuaSyncedRead::GetModelRootPiece(lua_State* L) { * * @function Spring.GetModelPieceMap * @param modelName string - * @return table? pieceInfos where keys are piece names and values are indices + * @return table? pieceInfos where keys are piece names and values are indices */ int LuaSyncedRead::GetModelPieceMap(lua_State* L) { return ::GetModelPieceMap(L, luaL_optsstring(L, 1, "")); @@ -8710,8 +8878,8 @@ int LuaSyncedRead::GetModelPieceList(lua_State* L) { /*** * * @function Spring.GetUnitRootPiece - * @param unitID integer - * @return number index of the root piece + * @param unitID UnitID + * @return integer index of the root piece */ int LuaSyncedRead::GetUnitRootPiece(lua_State* L) { return (GetSolidObjectRootPiece(L, ParseTypedUnit(L, __func__, 1))); @@ -8720,8 +8888,8 @@ int LuaSyncedRead::GetUnitRootPiece(lua_State* L) { /*** * * @function Spring.GetUnitPieceMap - * @param unitID integer - * @return table? pieceInfos where keys are piece names and values are indices + * @param unitID UnitID + * @return table? pieceInfos where keys are piece names and values are indices */ int LuaSyncedRead::GetUnitPieceMap(lua_State* L) { return (GetSolidObjectPieceMap(L, ParseTypedUnit(L, __func__, 1))); @@ -8731,7 +8899,7 @@ int LuaSyncedRead::GetUnitPieceMap(lua_State* L) { /*** * * @function Spring.GetUnitPieceList - * @param unitID integer + * @param unitID UnitID * @return string[] pieceNames */ int LuaSyncedRead::GetUnitPieceList(lua_State* L) { @@ -8742,7 +8910,7 @@ int LuaSyncedRead::GetUnitPieceList(lua_State* L) { /*** * * @function Spring.GetUnitPieceInfo - * @param unitID integer + * @param unitID UnitID * @param pieceIndex integer * @return PieceInfo? pieceInfo */ @@ -8754,7 +8922,7 @@ int LuaSyncedRead::GetUnitPieceInfo(lua_State* L) { /*** * * @function Spring.GetUnitPiecePosDir - * @param unitID integer + * @param unitID UnitID * @param pieceIndex integer * @return number? posX * @return number posY @@ -8771,7 +8939,7 @@ int LuaSyncedRead::GetUnitPiecePosDir(lua_State* L) { /*** * * @function Spring.GetUnitPiecePosition - * @param unitID integer + * @param unitID UnitID * @param pieceIndex integer * @return number? posX * @return number posY @@ -8785,7 +8953,7 @@ int LuaSyncedRead::GetUnitPiecePosition(lua_State* L) { /*** * * @function Spring.GetUnitPieceDirection - * @param unitID integer + * @param unitID UnitID * @param pieceIndex integer * @return number? dirX * @return number dirY @@ -8799,7 +8967,7 @@ int LuaSyncedRead::GetUnitPieceDirection(lua_State* L) { /*** * * @function Spring.GetUnitPieceMatrix - * @param unitID integer + * @param unitID UnitID * @param pieceIndex integer * @return number? m11 * @return number m12 @@ -8825,8 +8993,8 @@ int LuaSyncedRead::GetUnitPieceMatrix(lua_State* L) { /*** * * @function Spring.GetFeatureRootPiece - * @param featureID integer - * @return number index of the root piece + * @param featureID FeatureID + * @return integer index of the root piece */ int LuaSyncedRead::GetFeatureRootPiece(lua_State* L) { return (GetSolidObjectRootPiece(L, ParseFeature(L, __func__, 1))); @@ -8835,8 +9003,8 @@ int LuaSyncedRead::GetFeatureRootPiece(lua_State* L) { /*** * * @function Spring.GetFeaturePieceMap - * @param featureID integer - * @return table pieceInfos where keys are piece names and values are indices + * @param featureID FeatureID + * @return table pieceInfos where keys are piece names and values are indices */ int LuaSyncedRead::GetFeaturePieceMap(lua_State* L) { return (GetSolidObjectPieceMap(L, ParseFeature(L, __func__, 1))); @@ -8846,7 +9014,7 @@ int LuaSyncedRead::GetFeaturePieceMap(lua_State* L) { /*** * * @function Spring.GetFeaturePieceList - * @param featureID integer + * @param featureID FeatureID * @return string[] pieceNames */ int LuaSyncedRead::GetFeaturePieceList(lua_State* L) { @@ -8857,7 +9025,7 @@ int LuaSyncedRead::GetFeaturePieceList(lua_State* L) { /*** * * @function Spring.GetFeaturePieceInfo - * @param featureID integer + * @param featureID FeatureID * @param pieceIndex integer * @return PieceInfo? pieceInfo */ @@ -8869,7 +9037,7 @@ int LuaSyncedRead::GetFeaturePieceInfo(lua_State* L) { /*** * * @function Spring.GetFeaturePiecePosDir - * @param featureID integer + * @param featureID FeatureID * @param pieceIndex integer * @return number? posX * @return number posY @@ -8886,7 +9054,7 @@ int LuaSyncedRead::GetFeaturePiecePosDir(lua_State* L) { /*** * * @function Spring.GetFeaturePiecePosition - * @param featureID integer + * @param featureID FeatureID * @param pieceIndex integer * @return number? posX * @return number posY @@ -8900,7 +9068,7 @@ int LuaSyncedRead::GetFeaturePiecePosition(lua_State* L) { /*** * * @function Spring.GetFeaturePieceDirection - * @param featureID integer + * @param featureID FeatureID * @param pieceIndex integer * @return number? dirX * @return number dirY @@ -8914,7 +9082,7 @@ int LuaSyncedRead::GetFeaturePieceDirection(lua_State* L) { /*** * * @function Spring.GetFeaturePieceMatrix - * @param featureID integer + * @param featureID FeatureID * @param pieceIndex integer * @return number? m11 * @return number m12 @@ -8941,14 +9109,14 @@ int LuaSyncedRead::GetFeaturePieceMatrix(lua_State* L) { * * @function Spring.GetUnitScriptPiece * - * @param unitID integer + * @param unitID UnitID * @return integer[] pieceIndices */ /*** * * @function Spring.GetUnitScriptPiece * - * @param unitID integer + * @param unitID UnitID * @param scriptPiece integer * @return integer pieceIndex */ @@ -8987,9 +9155,9 @@ int LuaSyncedRead::GetUnitScriptPiece(lua_State* L) * * @function Spring.GetUnitScriptNames * - * @param unitID integer + * @param unitID UnitID * - * @return table where keys are piece names and values are piece indices + * @return table pieceInfos where keys are piece names and values are piece indices */ int LuaSyncedRead::GetUnitScriptNames(lua_State* L) { @@ -9011,6 +9179,142 @@ int LuaSyncedRead::GetUnitScriptNames(lua_State* L) return 1; } + +static int TraceRayImpl(lua_State *const L, const float3 &pos, const float3 &dir, const float maxLen, std::string_view type) +{ + if (type != "unit" && type != "feature" && type != "both") + return luaL_error(L, "invalid type '%s', expected 'unit', 'feature', or 'both'", type.data()); + + const bool testUnits = (type == "unit" || type == "both"); + const bool testFeatures = (type == "feature" || type == "both"); + + QuadFieldQuery qfQuery; + quadField.GetQuadsOnRay(qfQuery, pos, dir, maxLen); + + spring::unordered_set testedUnitIDs; + spring::unordered_set testedFeatureIDs; + std::vector > hits; + + for (const int quadIdx : *qfQuery.quads) { + const CQuadField::Quad& quad = quadField.GetQuad(quadIdx); + + if (testUnits) { + for (const auto *unit : quad.units) { + if (!unit->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) + continue; + + if (!testedUnitIDs.insert(unit->id).second) + continue; + + if (!LuaUtils::IsUnitInLos(L, unit)) + continue; + + CollisionQuery cq; + if (CCollisionHandler::DetectHit(unit, unit->GetTransformMatrix(true), pos, pos + dir * maxLen, &cq, true)) { + const float len = cq.GetHitPosDist(pos, dir); + if (len > maxLen) // possibly a bug in CCollisionHandler::DetectHit? + continue; + hits.emplace_back(len, unit->id, "unit"); + } + } + } + + if (testFeatures) { + for (const auto *feature : quad.features) { + if (!feature->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) + continue; + + if (!testedFeatureIDs.insert(feature->id).second) + continue; + + if (!LuaUtils::IsFeatureVisible(L, feature)) + continue; + + CollisionQuery cq; + if (CCollisionHandler::DetectHit(feature, feature->GetTransformMatrix(true), pos, pos + dir * maxLen, &cq, true)) { + const float len = cq.GetHitPosDist(pos, dir); + if (len > maxLen) + continue; + hits.emplace_back(len, feature->id, "feature"); + } + } + } + } + + std::stable_sort(hits.begin(), hits.end(), [] (const auto& a, const auto& b) { + return std::get<0>(a) < std::get<0>(b); + }); + + lua_createtable(L, hits.size(), 0); + + int num = 0; + for (const auto& [hitLength, objectID, objectType] : hits) { + lua_createtable(L, 3, 0); + + lua_pushnumber(L, hitLength); + lua_rawseti(L, -2, 1); + lua_pushnumber(L, objectID); + lua_rawseti(L, -2, 2); + lua_pushstring(L, objectType); + lua_rawseti(L, -2, 3); + + lua_rawseti(L, -2, ++num); + } + + return 1; +} + +/*** Traces a ray from a position in a direction + * + * @function Spring.TraceRayInDirection + * + * Returns all unit and/or feature hits along a ray, sorted by distance + * from the start position. + * + * @param posX number + * @param posY number + * @param posZ number + * @param dirX number + * @param dirY number + * @param dirZ number + * @param maxLength number + * @param type string Object type to test: `"unit"`, `"feature"`, or `"both"` + * @return table[] hits Array of `{hitLength, objectID, objectType}` entries + */ +int LuaSyncedRead::TraceRayInDirection(lua_State* L) +{ + float3 pos(luaL_checkfloat(L, 1), luaL_checkfloat(L, 2), luaL_checkfloat(L, 3)); + float3 dir(luaL_checkfloat(L, 4), luaL_checkfloat(L, 5), luaL_checkfloat(L, 6)); + const float maxLen = luaL_optfloat(L, 7, 999999.f); + const char* type = luaL_checkstring(L, 8); + return TraceRayImpl(L, pos, dir, maxLen, type); +} + +/*** Traces a ray between two positions + * + * @function Spring.TraceRayBetweenPositions + * + * Checks for unit and/or feature collisions between two positions + * and returns all hits sorted by distance from the start position. + * + * @param startX number + * @param startY number + * @param startZ number + * @param endX number + * @param endY number + * @param endZ number + * @param type string Object type to test: `"unit"`, `"feature"`, or `"both"` + * @return table[] hits Array of `{hitLength, objectID, objectType}` entries + */ +int LuaSyncedRead::TraceRayBetweenPositions(lua_State* L) +{ + float3 start(luaL_checkfloat(L, 1), luaL_checkfloat(L, 2), luaL_checkfloat(L, 3)); + float3 end(luaL_checkfloat(L, 4), luaL_checkfloat(L, 5), luaL_checkfloat(L, 6)); + const char* type = luaL_checkstring(L, 7); + const auto [dir, length] = (end - start).GetNormalized(); + return TraceRayImpl(L, start, dir, length, type); +} + static int TraceRayGroundImpl(lua_State *const L, const float3 &pos, const float3 &dir, const float maxLen, const bool testWater) { const float rayLength = CGround::LineGroundWaterCol(pos, dir, maxLen, testWater, CLuaHandle::GetHandleSynced(L)); @@ -9039,11 +9343,12 @@ static int TraceRayGroundImpl(lua_State *const L, const float3 &pos, const float * @param dirX number * @param dirY number * @param dirZ number + * @param maxLength number? (Default: `999999`) * @param testWater boolean? (Default: `true`) - * @return number rayLength - * @return number posX - * @return number posY - * @return number posZ + * @return number? rayLength + * @return number? posX + * @return number? posY + * @return number? posZ */ int LuaSyncedRead::TraceRayGroundInDirection(lua_State* L) { @@ -9097,7 +9402,7 @@ int LuaSyncedRead::TraceRayGroundBetweenPositions(lua_State* L) * * @function Spring.GetRadarErrorParams * - * @param allyTeamID integer + * @param allyTeamID AllyTeamID * * @return number? radarErrorSize actual radar error size (when allyTeamID is allied to current team) or base radar error size * @return number baseRadarErrorSize diff --git a/rts/Lua/LuaSyncedRead.h b/rts/Lua/LuaSyncedRead.h index 98c632ad9b0..dbec13c0ab7 100644 --- a/rts/Lua/LuaSyncedRead.h +++ b/rts/Lua/LuaSyncedRead.h @@ -302,9 +302,8 @@ class LuaSyncedRead { static int GetRadarErrorParams(lua_State* L); - static int TraceRay(lua_State* L); //TODO: not implemented - static int TraceRayUnits(lua_State* L); //TODO: not implemented - static int TraceRayFeatures(lua_State* L); //TODO: not implemented + static int TraceRayInDirection(lua_State* L); + static int TraceRayBetweenPositions(lua_State* L); static int TraceRayGroundBetweenPositions(lua_State* L); static int TraceRayGroundInDirection(lua_State* L); }; diff --git a/rts/Lua/LuaSyncedTable.cpp b/rts/Lua/LuaSyncedTable.cpp index a1d76c21d58..27452aacc65 100644 --- a/rts/Lua/LuaSyncedTable.cpp +++ b/rts/Lua/LuaSyncedTable.cpp @@ -91,7 +91,7 @@ static int SyncTableMetatable(lua_State* L) */ bool LuaSyncedTable::PushEntries(lua_State* L) { - HSTR_PUSH(L, "SYNCED"); + LuaPushString(L, "SYNCED"); lua_newtable(L); { // the proxy table lua_createtable(L, 0, 3); { // the metatable diff --git a/rts/Lua/LuaTextures.cpp b/rts/Lua/LuaTextures.cpp index 8d3a083dbef..0ae691d63c0 100644 --- a/rts/Lua/LuaTextures.cpp +++ b/rts/Lua/LuaTextures.cpp @@ -90,7 +90,9 @@ std::string LuaTextures::Create(const Texture& tex) } break; } - if (glGetError() != GL_NO_ERROR) { + if (const GLenum texErr = glGetError(); texErr != GL_NO_ERROR) { + LOG_L(L_ERROR, "[LuaTextures::%s] glTexImage failed: target=0x%x size=%dx%d fmt=0x%x dataFmt=0x%x dataType=0x%x border=%d glError=0x%x", + __func__, tex.target, tex.xsize, tex.ysize, tex.format, dataFormat, dataType, tex.border, texErr); glDeleteTextures(1, &texID); glBindTexture(tex.target, currentBinding); return ""; diff --git a/rts/Lua/LuaUI.cpp b/rts/Lua/LuaUI.cpp index 01204219fa4..002184fd1df 100644 --- a/rts/Lua/LuaUI.cpp +++ b/rts/Lua/LuaUI.cpp @@ -13,6 +13,7 @@ #include "LuaConstEngine.h" #include "LuaConstGame.h" #include "LuaConstPlatform.h" +#include "LuaDebugExtra.h" #include "LuaSyncedRead.h" #include "LuaInterCall.h" #include "LuaLibs.h" @@ -40,7 +41,6 @@ #include "System/Config/ConfigHandler.h" #include "System/StringUtil.h" #include "System/Threading/SpringThreading.h" -#include "lib/luasocket/src/luasocket.h" #include @@ -129,6 +129,7 @@ CLuaUI::CLuaUI() !AddEntriesToTable(L, "Spring", LuaUnsyncedCtrl::PushEntries) || !AddEntriesToTable(L, "Spring", LuaUnsyncedRead::PushEntries) || !AddEntriesToTable(L, "Spring", LuaUICommand::PushEntries) || + !AddEntriesToTable(L, "debug", LuaDebugExtra::PushEntries) || !AddEntriesToTable(L, "gl", LuaOpenGL::PushEntries) || !AddEntriesToTable(L, "GL", LuaConstGL::PushEntries) || !AddEntriesToTable(L, "Engine", LuaConstEngine::PushEntries) || @@ -207,25 +208,6 @@ GetWatchDef(Explosion) SetWatchDef(Explosion) -void CLuaUI::InitLuaSocket(lua_State* L) { - std::string code; - std::string filename = "LuaSocket/socket.lua"; - CFileHandler f(filename, SPRING_VFS_BASE); - - if (!f.FileExists()) { - LOG_L(L_ERROR, "Error loading %s (file does not exist)", filename.c_str()); - return; - } - - LUA_OPEN_LIB(L, luaopen_socket_core); - - if (f.LoadStringData(code)) { - LoadCode(L, std::move(code), filename); - } else { - LOG_L(L_ERROR, "Error loading %s", filename.c_str()); - } -} - string CLuaUI::LoadFile(const string& name, const std::string& mode) const { CFileHandler f(name, mode); @@ -691,6 +673,12 @@ bool CLuaUI::GetLuaCmdDescList(lua_State* L, int index, vectorhaveShockFront = true; diff --git a/rts/Lua/LuaUI.h b/rts/Lua/LuaUI.h index 6ee9b2d1f73..dfe2b9ab2a4 100644 --- a/rts/Lua/LuaUI.h +++ b/rts/Lua/LuaUI.h @@ -83,7 +83,6 @@ class CLuaUI : public CLuaHandle string LoadFile(const string& name, const std::string& mode) const; bool LoadCFunctions(lua_State* L); - void InitLuaSocket(lua_State* L); bool BuildCmdDescTable(lua_State* L, const vector& cmds); bool GetLuaIntMap(lua_State* L, int index, spring::unordered_map& intList); diff --git a/rts/Lua/LuaUICommand.cpp b/rts/Lua/LuaUICommand.cpp index 2258311f49d..b97e0b53ed5 100644 --- a/rts/Lua/LuaUICommand.cpp +++ b/rts/Lua/LuaUICommand.cpp @@ -40,20 +40,20 @@ int LuaUICommand::GetUICommands(lua_State* L) const ISyncedActionExecutor* exec = pair.second; lua_createtable(L, 0, 4); - HSTR_PUSH_STRING(L, "command", exec->GetCommand()); - HSTR_PUSH_STRING(L, "description", exec->GetDescription()); - HSTR_PUSH_BOOL(L, "synced", exec->IsSynced()); - HSTR_PUSH_BOOL(L, "cheat", exec->IsCheatRequired()); + LuaPushNamedString(L, "command", exec->GetCommand()); + LuaPushNamedString(L, "description", exec->GetDescription()); + LuaPushNamedBool(L, "synced", exec->IsSynced()); + LuaPushNamedBool(L, "cheat", exec->IsCheatRequired()); lua_rawseti(L, -2, count++); } for (const auto& pair: unsyncedExecutors) { const IUnsyncedActionExecutor* exec = pair.second; lua_createtable(L, 0, 4); - HSTR_PUSH_STRING(L, "command", exec->GetCommand()); - HSTR_PUSH_STRING(L, "description", exec->GetDescription()); - HSTR_PUSH_BOOL(L, "synced", exec->IsSynced()); - HSTR_PUSH_BOOL(L, "cheat", exec->IsCheatRequired()); + LuaPushNamedString(L, "command", exec->GetCommand()); + LuaPushNamedString(L, "description", exec->GetDescription()); + LuaPushNamedBool(L, "synced", exec->IsSynced()); + LuaPushNamedBool(L, "cheat", exec->IsCheatRequired()); lua_rawseti(L, -2, count++); } return 1; diff --git a/rts/Lua/LuaUnitDefs.cpp b/rts/Lua/LuaUnitDefs.cpp index 3818b78a7e5..21568ce42b9 100644 --- a/rts/Lua/LuaUnitDefs.cpp +++ b/rts/Lua/LuaUnitDefs.cpp @@ -370,18 +370,18 @@ static int WeaponsTable(lua_State* L, const void* data) lua_pushnumber(L, i + LUA_WEAPON_BASE_INDEX); lua_createtable(L, 0, 10); { - HSTR_PUSH_NUMBER(L, "weaponDef", wd->id); - HSTR_PUSH_NUMBER(L, "slavedTo", udw.slavedTo - 1 + LUA_WEAPON_BASE_INDEX); - HSTR_PUSH_NUMBER(L, "maxAngleDif", udw.maxMainDirAngleDif); - HSTR_PUSH_NUMBER(L, "mainDirX", udw.mainDir.x); - HSTR_PUSH_NUMBER(L, "mainDirY", udw.mainDir.y); - HSTR_PUSH_NUMBER(L, "mainDirZ", udw.mainDir.z); - - HSTR_PUSH(L, "badTargets"); + LuaPushNamedNumber(L, "weaponDef", wd->id); + LuaPushNamedNumber(L, "slavedTo", udw.slavedTo - 1 + LUA_WEAPON_BASE_INDEX); + LuaPushNamedNumber(L, "maxAngleDif", udw.maxMainDirAngleDif); + LuaPushNamedNumber(L, "mainDirX", udw.mainDir.x); + LuaPushNamedNumber(L, "mainDirY", udw.mainDir.y); + LuaPushNamedNumber(L, "mainDirZ", udw.mainDir.z); + + LuaPushString(L, "badTargets"); CategorySetFromBits(L, &udw.badTargetCat); lua_rawset(L, -3); - HSTR_PUSH(L, "onlyTargets"); + LuaPushString(L, "onlyTargets"); CategorySetFromBits(L, &udw.onlyTargetCat); lua_rawset(L, -3); } @@ -404,10 +404,10 @@ static void PushGuiSoundSet(lua_State* L, const string& name, lua_pushnumber(L, i + 1); lua_createtable(L, 0, CLuaHandle::GetHandleSynced(L) ? 2 : 3); const GuiSoundSetData& sound = soundSet.GetSoundData(i); - HSTR_PUSH_STRING(L, "name", sound.name); - HSTR_PUSH_NUMBER(L, "volume", sound.volume); + LuaPushNamedString(L, "name", sound.name); + LuaPushNamedNumber(L, "volume", sound.volume); if (!CLuaHandle::GetHandleSynced(L)) { - HSTR_PUSH_NUMBER(L, "id", sound.id); + LuaPushNamedNumber(L, "id", sound.id); } lua_rawset(L, -3); } @@ -451,22 +451,22 @@ static int MoveDefTable(lua_State* L, const void* data) assert(md->pathType == mdPathType); lua_createtable(L, 0, 14); - HSTR_PUSH_NUMBER(L, "id" , md->pathType); - HSTR_PUSH_NUMBER(L, "smClass" , md->speedModClass); - HSTR_PUSH_NUMBER(L, "xsize" , md->xsize); - HSTR_PUSH_NUMBER(L, "zsize" , md->zsize); - HSTR_PUSH_NUMBER(L, "depth" , md->depth); - HSTR_PUSH_NUMBER(L, "maxSlope" , md->maxSlope); - HSTR_PUSH_NUMBER(L, "slopeMod" , md->slopeMod); - HSTR_PUSH_NUMBER(L, "depthMod" , md->depthModParams[MoveDef::DEPTHMOD_LIN_COEFF]); - HSTR_PUSH_NUMBER(L, "crushStrength", md->crushStrength); - HSTR_PUSH_BOOL (L, "isSubmarine" , md->isSubmarine); - - HSTR_PUSH_BOOL (L, "heatMapping" , md->heatMapping); - HSTR_PUSH_NUMBER(L, "heatMod" , md->heatMod); - HSTR_PUSH_NUMBER(L, "heatProduced" , md->heatProduced); - - HSTR_PUSH_STRING(L, "name" , md->name); + LuaPushNamedNumber(L, "id" , md->pathType); + LuaPushNamedNumber(L, "smClass" , md->speedModClass); + LuaPushNamedNumber(L, "xsize" , md->xsize); + LuaPushNamedNumber(L, "zsize" , md->zsize); + LuaPushNamedNumber(L, "depth" , md->depth); + LuaPushNamedNumber(L, "maxSlope" , md->maxSlope); + LuaPushNamedNumber(L, "slopeMod" , md->slopeMod); + LuaPushNamedNumber(L, "depthMod" , md->depthModParams[MoveDef::DEPTHMOD_LIN_COEFF]); + LuaPushNamedNumber(L, "crushStrength", md->crushStrength); + LuaPushNamedBool (L, "isSubmarine" , md->isSubmarine); + + LuaPushNamedBool (L, "heatMapping" , md->heatMapping); + LuaPushNamedNumber(L, "heatMod" , md->heatMod); + LuaPushNamedNumber(L, "heatProduced" , md->heatProduced); + + LuaPushNamedString(L, "name" , md->name); return 1; } diff --git a/rts/Lua/LuaUnsyncedCtrl.cpp b/rts/Lua/LuaUnsyncedCtrl.cpp index 8156e83f52f..ea022782d01 100644 --- a/rts/Lua/LuaUnsyncedCtrl.cpp +++ b/rts/Lua/LuaUnsyncedCtrl.cpp @@ -115,6 +115,7 @@ #undef Yield + /****************************************************************************** * Callouts to set state * @@ -467,12 +468,11 @@ static inline CUnit* ParseSelectUnit(lua_State* L, const char* caller, int index * @section console ******************************************************************************/ - /*** Send a ping request to the server * * @function Spring.Ping * - * @param pingTag number + * @param pingTag integer? * * @return nil */ @@ -634,7 +634,7 @@ int LuaUnsyncedCtrl::SendSpectatorChat(lua_State* L) { * * @function Spring.SendPrivateChat * @param message string - * @param playerID integer + * @param playerID PlayerID * @return nil */ int LuaUnsyncedCtrl::SendPrivateChat(lua_State* L) { @@ -691,7 +691,7 @@ int LuaUnsyncedCtrl::SendMessageToSpectators(lua_State* L) /*** @function Spring.SendMessageToPlayer - * @param playerID integer + * @param playerID PlayerID * @param message string * @return nil */ @@ -705,7 +705,7 @@ int LuaUnsyncedCtrl::SendMessageToPlayer(lua_State* L) /*** @function Spring.SendMessageToTeam - * @param teamID integer + * @param teamID TeamID * @param message string * @return nil */ @@ -719,7 +719,7 @@ int LuaUnsyncedCtrl::SendMessageToTeam(lua_State* L) /*** @function Spring.SendMessageToAllyTeam - * @param allyID integer + * @param allyID AllyTeamID * @param message string * @return nil */ @@ -798,14 +798,14 @@ int LuaUnsyncedCtrl::LoadSoundDef(lua_State* L) /*** @function Spring.PlaySoundFile * @param soundfile string - * @param volume number? (Default: 1.0) - * @param posx number? + * @param volume number? (Default: 1.0) optional; all following arguments are optional + * @param posx number? world position X (use with `posy` and `posz`, or omit all three) * @param posy number? * @param posz number? - * @param speedx number? + * @param speedx number? velocity X (use with `speedy` and `speedz` after position, or omit all three) * @param speedy number? * @param speedz number? - * @param channel SoundChannel? (Default: `0|"general"`) + * @param channel SoundChannel? (Default: `0|"general"`) optional; parsed from the last argument index after position and speed triples * @return boolean playSound */ int LuaUnsyncedCtrl::PlaySoundFile(lua_State* L) @@ -1101,11 +1101,11 @@ int LuaUnsyncedCtrl::AddWorldText(lua_State* L) /*** * * @function Spring.AddWorldUnit - * @param unitDefID integer + * @param unitDefID UnitDefID * @param posX number * @param posY number * @param posZ number - * @param teamID integer + * @param teamID TeamID * @param facing FacingInteger * @return nil */ @@ -1133,16 +1133,16 @@ int LuaUnsyncedCtrl::AddWorldUnit(lua_State* L) /*** * @function Spring.DrawUnitCommands - * @param unitID integer + * @param unitID UnitID */ /*** * @function Spring.DrawUnitCommands - * @param unitIDs integer[] Unit ids. + * @param unitIDs UnitID[] Unit ids. * @param tableOrArray false|nil Set to `true` if the unit IDs should be read from the keys of `unitIDs`. */ /*** * @function Spring.DrawUnitCommands - * @param unitIDs table Table with unit IDs as keys. + * @param unitIDs table Table with unit IDs as keys. * @param tableOrArray true Set to `false` if the unit IDs should be read from the values of `unitIDs`. * @return nil */ @@ -1212,7 +1212,10 @@ static CCameraController::StateMap ParseCamStateMap(lua_State* L, int tableIdx) * @param x number * @param y number * @param z number - * @param transTime number? + * @param transTime number? (Default: `0.5`) transition duration; values below zero are clamped to zero + * @param dirX number? (Default: current camera direction X) + * @param dirY number? (Default: current camera direction Y) + * @param dirZ number? (Default: current camera direction Z) * @return nil */ int LuaUnsyncedCtrl::SetCameraTarget(lua_State* L) @@ -1330,7 +1333,7 @@ int LuaUnsyncedCtrl::RunDollyCamera(lua_State* L) /*** Pause Dolly Camera * * @function Spring.PauseDollyCamera - * @param fraction number Fraction of the total runtime to pause at, 0 to 1 inclusive. A null value pauses at current percent + * @param fraction number? Fraction of the total runtime to pause at, 0 to 1 inclusive. A null value pauses at current percent * @return nil */ int LuaUnsyncedCtrl::PauseDollyCamera(lua_State* L) @@ -1387,7 +1390,7 @@ int LuaUnsyncedCtrl::SetDollyCameraPosition(lua_State* L) /*** Sets Dolly Camera movement Curve * * @function Spring.SetDollyCameraCurve - * @param degree number + * @param degree integer * @param cpoints ControlPoint[] NURBS control point positions. * @param knots table * @return nil @@ -1425,7 +1428,7 @@ int LuaUnsyncedCtrl::SetDollyCameraMode(lua_State* L) /*** Sets Dolly Camera movement curve to world relative or look target relative * * @function Spring.SetDollyCameraRelativeMode - * @param relativeMode number `1` world, `2` look target + * @param relativeMode integer `1` world, `2` look target * @return nil */ int LuaUnsyncedCtrl::SetDollyCameraRelativeMode(lua_State* L) @@ -1441,7 +1444,7 @@ int LuaUnsyncedCtrl::SetDollyCameraRelativeMode(lua_State* L) /*** Sets Dolly Camera Look Curve * * @function Spring.SetDollyCameraLookCurve - * @param degree number + * @param degree integer * @param cpoints ControlPoint[] NURBS control point positions. * @param knots table * @return nil @@ -1485,7 +1488,7 @@ int LuaUnsyncedCtrl::SetDollyCameraLookPosition(lua_State* L) /*** Sets target unit for Dolly Camera to look towards * * @function Spring.SetDollyCameraLookUnit - * @param unitID integer The unit to look at. + * @param unitID UnitID The unit to look at. * @return nil */ int LuaUnsyncedCtrl::SetDollyCameraLookUnit(lua_State* L) @@ -1508,7 +1511,7 @@ int LuaUnsyncedCtrl::SetDollyCameraLookUnit(lua_State* L) /*** Selects a single unit * * @function Spring.SelectUnit - * @param unitID integer? + * @param unitID UnitID? * @param append boolean? (Default: `false`) Append to current selection. * @return nil */ @@ -1532,7 +1535,7 @@ int LuaUnsyncedCtrl::SelectUnit(lua_State* L) /*** * * @function Spring.DeselectUnit - * @param unitID integer + * @param unitID UnitID * @return nil */ int LuaUnsyncedCtrl::DeselectUnit(lua_State* L) @@ -1574,7 +1577,7 @@ static int TableSelectionCommonFunc(lua_State* L, int unitIndexInTable, bool isS /*** Deselects multiple units. * * @function Spring.DeselectUnitArray - * @param unitIDs integer[] Table with unit IDs as values. + * @param unitIDs UnitID[] Table with unit IDs as values. * @return nil */ int LuaUnsyncedCtrl::DeselectUnitArray(lua_State* L) @@ -1585,7 +1588,7 @@ int LuaUnsyncedCtrl::DeselectUnitArray(lua_State* L) /*** Deselects multiple units. * * @function Spring.DeselectUnitMap - * @param unitMap table Table with unit IDs as keys. + * @param unitMap table Table with unit IDs as keys. * @return nil */ int LuaUnsyncedCtrl::DeselectUnitMap(lua_State* L) @@ -1596,7 +1599,7 @@ int LuaUnsyncedCtrl::DeselectUnitMap(lua_State* L) /*** Selects multiple units, or appends to selection. Accepts a table with unitIDs as values * * @function Spring.SelectUnitArray - * @param unitIDs integer[] Table with unit IDs as values. + * @param unitIDs UnitID[] Table with unit IDs as values. * @param append boolean? (Default: `false`) append to current selection * @return nil */ @@ -1608,7 +1611,7 @@ int LuaUnsyncedCtrl::SelectUnitArray(lua_State* L) /*** Selects multiple units, or appends to selection. Accepts a table with unitIDs as keys * * @function Spring.SelectUnitMap - * @param unitMap table Table with unit IDs as keys. + * @param unitMap table Table with unit IDs as keys. * @param append boolean? (Default: `false`) append to current selection * @return nil */ @@ -1795,7 +1798,7 @@ int LuaUnsyncedCtrl::AddMapLight(lua_State* L) * requires MaxDynamicMapLights > 0 * * @param lightParams LightParams - * @return number lightHandle + * @return integer lightHandle */ int LuaUnsyncedCtrl::AddModelLight(lua_State* L) { @@ -1818,7 +1821,7 @@ int LuaUnsyncedCtrl::AddModelLight(lua_State* L) /*** * @function Spring.UpdateMapLight * - * @param lightHandle number + * @param lightHandle integer * @param lightParams LightParams * @return boolean success */ @@ -1840,7 +1843,7 @@ int LuaUnsyncedCtrl::UpdateMapLight(lua_State* L) /*** * @function Spring.UpdateModelLight * - * @param lightHandle number + * @param lightHandle integer * @param lightParams LightParams * @return boolean success */ @@ -1920,10 +1923,10 @@ static bool AddLightTrackingTarget(lua_State* L, GL::Light* light, bool trackEna * * @function Spring.SetMapLightTrackingState * - * @param lightHandle number - * @param unitOrProjectileID integer - * @param enableTracking boolean - * @param unitOrProjectile boolean + * @param lightHandle integer + * @param unitOrProjectileID UnitID|ProjectileID + * @param enableTracking boolean? + * @param unitOrProjectile boolean? * @return boolean success */ int LuaUnsyncedCtrl::SetMapLightTrackingState(lua_State* L) @@ -1956,10 +1959,10 @@ int LuaUnsyncedCtrl::SetMapLightTrackingState(lua_State* L) * * @function Spring.SetModelLightTrackingState * - * @param lightHandle number - * @param unitOrProjectileID integer - * @param enableTracking boolean - * @param unitOrProjectile boolean + * @param lightHandle integer + * @param unitOrProjectileID UnitID|ProjectileID + * @param enableTracking boolean? + * @param unitOrProjectile boolean? * @return boolean success */ int LuaUnsyncedCtrl::SetModelLightTrackingState(lua_State* L) @@ -2024,8 +2027,8 @@ int LuaUnsyncedCtrl::SetMapShader(lua_State* L) /*** @function Spring.SetMapSquareTexture - * @param texSqrX number - * @param texSqrY number + * @param texSqrX integer + * @param texSqrY integer * @param luaTexName string * @return boolean success */ @@ -2150,8 +2153,7 @@ int LuaUnsyncedCtrl::SetSkyBoxTexture(lua_State* L) if (CLuaHandle::GetHandleSynced(L)) return 0; - if (const auto& sky = ISky::GetSky(); sky != nullptr) - sky->SetLuaTexture(ParseLuaTextureData(L, false)); + ISky::SetSkyLuaTexture(ParseLuaTextureData(L, false)); return 0; } @@ -2166,7 +2168,7 @@ int LuaUnsyncedCtrl::SetSkyBoxTexture(lua_State* L) /*** * * @function Spring.SetUnitNoDraw - * @param unitID integer + * @param unitID UnitID * @param noDraw boolean * @return nil */ @@ -2185,7 +2187,7 @@ int LuaUnsyncedCtrl::SetUnitNoDraw(lua_State* L) /*** * * @function Spring.SetUnitEngineDrawMask - * @param unitID integer + * @param unitID UnitID * @param drawMask number * @return nil */ @@ -2204,7 +2206,7 @@ int LuaUnsyncedCtrl::SetUnitEngineDrawMask(lua_State* L) /*** * * @function Spring.SetUnitAlwaysUpdateMatrix - * @param unitID integer + * @param unitID UnitID * @param alwaysUpdateMatrix boolean * @return nil */ @@ -2223,7 +2225,7 @@ int LuaUnsyncedCtrl::SetUnitAlwaysUpdateMatrix(lua_State* L) /*** * * @function Spring.SetUnitNoMinimap - * @param unitID integer + * @param unitID UnitID * @param unitNoMinimap boolean * @return nil */ @@ -2271,7 +2273,7 @@ int LuaUnsyncedCtrl::SetMiniMapRotation(lua_State* L) /*** * * @function Spring.SetUnitNoGroup - * @param unitID integer + * @param unitID UnitID * @param unitNoGroup boolean Whether unit can be added to selection groups */ int LuaUnsyncedCtrl::SetUnitNoGroup(lua_State* L) @@ -2293,7 +2295,7 @@ int LuaUnsyncedCtrl::SetUnitNoGroup(lua_State* L) /*** * * @function Spring.SetUnitNoSelect - * @param unitID integer + * @param unitID UnitID * @param unitNoSelect boolean whether unit can be selected or not * @return nil */ @@ -2321,7 +2323,7 @@ int LuaUnsyncedCtrl::SetUnitNoSelect(lua_State* L) /*** * * @function Spring.SetUnitLeaveTracks - * @param unitID integer + * @param unitID UnitID * @param unitLeaveTracks boolean whether unit leaves tracks on movement * @return nil */ @@ -2340,17 +2342,16 @@ int LuaUnsyncedCtrl::SetUnitLeaveTracks(lua_State* L) /*** * * @function Spring.SetUnitSelectionVolumeData - * @param unitID integer - * @param featureID integer + * @param unitID UnitID * @param scaleX number * @param scaleY number * @param scaleZ number * @param offsetX number * @param offsetY number * @param offsetZ number - * @param vType number - * @param tType number - * @param Axis number + * @param vType integer + * @param tType integer + * @param Axis integer * @return nil */ int LuaUnsyncedCtrl::SetUnitSelectionVolumeData(lua_State* L) @@ -2374,7 +2375,7 @@ int LuaUnsyncedCtrl::SetUnitSelectionVolumeData(lua_State* L) * * @function Spring.SetFeatureNoDraw * - * @param featureID integer + * @param featureID FeatureID * @param noDraw boolean * * @return nil @@ -2394,7 +2395,7 @@ int LuaUnsyncedCtrl::SetFeatureNoDraw(lua_State* L) /*** * * @function Spring.SetFeatureEngineDrawMask - * @param featureID integer + * @param featureID FeatureID * @param engineDrawMask number * @return nil */ @@ -2413,7 +2414,7 @@ int LuaUnsyncedCtrl::SetFeatureEngineDrawMask(lua_State* L) /*** * * @function Spring.SetFeatureAlwaysUpdateMatrix - * @param featureID integer + * @param featureID FeatureID * @param alwaysUpdateMat number * @return nil */ @@ -2433,7 +2434,7 @@ int LuaUnsyncedCtrl::SetFeatureAlwaysUpdateMatrix(lua_State* L) * * @function Spring.SetFeatureFade * - * @param featureID integer + * @param featureID FeatureID * @param allow boolean * * @return nil @@ -2454,16 +2455,16 @@ int LuaUnsyncedCtrl::SetFeatureFade(lua_State* L) * * @function Spring.SetFeatureSelectionVolumeData * - * @param featureID integer + * @param featureID FeatureID * @param scaleX number * @param scaleY number * @param scaleZ number * @param offsetX number * @param offsetY number * @param offsetZ number - * @param vType number - * @param tType number - * @param Axis number + * @param vType integer + * @param tType integer + * @param Axis integer * @return nil */ int LuaUnsyncedCtrl::SetFeatureSelectionVolumeData(lua_State* L) @@ -2547,7 +2548,7 @@ int LuaUnsyncedCtrl::FreeUnitIcon(lua_State* L) * @function Spring.UnitIconSetDraw * Use Spring.SetUnitIconDraw instead. * @deprecated - * @param unitID integer + * @param unitID UnitID * @param drawIcon boolean * @return nil */ @@ -2561,7 +2562,7 @@ int LuaUnsyncedCtrl::UnitIconSetDraw(lua_State* L) /*** * * @function Spring.SetUnitIconDraw - * @param unitID integer + * @param unitID UnitID * @param drawIcon boolean * @return nil */ @@ -2579,7 +2580,7 @@ int LuaUnsyncedCtrl::SetUnitIconDraw(lua_State* L) /*** * * @function Spring.SetUnitIcon - * @param unitID integer + * @param unitID UnitID * @param iconName string? supply nil to reset to the default * @return nil */ @@ -2615,7 +2616,7 @@ int LuaUnsyncedCtrl::SetUnitIcon(lua_State* L) * * @function Spring.SetUnitDefIcon * - * @param unitDefID integer + * @param unitDefID UnitDefID * @param iconName string * * @return nil @@ -2663,8 +2664,8 @@ int LuaUnsyncedCtrl::SetUnitDefIcon(lua_State* L) * * @function Spring.SetUnitDefImage * - * @param unitDefID integer - * @param image string luaTexture|texFile + * @param unitDefID UnitDefID + * @param image string? luaTexture|texFile * * @return nil */ @@ -2861,8 +2862,8 @@ static int SetActiveCommandByAction(lua_State* L) */ /*** @function Spring.SetActiveCommand - * @param cmdIndex number - * @param button number? (Default: `1`) + * @param cmdIndex integer + * @param button integer? (Default: `1`) * @param leftClick boolean? * @param rightClick boolean? * @param alt boolean? @@ -2962,10 +2963,11 @@ int LuaUnsyncedCtrl::SetBoxSelectionByEngine(lua_State* L) /*** * * @function Spring.SetTeamColor - * @param teamID integer + * @param teamID TeamID * @param r number * @param g number * @param b number + * @param alpha number? * @return nil */ int LuaUnsyncedCtrl::SetTeamColor(lua_State* L) @@ -3012,7 +3014,7 @@ int LuaUnsyncedCtrl::SetCustomPaletteColor(lua_State* L) * Sets a custom color for a unit from the palette. Custom assignments are permanent * until explicitly reset by passing nil, and are NOT affected by team changes. * @function Spring.SetUnitPaletteIndex - * @param unitID integer + * @param unitID UnitID * @param customIndex integer? [0..MAX_CUSTOM_COLORS) index into custom palette, or nil to reset to team color * @return nil */ @@ -3038,7 +3040,7 @@ int LuaUnsyncedCtrl::SetUnitPaletteIndex(lua_State* L) * Sets a custom color for a feature from the palette. Custom assignments are permanent * until explicitly reset by passing nil, and are NOT affected by team changes. * @function Spring.SetFeaturePaletteIndex - * @param featureID integer + * @param featureID FeatureID * @param customIndex integer? [0..MAX_CUSTOM_COLORS) index into custom palette, or nil to reset to team color * @return nil */ @@ -3163,8 +3165,8 @@ int LuaUnsyncedCtrl::SetCustomCommandDrawData(lua_State* L) /*** @function Spring.WarpMouse - * @param x number - * @param y number + * @param x integer + * @param y integer * @return nil */ int LuaUnsyncedCtrl::WarpMouse(lua_State* L) @@ -3432,8 +3434,8 @@ int LuaUnsyncedCtrl::Quit(lua_State* L) /*** * * @function Spring.SetUnitGroup - * @param unitID integer - * @param groupID integer the group number to be assigned, or -1 for deassignment + * @param unitID UnitID + * @param groupID GroupID the group number to be assigned, or -1 for deassignment * @return nil */ int LuaUnsyncedCtrl::SetUnitGroup(lua_State* L) @@ -3530,7 +3532,7 @@ static bool CanGiveOrders(const lua_State* L) * * @function Spring.GiveOrder * @param cmdID CMD|integer The command ID. - * @param params CreateCommandParams Parameters for the given command. + * @param params CreateCommandParams? Parameters for the given command. * @param options CreateCommandOptions? * @param timeout integer? Absolute frame number. The command will be discarded after this frame. Only respected by mobile units. * @return boolean @@ -3553,7 +3555,7 @@ int LuaUnsyncedCtrl::GiveOrder(lua_State* L) * Give order to specific unit. * * @function Spring.GiveOrderToUnit - * @param unitID integer + * @param unitID UnitID * @param cmdID CMD|integer The command ID. * @param params CreateCommandParams? Parameters for the given command. * @param options CreateCommandOptions? @@ -3587,7 +3589,7 @@ int LuaUnsyncedCtrl::GiveOrderToUnit(lua_State* L) * Give order to multiple units, specified by table keys. * * @function Spring.GiveOrderToUnitMap - * @param unitMap table A table with unit IDs as keys. + * @param unitMap table A table with unit IDs as keys. * @param cmdID CMD|integer The command ID. * @param params CreateCommandParams? Parameters for the given command. * @param options CreateCommandOptions? @@ -3621,7 +3623,7 @@ int LuaUnsyncedCtrl::GiveOrderToUnitMap(lua_State* L) * Give order to an array of units. * * @function Spring.GiveOrderToUnitArray - * @param unitIDs integer[] Array of unit IDs. + * @param unitIDs UnitID[] Array of unit IDs. * @param cmdID CMD|integer The command ID. * @param params CreateCommandParams? Parameters for the given command. * @param options CreateCommandOptions? @@ -3653,7 +3655,7 @@ int LuaUnsyncedCtrl::GiveOrderToUnitArray(lua_State* L) /*** * * @function Spring.GiveOrderArrayToUnit - * @param unitID integer Unit ID. + * @param unitID UnitID Unit ID. * @param commands CreateCommand[] * @return boolean ordersGiven `true` if any orders were sent, otherwise `false`. */ @@ -3687,7 +3689,7 @@ int LuaUnsyncedCtrl::GiveOrderArrayToUnit(lua_State* L) /*** * * @function Spring.GiveOrderArrayToUnitMap - * @param unitMap table A table with unit IDs as keys. + * @param unitMap table A table with unit IDs as keys. * @param commands CreateCommand[] * @return boolean ordersGiven `true` if any orders were sent, otherwise `false`. */ @@ -3720,7 +3722,7 @@ int LuaUnsyncedCtrl::GiveOrderArrayToUnitMap(lua_State* L) /*** * @function Spring.GiveOrderArrayToUnitArray - * @param unitIDs integer[] Array of unit IDs. + * @param unitIDs UnitID[] Array of unit IDs. * @param commands CreateCommand[] * @param pairwise boolean? (Default: `false`) When `false`, assign all commands to each unit. * @@ -3763,7 +3765,7 @@ int LuaUnsyncedCtrl::GiveOrderArrayToUnitArray(lua_State* L) /*** * * @function Spring.SetBuildSpacing - * @param spacing number + * @param spacing integer * @return nil */ int LuaUnsyncedCtrl::SetBuildSpacing(lua_State* L) @@ -3799,7 +3801,7 @@ int LuaUnsyncedCtrl::SetBuildFacing(lua_State* L) /*** @function Spring.SendLuaUIMsg * @param message string - * @param mode string "s"/"specs" | "a"/"allies" + * @param mode string? "s"/"specs" | "a"/"allies" * @return nil */ int LuaUnsyncedCtrl::SendLuaUIMsg(lua_State* L) @@ -3915,7 +3917,7 @@ int LuaUnsyncedCtrl::SetShareLevel(lua_State* L) * * @function Spring.ShareResources * - * @param teamID integer + * @param teamID TeamID * @param units string * @return nil */ @@ -3924,7 +3926,7 @@ int LuaUnsyncedCtrl::SetShareLevel(lua_State* L) * * @function Spring.ShareResources * - * @param teamID integer + * @param teamID TeamID * @param resource string metal | energy * @param amount number * @return nil @@ -4005,7 +4007,7 @@ int LuaUnsyncedCtrl::SetLastMessagePosition(lua_State* L) * @param z number * @param text string? (Default: `""`) * @param localOnly boolean? - * @param playerID number? Local labels pretend they are from this player + * @param playerID PlayerID? Local labels pretend they are from this player * @return nil */ int LuaUnsyncedCtrl::MarkerAddPoint(lua_State* L) @@ -4037,7 +4039,7 @@ int LuaUnsyncedCtrl::MarkerAddPoint(lua_State* L) * @param y2 number * @param z2 number * @param localOnly boolean? (Default: `false`) - * @param playerId number? + * @param playerId PlayerID? * @return nil */ int LuaUnsyncedCtrl::MarkerAddLine(lua_State* L) @@ -4072,7 +4074,7 @@ int LuaUnsyncedCtrl::MarkerAddLine(lua_State* L) * @param z number * @param unused nil This argument is ignored. * @param localOnly boolean? (Default: `false`) do not issue a network message, erase only for the current player - * @param playerId number? when not specified it uses the issuer playerId + * @param playerId PlayerID? when not specified it uses the issuer playerId * @param alwaysErase boolean? (Default: `false`) erase any marker when `localOnly` and current player is spectating. Allows spectators to erase players markers locally * @return nil */ @@ -4109,12 +4111,12 @@ int LuaUnsyncedCtrl::MarkerErasePosition(lua_State* L) /*** * @class AtmosphereParams * @x_helper - * @field fogStart number - * @field fogEnd number - * @field sunColor rgba - * @field skyColor rgba - * @field cloudColor rgba - * @field skyAxisAngle xyzw rotation axis and angle in radians of skybox orientation + * @field fogStart number? + * @field fogEnd number? + * @field sunColor rgba? + * @field skyColor rgba? + * @field cloudColor rgba? + * @field skyAxisAngle xyzw? rotation axis and angle in radians of skybox orientation */ /*** Set atmosphere parameters @@ -4199,9 +4201,30 @@ int LuaUnsyncedCtrl::SetSunDirection(lua_State* L) auto dir = float3(luaL_checkfloat(L, 1), luaL_checkfloat(L, 2), luaL_checkfloat(L, 3)); auto intensity = luaL_optfloat(L, 4, 1.0f); // seems broken atm, only toggles shadows off when set to 0 ISky::GetSky()->GetLight()->SetLightDir(float4(dir.SafeNormalize(), intensity)); + sunLighting->SetUpdated(); + eventHandler.SunChanged(); return 0; } +/*** + * @class SunLightingParams + * + * The parameter table for sun lighting + * @see Spring.SetSunLighting + * + * @field specularExponent number? + * @field groundShadowDensity number? + * @field modelShadowDensity number? + * @field groundAmbientColor rgba? + * @field groundDiffuseColor rgba? + * @field groundSpecularColor rgba? + * @field unitAmbientColor rgba? + * @field modelAmbientColor rgba? + * @field unitDiffuseColor rgba? + * @field modelDiffuseColor rgba? + * @field unitSpecularColor rgba? + * @field modelSpecularColor rgba? + */ /*** * Modify sun lighting parameters. @@ -4211,7 +4234,7 @@ int LuaUnsyncedCtrl::SetSunDirection(lua_State* L) * ``` * * @function Spring.SetSunLighting - * @param params { groundAmbientColor: rgb, groundDiffuseColor: rgb } + * @param params SunLightingParams */ int LuaUnsyncedCtrl::SetSunLighting(lua_State* L) { @@ -4254,11 +4277,11 @@ int LuaUnsyncedCtrl::SetSunLighting(lua_State* L) * * @class MapRenderingParams * @x_helper - * @field splatTexMults rgba - * @field splatTexScales rgba - * @field voidWater boolean - * @field voidGround boolean - * @field splatDetailNormalDiffuseAlpha boolean + * @field splatTexMults rgba? + * @field splatTexScales rgba? + * @field voidWater boolean? + * @field voidGround boolean? + * @field splatDetailNormalDiffuseAlpha boolean? */ @@ -4367,7 +4390,7 @@ int LuaUnsyncedCtrl::ForceTesselationUpdate(lua_State* L) /*** @function Spring.SendSkirmishAIMessage - * @param aiTeam number + * @param aiTeam TeamID * @param message string * @return boolean? ai_processed */ @@ -4572,44 +4595,44 @@ int LuaUnsyncedCtrl::SetVideoCapturingTimeOffset(lua_State* L) * * @class WaterParams * @x_helper - * @field absorb rgb - * @field baseColor rgb - * @field minColor rgb - * @field surfaceColor rgb - * @field diffuseColor rgb - * @field specularColor rgb - * @field planeColor rgb - * @field texture string file - * @field foamTexture string file - * @field normalTexture string file - * @field damage number - * @field repeatX number - * @field repeatY number - * @field surfaceAlpha number - * @field ambientFactor number - * @field diffuseFactor number - * @field specularFactor number - * @field specularPower number - * @field fresnelMin number - * @field fresnelMax number - * @field fresnelPower number - * @field reflectionDistortion number - * @field blurBase number - * @field blurExponent number - * @field perlinStartFreq number - * @field perlinLacunarity number - * @field perlinAmplitude number - * @field windSpeed number - * @field waveOffsetFactor number - * @field waveLength number - * @field waveFoamDistortion number - * @field waveFoamIntensity number - * @field causticsResolution number - * @field causticsStrength number - * @field numTiles integer - * @field shoreWaves boolean - * @field forceRendering boolean - * @field hasWaterPlane boolean + * @field absorb rgb? + * @field baseColor rgb? + * @field minColor rgb? + * @field surfaceColor rgb? + * @field diffuseColor rgb? + * @field specularColor rgb? + * @field planeColor rgb? + * @field texture string? file + * @field foamTexture string? file + * @field normalTexture string? file + * @field damage number? + * @field repeatX number? + * @field repeatY number? + * @field surfaceAlpha number? + * @field ambientFactor number? + * @field diffuseFactor number? + * @field specularFactor number? + * @field specularPower number? + * @field fresnelMin number? + * @field fresnelMax number? + * @field fresnelPower number? + * @field reflectionDistortion number? + * @field blurBase number? + * @field blurExponent number? + * @field perlinStartFreq number? + * @field perlinLacunarity number? + * @field perlinAmplitude number? + * @field windSpeed number? + * @field waveOffsetFactor number? + * @field waveLength number? + * @field waveFoamDistortion number? + * @field waveFoamIntensity number? + * @field causticsResolution number? + * @field causticsStrength number? + * @field numTiles integer? + * @field shoreWaves boolean? + * @field forceRendering boolean? + * @field hasWaterPlane boolean? */ /*** @@ -4838,7 +4861,7 @@ int LuaUnsyncedCtrl::SetWaterParams(lua_State* L) * Allow the engine to load the unit's model (and texture) in a background thread. * Wreckages and buildOptions of a unit are automatically preloaded. * - * @param unitDefID integer + * @param unitDefID UnitDefID * @return nil */ int LuaUnsyncedCtrl::PreloadUnitDefModel(lua_State* L) { @@ -4854,7 +4877,7 @@ int LuaUnsyncedCtrl::PreloadUnitDefModel(lua_State* L) { /*** @function Spring.PreloadFeatureDefModel * - * @param featureDefID integer + * @param featureDefID FeatureDefID * @return nil */ int LuaUnsyncedCtrl::PreloadFeatureDefModel(lua_State* L) { @@ -4886,7 +4909,7 @@ int LuaUnsyncedCtrl::PreloadSoundItem(lua_State* L) /*** @function Spring.LoadModelTextures * - * @param modelName string + * @param modelName string? * @return boolean? success */ int LuaUnsyncedCtrl::LoadModelTextures(lua_State* L) @@ -4923,7 +4946,7 @@ int LuaUnsyncedCtrl::LoadModelTextures(lua_State* L) /*** * * @function Spring.CreateGroundDecal - * @return nil|number decalID + * @return DecalID? decalID */ int LuaUnsyncedCtrl::CreateGroundDecal(lua_State* L) { @@ -4939,7 +4962,7 @@ int LuaUnsyncedCtrl::CreateGroundDecal(lua_State* L) /*** * * @function Spring.DestroyGroundDecal - * @param decalID integer + * @param decalID DecalID * @return boolean delSuccess */ int LuaUnsyncedCtrl::DestroyGroundDecal(lua_State* L) @@ -4952,7 +4975,7 @@ int LuaUnsyncedCtrl::DestroyGroundDecal(lua_State* L) /*** * * @function Spring.SetGroundDecalPosAndDims - * @param decalID integer + * @param decalID DecalID * @param midPosX number? (Default: currMidPosX) * @param midPosZ number? (Default: currMidPosZ) * @param sizeX number? (Default: currSizeX) @@ -5005,7 +5028,7 @@ int LuaUnsyncedCtrl::SetGroundDecalPosAndDims(lua_State* L) * * Use for non-rectangular decals * - * @param decalID integer + * @param decalID DecalID * @param posTL xz? (Default: currPosTL) * @param posTR xz? (Default: currPosTR) * @param posBR xz? (Default: currPosBR) @@ -5038,7 +5061,7 @@ int LuaUnsyncedCtrl::SetGroundDecalQuadPosAndHeight(lua_State* L) /*** * * @function Spring.SetGroundDecalRotation - * @param decalID integer + * @param decalID DecalID * @param rot number? (Default: random) in radians * @return boolean decalSet */ @@ -5060,7 +5083,7 @@ int LuaUnsyncedCtrl::SetGroundDecalRotation(lua_State* L) /*** * * @function Spring.SetGroundDecalTexture - * @param decalID integer + * @param decalID DecalID * @param textureName string The texture has to be on the atlas which seems to mean it's defined as an explosion, unit tracks, or building plate decal on some unit already (no arbitrary textures) * @param isMainTex boolean? (Default: `true`) If false, it sets the normals/glow map * @return nil|boolean decalSet @@ -5076,7 +5099,7 @@ int LuaUnsyncedCtrl::SetGroundDecalTexture(lua_State* L) /*** * * @function Spring.SetGroundDecalTextureParams - * @param decalID integer + * @param decalID DecalID * @param texWrapDistance number? (Default: currTexWrapDistance) if non-zero sets the mode to repeat the texture along the left-right direction of the decal every texWrapFactor elmos * @param texTraveledDistance number? (Default: currTexTraveledDistance) shifts the texture repetition defined by texWrapFactor so the texture of a next line in the continuous multiline can start where the previous finished. For that it should collect all elmo lengths of the previously set multiline segments. * @return nil|boolean decalSet @@ -5100,7 +5123,7 @@ int LuaUnsyncedCtrl::SetGroundDecalTextureParams(lua_State* L) /*** * * @function Spring.SetGroundDecalAlpha - * @param decalID integer + * @param decalID DecalID * @param alpha number? (Default: currAlpha) Between 0 and 1 * @param alphaFalloff number? (Default: currAlphaFalloff) Between 0 and 1, per second * @return boolean decalSet @@ -5125,7 +5148,7 @@ int LuaUnsyncedCtrl::SetGroundDecalAlpha(lua_State* L) * @function Spring.SetGroundDecalNormal * Sets projection cube normal to orient in 3D space. * In case the normal (0,0,0) then normal is picked from the terrain - * @param decalID integer + * @param decalID DecalID * @param normalX number? (Default: `0`) * @param normalY number? (Default: `0`) * @param normalZ number? (Default: `0`) @@ -5157,7 +5180,7 @@ int LuaUnsyncedCtrl::SetGroundDecalNormal(lua_State* L) * @function Spring.SetGroundDecalTint * Sets the tint of the ground decal. Color = 2 * textureColor * tintColor * Respectively a color of (0.5, 0.5, 0.5, 0.5) is effectively no tint - * @param decalID integer + * @param decalID DecalID * @param tintColR number? (Default: curTintColR) * @param tintColG number? (Default: curTintColG) * @param tintColB number? (Default: curTintColB) @@ -5188,7 +5211,7 @@ int LuaUnsyncedCtrl::SetGroundDecalTint(lua_State* L) * * @function Spring.SetGroundDecalMisc * Sets varios secondary parameters of a decal - * @param decalID integer + * @param decalID DecalID * @param dotElimExp number? (Default: curValue) pow(max(dot(decalProjVector, SurfaceNormal), 0.0), dotElimExp), used to reduce decal artifacts on surfaces non-collinear with the projection vector * @param refHeight number? (Default: curValue) * @param minHeight number? (Default: curValue) @@ -5220,7 +5243,7 @@ int LuaUnsyncedCtrl::SetGroundDecalMisc(lua_State* L) * * Use separate min and max for "gradient" style decals such as tank tracks * - * @param decalID integer + * @param decalID DecalID * @param creationFrameMin number? (Default: currCreationFrameMin) * @param creationFrameMax number? (Default: currCreationFrameMax) * @return boolean decalSet @@ -5246,7 +5269,7 @@ int LuaUnsyncedCtrl::SetGroundDecalCreationFrame(lua_State* L) * * Set decal glow parameters * - * @param decalID integer + * @param decalID DecalID * @param glow number? Between 0 and 1 (Default: currGlow) * @param glowFalloff number? Between 0 and 1, per second (Default: currGlowFallOff) * @return boolean decalSet @@ -5272,7 +5295,7 @@ int LuaUnsyncedCtrl::SetGroundDecalGlowParams(lua_State* L) * * Set decal user data. Useful in conjunction with custom decal shaders * - * @param decalID integer + * @param decalID DecalID * @param udQuad integer vec4 index, must be within [0;1] for now * @param x number? Any valid Lua float number (Default: current data) * @param y number? Any valid Lua float number (Default: current data) @@ -5311,10 +5334,10 @@ int LuaUnsyncedCtrl::SetGroundDecalUserData(lua_State* L) /*** * * @function Spring.SDLSetTextInputRect - * @param x number - * @param y number - * @param width number - * @param height number + * @param x integer + * @param y integer + * @param width integer + * @param height integer * @return nil */ int LuaUnsyncedCtrl::SDLSetTextInputRect(lua_State* L) @@ -5359,11 +5382,11 @@ int LuaUnsyncedCtrl::SDLStopTextInput(lua_State* L) /*** * * @function Spring.SetWindowGeometry - * @param displayIndex number - * @param winRelPosX number - * @param winRelPosY number - * @param winSizeX number - * @param winSizeY number + * @param displayIndex integer + * @param winRelPosX integer + * @param winRelPosY integer + * @param winSizeX integer + * @param winSizeY integer * @param fullScreen boolean * @param borderless boolean * @return nil @@ -5471,6 +5494,7 @@ int LuaUnsyncedCtrl::Start(lua_State* L) * Note: *.ico images are not supported. * * @param iconFileName string + * @param autoFree boolean? * @return nil */ int LuaUnsyncedCtrl::SetWMIcon(lua_State* L) @@ -5555,7 +5579,7 @@ int LuaUnsyncedCtrl::SetClipboard(lua_State* L) * wantYield = wantYield and Spring.Yield() * end * - * @return boolean when true caller should continue calling `Spring.Yield` during the widgets/gadgets load, when false it shouldn't call it any longer. + * @return boolean continueYielding when true caller should continue calling `Spring.Yield` during the widgets/gadgets load, when false it shouldn't call it any longer. */ int LuaUnsyncedCtrl::Yield(lua_State* L) { diff --git a/rts/Lua/LuaUnsyncedRead.cpp b/rts/Lua/LuaUnsyncedRead.cpp index f1615b8079b..2546c04ebea 100644 --- a/rts/Lua/LuaUnsyncedRead.cpp +++ b/rts/Lua/LuaUnsyncedRead.cpp @@ -75,6 +75,7 @@ #include "System/Sound/ISound.h" #include "System/Sound/ISoundChannels.h" #include "System/StringUtil.h" +#include "System/Sync/SyncChecker.h" #include "System/Misc/SpringTime.h" #include "System/ScopedResource.h" #include "System/Math/NURBS.h" @@ -93,6 +94,7 @@ #include + /****************************************************************************** * Callouts to get state * @@ -120,6 +122,7 @@ bool LuaUnsyncedRead::PushEntries(lua_State* L) REGISTER_LUA_CFUNC(GetGameSecondsInterpolated); REGISTER_LUA_CFUNC(GetLastUpdateSeconds); REGISTER_LUA_CFUNC(GetVideoCapturingMode); + REGISTER_LUA_CFUNC(GetPrevFrameSyncChecksum); REGISTER_LUA_CFUNC(GetNumDisplays); REGISTER_LUA_CFUNC(GetViewGeometry); @@ -483,7 +486,7 @@ static size_t PushSparseUnitTallyByDef(lua_State *const L, const T &v) * * @function Spring.IsReplay * - * @return boolean? isReplay + * @return boolean isReplay */ int LuaUnsyncedRead::IsReplay(lua_State* L) { @@ -607,29 +610,32 @@ int LuaUnsyncedRead::GetMenuName(lua_State* L) * @return number max_dt * @return number time_pct * @return number peak_pct - * @return table? frameData Table where key is the frame index and value is duration. + * @return table? frameData Table where key is the frame index and value is duration. */ int LuaUnsyncedRead::GetProfilerTimeRecord(lua_State* L) { const CTimeProfiler::TimeRecord& record = CTimeProfiler::GetInstance().GetTimeRecord(lua_tostring(L, 1)); - int numRet = 5; + const bool wantFrameData = luaL_optboolean(L, 2, false); + lua_pushnumber(L, record.total.toMilliSecsf()); lua_pushnumber(L, record.current.toMilliSecsf()); lua_pushnumber(L, record.stats.x); // max-dt lua_pushnumber(L, record.stats.y); // time-% lua_pushnumber(L, record.stats.z); // peak-% - if (luaL_optboolean(L, 2, false)) { - for (size_t i = 0; i < record.frames.size(); i++) { - lua_pushnumber(L, i + 1); // key - lua_pushnumber(L, record.frames[i].toMilliSecsf()); // val - lua_rawset(L, -3); - } - ++numRet; + if (!wantFrameData) + return 5; + + lua_createtable(L, record.frames.size(), 0); + + for (size_t i = 0; i < record.frames.size(); i++) { + lua_pushnumber(L, i + 1); // key + lua_pushnumber(L, record.frames[i].toMilliSecsf()); // val + lua_rawset(L, -3); } - return numRet; + return 6; } /*** @@ -865,7 +871,7 @@ int LuaUnsyncedRead::DiffTimers(lua_State* L) * * @function Spring.GetNumDisplays * - * @return number numDisplays as returned by `SDL_GetNumVideoDisplays` + * @return integer numDisplays as returned by `SDL_GetNumVideoDisplays` */ int LuaUnsyncedRead::GetNumDisplays(lua_State* L) { @@ -878,10 +884,10 @@ int LuaUnsyncedRead::GetNumDisplays(lua_State* L) * * @function Spring.GetViewGeometry * - * @return number viewSizeX in px - * @return number viewSizeY in px - * @return number viewPosX offset from leftmost screen left border in px - * @return number viewPosY offset from bottommost screen bottom border in px + * @return integer viewSizeX in px + * @return integer viewSizeY in px + * @return integer viewPosX offset from leftmost screen left border in px + * @return integer viewPosY offset from bottommost screen bottom border in px */ int LuaUnsyncedRead::GetViewGeometry(lua_State* L) { @@ -897,10 +903,10 @@ int LuaUnsyncedRead::GetViewGeometry(lua_State* L) * * @function Spring.GetDualViewGeometry * - * @return number dualViewSizeX in px - * @return number dualViewSizeY in px - * @return number dualViewPosX offset from leftmost screen left border in px - * @return number dualViewPosY offset from bottommost screen bottom border in px + * @return integer dualViewSizeX in px + * @return integer dualViewSizeY in px + * @return integer dualViewPosX offset from leftmost screen left border in px + * @return integer dualViewPosY offset from bottommost screen bottom border in px */ int LuaUnsyncedRead::GetDualViewGeometry(lua_State* L) { @@ -916,14 +922,14 @@ int LuaUnsyncedRead::GetDualViewGeometry(lua_State* L) * * @function Spring.GetWindowGeometry * - * @return number winSizeX in px - * @return number winSizeY in px - * @return number winPosX in px - * @return number winPosY in px - * @return number windowBorderTop in px - * @return number windowBorderLeft in px - * @return number windowBorderBottom in px - * @return number windowBorderRight in px + * @return integer winSizeX in px + * @return integer winSizeY in px + * @return integer winPosX in px + * @return integer winPosY in px + * @return integer windowBorderTop in px + * @return integer windowBorderLeft in px + * @return integer windowBorderBottom in px + * @return integer windowBorderRight in px */ int LuaUnsyncedRead::GetWindowGeometry(lua_State* L) { @@ -945,10 +951,10 @@ int LuaUnsyncedRead::GetWindowGeometry(lua_State* L) /*** Get main window display mode * * @function Spring.GetWindowDisplayMode - * @return number width in px - * @return number height in px - * @return number bits per pixel - * @return number refresh rate in Hz + * @return integer width in px + * @return integer height in px + * @return integer bits per pixel + * @return integer refresh rate in Hz */ int LuaUnsyncedRead::GetWindowDisplayMode(lua_State* L) { @@ -969,21 +975,21 @@ int LuaUnsyncedRead::GetWindowDisplayMode(lua_State* L) * * @function Spring.GetScreenGeometry * - * @param displayIndex number? (Default: `-1`) + * @param displayIndex integer? (Default: `-1`) * @param queryUsable boolean? (Default: `false`) * - * @return number screenSizeX in px - * @return number screenSizeY in px - * @return number screenPosX in px - * @return number screenPosY in px - * @return number windowBorderTop in px - * @return number windowBorderLeft in px - * @return number windowBorderBottom in px - * @return number windowBorderRight in px - * @return number? screenUsableSizeX in px - * @return number? screenUsableSizeY in px - * @return number? screenUsablePosX in px - * @return number? screenUsablePosY in px + * @return integer screenSizeX in px + * @return integer screenSizeY in px + * @return integer screenPosX in px + * @return integer screenPosY in px + * @return integer windowBorderTop in px + * @return integer windowBorderLeft in px + * @return integer windowBorderBottom in px + * @return integer windowBorderRight in px + * @return integer? screenUsableSizeX in px + * @return integer? screenUsableSizeY in px + * @return integer? screenUsablePosX in px + * @return integer? screenUsablePosY in px */ int LuaUnsyncedRead::GetScreenGeometry(lua_State* L) { @@ -1026,10 +1032,10 @@ int LuaUnsyncedRead::GetScreenGeometry(lua_State* L) * * @function Spring.GetMiniMapGeometry * - * @return number minimapPosX in px - * @return number minimapPosY in px - * @return number minimapSizeX in px - * @return number minimapSizeY in px + * @return integer minimapPosX in px + * @return integer minimapPosY in px + * @return integer minimapSizeX in px + * @return integer minimapSizeY in px * @return boolean minimized * @return boolean maximized */ @@ -1052,7 +1058,7 @@ int LuaUnsyncedRead::GetMiniMapGeometry(lua_State* L) /*** Get minimap rotation * * @function Spring.GetMiniMapRotation - * @return number amount in radians + * @return number rotation in radians */ int LuaUnsyncedRead::GetMiniMapRotation(lua_State* L) { @@ -1137,8 +1143,8 @@ int LuaUnsyncedRead::GetDrawSelectionInfo(lua_State* L) * * @function Spring.IsAboveMiniMap * - * @param x number - * @param y number + * @param x integer + * @param y integer * * @return boolean isAbove */ @@ -1169,8 +1175,8 @@ int LuaUnsyncedRead::IsAboveMiniMap(lua_State* L) * * @function Spring.GetDrawFrame * - * @return number low_16bit - * @return number high_16bit + * @return integer low_16bit + * @return integer high_16bit */ int LuaUnsyncedRead::GetDrawFrame(lua_State* L) { @@ -1199,10 +1205,12 @@ int LuaUnsyncedRead::GetFrameTimeOffset(lua_State* L) } /*** Gets game time for drawing purposes + * + * @function Spring.GetGameSecondsInterpolated * * Returns the game time, taking the interpolated draw frame into account. * - * @return number game time in seconds + * @return number time in seconds */ int LuaUnsyncedRead::GetGameSecondsInterpolated(lua_State* L) { @@ -1236,6 +1244,35 @@ int LuaUnsyncedRead::GetVideoCapturingMode(lua_State* L) } +/*** + * + * Returns the engine's sync checksum for the previous simframe, + * useful for testing. The returned string is NOT convertible to + * a number within Lua. + * + * Returns a dummy value if `Platform.hasSyncChecksums` is false, + * or if no frames were processed yet. + * + * @function Spring.GetPrevFrameSyncChecksum + * + * @return string checksum + */ +int LuaUnsyncedRead::GetPrevFrameSyncChecksum(lua_State* L) +{ +#ifdef SYNCCHECK + unsigned checksum = CSyncChecker::GetPrevChecksum(); +#else + unsigned checksum = 0; +#endif + + char buf[9]; + snprintf(buf, sizeof(buf), "%08x", checksum); + lua_pushstring(L, buf); + + return 1; +} + + /****************************************************************************** * Unit attributes * @section unitattributes @@ -1245,7 +1282,7 @@ int LuaUnsyncedRead::GetVideoCapturingMode(lua_State* L) /*** * * @function Spring.IsUnitAllied - * @param unitID integer + * @param unitID UnitID * @return boolean? isAllied nil with unitID cannot be parsed */ int LuaUnsyncedRead::IsUnitAllied(lua_State* L) @@ -1269,7 +1306,7 @@ int LuaUnsyncedRead::IsUnitAllied(lua_State* L) /*** * * @function Spring.IsUnitSelected - * @param unitID integer + * @param unitID UnitID * @return boolean? isSelected nil when unitID cannot be parsed */ int LuaUnsyncedRead::IsUnitSelected(lua_State* L) @@ -1287,7 +1324,7 @@ int LuaUnsyncedRead::IsUnitSelected(lua_State* L) /*** * * @function Spring.GetUnitLuaDraw - * @param unitID integer + * @param unitID UnitID * @return boolean? draw nil when unitID cannot be parsed */ int LuaUnsyncedRead::GetUnitLuaDraw(lua_State* L) @@ -1298,8 +1335,8 @@ int LuaUnsyncedRead::GetUnitLuaDraw(lua_State* L) /*** * * @function Spring.GetUnitNoDraw - * @param unitID integer - * @return boolean? nil when unitID cannot be parsed + * @param unitID UnitID + * @return boolean? noDraw `nil` when unitID cannot be parsed */ int LuaUnsyncedRead::GetUnitNoDraw(lua_State* L) { @@ -1309,8 +1346,8 @@ int LuaUnsyncedRead::GetUnitNoDraw(lua_State* L) /*** * * @function Spring.GetUnitEngineDrawMask - * @param unitID integer - * @return boolean? nil when unitID cannot be parsed + * @param unitID UnitID + * @return boolean? drawMask `nil` when unitID cannot be parsed */ int LuaUnsyncedRead::GetUnitEngineDrawMask(lua_State* L) { @@ -1320,8 +1357,8 @@ int LuaUnsyncedRead::GetUnitEngineDrawMask(lua_State* L) /*** * * @function Spring.GetUnitAlwaysUpdateMatrix - * @param unitID integer - * @return boolean? nil when unitID cannot be parsed + * @param unitID UnitID + * @return boolean? alwaysUpdateMatrix `nil` when unitID cannot be parsed */ int LuaUnsyncedRead::GetUnitAlwaysUpdateMatrix(lua_State* L) { @@ -1337,8 +1374,8 @@ int LuaUnsyncedRead::GetUnitAlwaysUpdateMatrix(lua_State* L) /*** * * @function Spring.GetUnitDrawFlag - * @param unitID integer - * @return number? nil when unitID cannot be parsed + * @param unitID UnitID + * @return number? drawFlag `nil` when unitID cannot be parsed */ int LuaUnsyncedRead::GetUnitDrawFlag(lua_State* L) { @@ -1354,8 +1391,8 @@ int LuaUnsyncedRead::GetUnitDrawFlag(lua_State* L) /*** * * @function Spring.GetUnitNoMinimap - * @param unitID integer - * @return boolean? nil when unitID cannot be parsed + * @param unitID UnitID + * @return boolean? noMinimap `nil` when unitID cannot be parsed */ int LuaUnsyncedRead::GetUnitNoMinimap(lua_State* L) { @@ -1372,7 +1409,7 @@ int LuaUnsyncedRead::GetUnitNoMinimap(lua_State* L) * Check if a unit is not allowed to be added to a group by a player. * * @function Spring.GetUnitNoGroup - * @param unitID integer + * @param unitID UnitID * @return boolean? noGroup `true` if the unit is not allowed to be added to a group, `false` if it is allowed to be added to a group, or `nil` when `unitID` is not valid. */ int LuaUnsyncedRead::GetUnitNoGroup(lua_State* L) @@ -1389,7 +1426,7 @@ int LuaUnsyncedRead::GetUnitNoGroup(lua_State* L) /*** * * @function Spring.GetUnitNoSelect - * @param unitID integer + * @param unitID UnitID * @return boolean? noSelect `nil` when `unitID` cannot be parsed. */ int LuaUnsyncedRead::GetUnitNoSelect(lua_State* L) @@ -1407,7 +1444,7 @@ int LuaUnsyncedRead::GetUnitNoSelect(lua_State* L) /*** * * @function Spring.UnitIconGetDraw - * @param unitID integer + * @param unitID UnitID * @return boolean? drawIcon * `true` if icon is being drawn, `nil` when unitID is invalid, otherwise `false`. */ @@ -1498,9 +1535,9 @@ namespace Impl { /*** Get unit icon data * * @function Spring.GetUnitIconData - * @param unitID number + * @param unitID UnitID * @param fullData boolean? (Default: false) Whether additional information about the icon is returned, otherwise only `name` and `atlasTexCoords` are returned - * @return IconData iconData + * @return IconData? `nil` if unit is not found or unit currentIconIndex is invalid * @see Spring.GetIconData */ int LuaUnsyncedRead::GetUnitIconData(lua_State* L) @@ -1520,7 +1557,7 @@ int LuaUnsyncedRead::GetUnitIconData(lua_State* L) /*** Get unit icon name * * @function Spring.GetUnitIcon - * @param unitID number + * @param unitID UnitID * @return string iconName */ int LuaUnsyncedRead::GetUnitIcon(lua_State* L) @@ -1544,7 +1581,7 @@ int LuaUnsyncedRead::GetUnitIcon(lua_State* L) * @function Spring.GetIconData * @param iconName string * @param fullData boolean? (Default: false) Whether additional information about the icon is returned, otherwise only `name` and `atlasTexCoords` are returned - * @return IconData iconData + * @return IconData? `nil` if iconName lookup fails * @see Spring.GetUnitIconData */ int LuaUnsyncedRead::GetIconData(lua_State* L) @@ -1591,16 +1628,16 @@ int LuaUnsyncedRead::GetAllIconDataArray(lua_State* L) /*** * * @function Spring.GetUnitSelectionVolumeData - * @param unitID integer + * @param unitID UnitID * @return number? scaleX nil when unitID cannot be parsed * @return number scaleY * @return number scaleZ * @return number offsetX * @return number offsetY * @return number offsetZ - * @return number volumeType - * @return number useContHitTest - * @return number getPrimaryAxis + * @return integer volumeType + * @return integer useContHitTest + * @return integer getPrimaryAxis * @return boolean ignoreHits */ int LuaUnsyncedRead::GetUnitSelectionVolumeData(lua_State* L) @@ -1618,8 +1655,8 @@ int LuaUnsyncedRead::GetUnitSelectionVolumeData(lua_State* L) /*** * * @function Spring.GetFeatureLuaDraw - * @param featureID integer - * @return boolean? nil when featureID cannot be parsed + * @param featureID FeatureID + * @return boolean? luaDraw `nil` when featureID cannot be parsed */ int LuaUnsyncedRead::GetFeatureLuaDraw(lua_State* L) { @@ -1629,8 +1666,8 @@ int LuaUnsyncedRead::GetFeatureLuaDraw(lua_State* L) /*** * * @function Spring.GetFeatureNoDraw - * @param featureID integer - * @return boolean? nil when featureID cannot be parsed + * @param featureID FeatureID + * @return boolean? noDraw `nil` when featureID cannot be parsed */ int LuaUnsyncedRead::GetFeatureNoDraw(lua_State* L) { @@ -1640,8 +1677,8 @@ int LuaUnsyncedRead::GetFeatureNoDraw(lua_State* L) /*** * * @function Spring.GetFeatureEngineDrawMask - * @param featureID integer - * @return boolean? nil when featureID cannot be parsed + * @param featureID FeatureID + * @return boolean? drawMask `nil` when featureID cannot be parsed */ int LuaUnsyncedRead::GetFeatureEngineDrawMask(lua_State* L) { @@ -1651,8 +1688,8 @@ int LuaUnsyncedRead::GetFeatureEngineDrawMask(lua_State* L) /*** * * @function Spring.GetFeatureAlwaysUpdateMatrix - * @param featureID integer - * @return boolean? nil when featureID cannot be parsed + * @param featureID FeatureID + * @return boolean? alwaysUpdateMatrix `nil` when featureID cannot be parsed */ int LuaUnsyncedRead::GetFeatureAlwaysUpdateMatrix(lua_State* L) { @@ -1668,8 +1705,8 @@ int LuaUnsyncedRead::GetFeatureAlwaysUpdateMatrix(lua_State* L) /*** * * @function Spring.GetFeatureDrawFlag - * @param featureID integer - * @return number? nil when featureID cannot be parsed + * @param featureID FeatureID + * @return number? drawFlag `nil` when featureID cannot be parsed */ int LuaUnsyncedRead::GetFeatureDrawFlag(lua_State* L) { @@ -1685,16 +1722,16 @@ int LuaUnsyncedRead::GetFeatureDrawFlag(lua_State* L) /*** * * @function Spring.GetFeatureSelectionVolumeData - * @param featureID integer - * @return number? scaleX nil when unitID cannot be parsed + * @param featureID FeatureID + * @return number? scaleX nil when featureID cannot be parsed * @return number scaleY * @return number scaleZ * @return number offsetX * @return number offsetY * @return number offsetZ - * @return number volumeType - * @return number useContHitTest - * @return number getPrimaryAxis + * @return integer volumeType + * @return integer useContHitTest + * @return integer getPrimaryAxis * @return boolean ignoreHits */ int LuaUnsyncedRead::GetFeatureSelectionVolumeData(lua_State* L) @@ -1734,8 +1771,14 @@ static int GetObjectTransformMatrix(const CSolidObject* o, lua_State* L) /*** * * @function Spring.GetUnitTransformMatrix - * @param unitID integer - * @return number? m11 nil when unitID cannot be parsed + * @param unitID UnitID + * @return nil # when unitID cannot be parsed + */ +/*** + * + * @function Spring.GetUnitTransformMatrix + * @param unitID UnitID + * @return number m11 * @return number m12 * @return number m13 * @return number m14 @@ -1758,8 +1801,14 @@ int LuaUnsyncedRead::GetUnitTransformMatrix(lua_State* L) { return (GetObjectTra /*** * * @function Spring.GetFeatureTransformMatrix - * @param featureID integer - * @return number? m11 nil when featureID cannot be parsed + * @param featureID FeatureID + * @return nil # when featureID cannot be parsed + */ +/*** + * + * @function Spring.GetFeatureTransformMatrix + * @param featureID FeatureID + * @return number m11 * @return number m12 * @return number m13 * @return number m14 @@ -1788,7 +1837,7 @@ int LuaUnsyncedRead::GetFeatureTransformMatrix(lua_State* L) { return (GetObject /*** * * @function Spring.IsUnitInView - * @param unitID integer + * @param unitID UnitID * @return boolean? inView nil when unitID cannot be parsed */ int LuaUnsyncedRead::IsUnitInView(lua_State* L) @@ -1806,7 +1855,7 @@ int LuaUnsyncedRead::IsUnitInView(lua_State* L) /*** * * @function Spring.IsUnitVisible - * @param unitID integer + * @param unitID UnitID * @param radius number? unitRadius when not specified * @param checkIcon boolean * @return boolean? isVisible nil when unitID cannot be parsed @@ -1848,7 +1897,7 @@ int LuaUnsyncedRead::IsUnitVisible(lua_State* L) /*** * * @function Spring.IsUnitIcon - * @param unitID integer + * @param unitID UnitID * @return boolean? isUnitIcon nil when unitID cannot be parsed */ int LuaUnsyncedRead::IsUnitIcon(lua_State* L) @@ -1916,7 +1965,7 @@ int LuaUnsyncedRead::IsSphereInView(lua_State* L) /*** * * @function Spring.GetUnitViewPosition - * @param unitID integer + * @param unitID UnitID * @param midPos boolean? (Default: `false`) * @return number? x nil when unitID cannot be parsed * @return number y @@ -2011,10 +2060,10 @@ class CVisProjectileQuadDrawer: public CWorldObjectQuadDrawer { /*** * * @function Spring.GetVisibleUnits - * @param teamID integer? (Default: `-1`) + * @param teamID TeamID? (Default: `-1`) * @param radius number? (Default: `30`) * @param icons boolean? (Default: `true`) - * @return number[]? unitIDs + * @return UnitID[]? unitIDs */ int LuaUnsyncedRead::GetVisibleUnits(lua_State* L) { @@ -2109,11 +2158,11 @@ int LuaUnsyncedRead::GetVisibleUnits(lua_State* L) /*** * * @function Spring.GetVisibleFeatures - * @param teamID integer? (Default: `-1`) + * @param teamID TeamID? (Default: `-1`) * @param radius number? (Default: `30`) * @param icons boolean? (Default: `true`) * @param geos boolean? (Default: `true`) - * @return number[]? featureIDs + * @return FeatureID[]? featureIDs */ int LuaUnsyncedRead::GetVisibleFeatures(lua_State* L) { @@ -2190,11 +2239,11 @@ int LuaUnsyncedRead::GetVisibleFeatures(lua_State* L) /*** * * @function Spring.GetVisibleProjectiles - * @param allyTeamID integer? (Default: `-1`) + * @param allyTeamID AllyTeamID? (Default: `-1`) * @param addSyncedProjectiles boolean? (Default: `true`) * @param addWeaponProjectiles boolean? (Default: `true`) * @param addPieceProjectiles boolean? (Default: `true`) - * @return number[]? projectileIDs + * @return ProjectileID[]? projectileIDs */ int LuaUnsyncedRead::GetVisibleProjectiles(lua_State* L) { @@ -2366,7 +2415,7 @@ namespace { * @function Spring.GetRenderUnits * @param drawMask DrawMask (Default: `0`) Filter objects by their draw flags. * @param sendMask true Whether to send objects draw flags as second return - * @return integer[] featureIDs + * @return UnitID[] unitIDs * @return DrawFlag[] drawFlags */ @@ -2375,7 +2424,7 @@ namespace { * @function Spring.GetRenderUnits * @param drawMask DrawMask (Default: `0`) Filter objects by their draw flags. * @param sendMask false? Whether to send objects draw flags as second return - * @return integer[] featureIDs + * @return UnitID[] unitIDs */ int LuaUnsyncedRead::GetRenderUnits(lua_State* L) { @@ -2386,7 +2435,7 @@ int LuaUnsyncedRead::GetRenderUnits(lua_State* L) * @function Spring.GetRenderUnitsDrawFlagChanged * Gets a list of IDs of units that have had their draw flags changed, and the corresponding flags. * @param sendMask true Whether to send objects draw flags as second return. - * @return integer[] ids + * @return UnitID[] ids * @return DrawFlag[] unitDrawFlags */ @@ -2394,7 +2443,7 @@ int LuaUnsyncedRead::GetRenderUnits(lua_State* L) * @function Spring.GetRenderUnitsDrawFlagChanged * Gets a list of IDs of units that have had their draw flags changed, and the corresponding flags. * @param sendMask false? Whether to send objects draw flags as second return. - * @return integer[] ids + * @return UnitID[] ids */ int LuaUnsyncedRead::GetRenderUnitsDrawFlagChanged(lua_State* L) { @@ -2406,7 +2455,7 @@ int LuaUnsyncedRead::GetRenderUnitsDrawFlagChanged(lua_State* L) * @function Spring.GetRenderFeatures * @param drawMask DrawMask (Default: `0`) Filter objects by their draw flags. * @param sendMask true Whether to send objects draw flags as second return - * @return integer[] featureIDs + * @return FeatureID[] featureIDs * @return DrawFlag[] drawFlags */ @@ -2415,7 +2464,7 @@ int LuaUnsyncedRead::GetRenderUnitsDrawFlagChanged(lua_State* L) * @function Spring.GetRenderFeatures * @param drawMask DrawMask (Default: `0`) Filter objects by their draw flags. * @param sendMask false? Whether to send objects draw flags as second return - * @return integer[] featureIDs + * @return FeatureID[] featureIDs */ int LuaUnsyncedRead::GetRenderFeatures(lua_State* L) { @@ -2426,15 +2475,15 @@ int LuaUnsyncedRead::GetRenderFeatures(lua_State* L) * @function Spring.GetRenderFeaturesDrawFlagChanged * Gets a list of IDs of features that have had their draw flags changed, and the corresponding flags. * @param sendMask true Whether to send objects draw flags as second return. - * @return integer[] ids - * @return DrawFlag[] unitDrawFlags + * @return FeatureID[] ids + * @return DrawFlag[] featureDrawFlags */ /*** * @function Spring.GetRenderFeaturesDrawFlagChanged * Gets a list of IDs of features that have had their draw flags changed, and the corresponding flags. * @param sendMask false? Whether to send objects draw flags as second return. - * @return integer[] ids + * @return FeatureID[] ids */ int LuaUnsyncedRead::GetRenderFeaturesDrawFlagChanged(lua_State* L) { @@ -2468,8 +2517,8 @@ int LuaUnsyncedRead::ClearFeaturesPreviousDrawFlag(lua_State* L) * @param top number * @param right number * @param bottom number - * @param allegiance number? (Default: `-1`) teamID when > 0, when < 0 one of AllUnits = -1, MyUnits = -2, AllyUnits = -3, EnemyUnits = -4 - * @return number[]? unitIDs + * @param allegiance integer? (Default: `-1`) teamID when > 0, when < 0 one of AllUnits = -1, MyUnits = -2, AllyUnits = -3, EnemyUnits = -4 + * @return UnitID[]? unitIDs */ int LuaUnsyncedRead::GetUnitsInScreenRectangle(lua_State* L) { @@ -2553,7 +2602,7 @@ int LuaUnsyncedRead::GetUnitsInScreenRectangle(lua_State* L) * @param top number * @param right number * @param bottom number - * @return number[]? featureIDs + * @return FeatureID[]? featureIDs */ int LuaUnsyncedRead::GetFeaturesInScreenRectangle(lua_State* L) { @@ -2605,7 +2654,8 @@ int LuaUnsyncedRead::GetFeaturesInScreenRectangle(lua_State* L) /*** * * @function Spring.GetLocalPlayerID - * @return integer playerID + * @function Spring.GetMyPlayerID Alias of GetLocalPlayerID + * @return PlayerID playerID */ int LuaUnsyncedRead::GetLocalPlayerID(lua_State* L) { @@ -2617,7 +2667,8 @@ int LuaUnsyncedRead::GetLocalPlayerID(lua_State* L) /*** * * @function Spring.GetLocalTeamID - * @return integer teamID + * @function Spring.GetMyTeamID Alias of GetLocalTeamID + * @return TeamID teamID */ int LuaUnsyncedRead::GetLocalTeamID(lua_State* L) { @@ -2629,7 +2680,8 @@ int LuaUnsyncedRead::GetLocalTeamID(lua_State* L) /*** * * @function Spring.GetLocalAllyTeamID - * @return integer allyTeamID + * @function Spring.GetMyAllyTeamID Alias of GetLocalAllyTeamID + * @return AllyTeamID allyTeamID */ int LuaUnsyncedRead::GetLocalAllyTeamID(lua_State* L) { @@ -2660,7 +2712,7 @@ int LuaUnsyncedRead::GetSpectatingState(lua_State* L) /*** * * @function Spring.GetSelectedUnits - * @return number[] unitIDs + * @return UnitID[] unitIDs */ int LuaUnsyncedRead::GetSelectedUnits(lua_State* L) { @@ -2671,8 +2723,8 @@ int LuaUnsyncedRead::GetSelectedUnits(lua_State* L) /*** Get selected units aggregated by unitDefID * * @function Spring.GetSelectedUnitsSorted - * @return table where keys are unitDefIDs and values are unitIDs - * @return integer the number of unitDefIDs + * @return table unitsIDs + * @return integer countDefs the number of unitDefIDs */ int LuaUnsyncedRead::GetSelectedUnitsSorted(lua_State* L) { @@ -2687,8 +2739,8 @@ int LuaUnsyncedRead::GetSelectedUnitsSorted(lua_State* L) * * @function Spring.GetSelectedUnitsCounts * - * @return table unitsCounts where keys are unitDefIDs and values are counts - * @return integer the number of unitDefIDs + * @return table unitsCounts + * @return integer countDefs the number of unitDefIDs */ int LuaUnsyncedRead::GetSelectedUnitsCounts(lua_State* L) { @@ -2702,7 +2754,7 @@ int LuaUnsyncedRead::GetSelectedUnitsCounts(lua_State* L) /*** Returns the amount of selected units * * @function Spring.GetSelectedUnitsCount - * @return number selectedUnitsCount + * @return integer selectedUnitsCount */ int LuaUnsyncedRead::GetSelectedUnitsCount(lua_State* L) { @@ -2815,11 +2867,11 @@ int LuaUnsyncedRead::GetMapDrawMode(lua_State* L) /*** * * @function Spring.GetMapSquareTexture - * @param texSquareX number - * @param texSquareY number - * @param lodMin number + * @param texSquareX integer + * @param texSquareY integer + * @param lodMin integer * @param luaTexName string - * @param lodMax number? (Default: lodMin) + * @param lodMax integer? (Default: lodMin) * @return boolean? success */ int LuaUnsyncedRead::GetMapSquareTexture(lua_State* L) @@ -3092,7 +3144,7 @@ int LuaUnsyncedRead::GetCameraFOV(lua_State* L) int LuaUnsyncedRead::GetCameraVectors(lua_State* L) { #define PACK_CAMERA_VECTOR(s,n) \ - HSTR_PUSH(L, #s); \ + lua_pushhstring(L, CompileTimeHash(#s), #s, sizeof(#s) - 1); \ lua_createtable(L, 3, 0); \ lua_pushnumber(L, camera-> n .x); lua_rawseti(L, -2, 1); \ lua_pushnumber(L, camera-> n .y); lua_rawseti(L, -2, 2); \ @@ -3147,16 +3199,16 @@ int LuaUnsyncedRead::WorldToScreenCoords(lua_State* L) * * The unit must be selectable, to appear to a screen trace ray. * - * @param screenX number position on x axis in mouse coordinates (origin on left border of view) - * @param screenY number position on y axis in mouse coordinates (origin on top border of view) + * @param screenX integer position on x axis in mouse coordinates (origin on left border of view) + * @param screenY integer position on y axis in mouse coordinates (origin on top border of view) * @param onlyCoords boolean? (Default: `false`) return only description (1st return value) and coordinates (2nd return value) * @param useMinimap boolean? (Default: `false`) if position arguments are contained by minimap, use the minimap corresponding world position * @param includeSky boolean? (Default: `false`) * @param ignoreWater boolean? (Default: `false`) * @param heightOffset number? (Default: `0`) * @return string? description of traced position - * @return number|string|xyz|nil unitID or feature, position triple when onlyCoords=true - * @return number|string|nil featureID or ground + * @return UnitID|FeatureID|string|xyz|nil unitID or feature, position triple when onlyCoords=true + * @return FeatureID|string|nil featureID or ground * @return xyz? coords */ int LuaUnsyncedRead::TraceScreenRay(lua_State* L) @@ -3264,8 +3316,8 @@ int LuaUnsyncedRead::TraceScreenRay(lua_State* L) /*** * * @function Spring.GetPixelDir - * @param x number - * @param y number + * @param x integer + * @param y integer * @return number dirX * @return number dirY * @return number dirZ @@ -3319,7 +3371,7 @@ static bool AddPlayerToRoster(lua_State* L, int playerID, bool onlyActivePlayers /*** * * @function Spring.GetTeamColor - * @param teamID integer + * @param teamID TeamID * @return number? r factor from 0 to 1 * @return number? g factor from 0 to 1 * @return number? b factor from 0 to 1 @@ -3346,7 +3398,7 @@ int LuaUnsyncedRead::GetTeamColor(lua_State* L) /*** * * @function Spring.GetTeamOrigColor - * @param teamID integer + * @param teamID TeamID * @return number? r factor from 0 to 1 * @return number? g factor from 0 to 1 * @return number? b factor from 0 to 1 @@ -3393,7 +3445,7 @@ int LuaUnsyncedRead::GetCustomPaletteColor(lua_State* L) /*** * Returns the custom palette index for a unit, or nil if using team color. * @function Spring.GetUnitPaletteIndex - * @param unitID integer + * @param unitID UnitID * @return integer? customIndex [0..MAX_CUSTOM_COLORS) if unit uses a custom color, nil if using team color */ int LuaUnsyncedRead::GetUnitPaletteIndex(lua_State* L) @@ -3415,7 +3467,7 @@ int LuaUnsyncedRead::GetUnitPaletteIndex(lua_State* L) /*** * Returns the custom palette index for a feature, or nil if using team color. * @function Spring.GetFeaturePaletteIndex - * @param featureID integer + * @param featureID FeatureID * @return integer? customIndex [0..MAX_CUSTOM_COLORS) if feature uses a custom color, nil if using team color */ int LuaUnsyncedRead::GetFeaturePaletteIndex(lua_State* L) @@ -3496,6 +3548,7 @@ int LuaUnsyncedRead::GetSoundStreamTime(lua_State* L) /*** * * @function Spring.GetSoundEffectParams + * @return table? soundEffectParams `nil` on headless/no-sound builds or when EFX is unsupported. */ int LuaUnsyncedRead::GetSoundEffectParams(lua_State* L) { @@ -3586,7 +3639,7 @@ int LuaUnsyncedRead::GetSoundEffectParams(lua_State* L) /*** * * @function Spring.GetFPS - * @return number fps + * @return integer fps */ int LuaUnsyncedRead::GetFPS(lua_State* L) { @@ -3642,9 +3695,9 @@ int LuaUnsyncedRead::GetGameState(lua_State* L) /*** * * @function Spring.GetActiveCommand - * @return number? cmdIndex + * @return integer? cmdIndex * @return integer? cmdID - * @return number? cmdType + * @return integer? cmdType * @return string? cmdName */ int LuaUnsyncedRead::GetActiveCommand(lua_State* L) @@ -3807,7 +3860,7 @@ int LuaUnsyncedRead::GetBuildFacing(lua_State* L) /*** * * @function Spring.GetBuildSpacing - * @return number buildSpacing + * @return integer buildSpacing */ int LuaUnsyncedRead::GetBuildSpacing(lua_State* L) { @@ -3822,7 +3875,7 @@ int LuaUnsyncedRead::GetBuildSpacing(lua_State* L) /*** * * @function Spring.GetGatherMode - * @return number gatherMode + * @return integer gatherMode */ int LuaUnsyncedRead::GetGatherMode(lua_State* L) { @@ -3839,8 +3892,8 @@ int LuaUnsyncedRead::GetGatherMode(lua_State* L) /*** * * @function Spring.GetActivePage - * @return number activePage - * @return number maxPage + * @return integer activePage + * @return integer maxPage */ int LuaUnsyncedRead::GetActivePage(lua_State* L) { @@ -3862,11 +3915,11 @@ int LuaUnsyncedRead::GetActivePage(lua_State* L) /*** * * @function Spring.GetMouseState - * @return number x - * @return number y - * @return number lmbPressed left mouse button pressed - * @return number mmbPressed middle mouse button pressed - * @return number rmbPressed right mouse button pressed + * @return integer x + * @return integer y + * @return boolean lmbPressed left mouse button pressed + * @return boolean mmbPressed middle mouse button pressed + * @return boolean rmbPressed right mouse button pressed * @return boolean offscreen * @return boolean mmbScroll */ @@ -3906,9 +3959,9 @@ int LuaUnsyncedRead::GetMouseCursor(lua_State* L) /*** * * @function Spring.GetMouseStartPosition - * @param button number - * @return number x - * @return number y + * @param button integer + * @return integer x + * @return integer y * @return number camPosX * @return number camPosY * @return number camPosZ @@ -4036,7 +4089,7 @@ int LuaUnsyncedRead::GetLastMessagePositions(lua_State* L) /*** * @function Spring.GetConsoleBuffer - * @param maxLines number + * @param maxLines integer * @return { text: string, priority: integer }[] buffer */ int LuaUnsyncedRead::GetConsoleBuffer(lua_State* L) @@ -4089,7 +4142,7 @@ int LuaUnsyncedRead::GetCurrentTooltip(lua_State* L) /*** * @function Spring.GetKeyFromScanSymbol - * @param scanSymbol string + * @param scanSymbol string? * @return string keyName */ int LuaUnsyncedRead::GetKeyFromScanSymbol(lua_State* L) @@ -4125,7 +4178,7 @@ int LuaUnsyncedRead::GetKeyFromScanSymbol(lua_State* L) /*** * * @function Spring.GetKeyState - * @param keyCode number + * @param keyCode integer * @return boolean pressed */ int LuaUnsyncedRead::GetKeyState(lua_State* L) @@ -4157,7 +4210,7 @@ int LuaUnsyncedRead::GetModKeyState(lua_State* L) /*** * * @function Spring.GetPressedKeys - * @return table where keys are keyCodes or key names + * @return table keys where keys are keyCodes or key names */ int LuaUnsyncedRead::GetPressedKeys(lua_State* L) { @@ -4188,7 +4241,7 @@ int LuaUnsyncedRead::GetPressedKeys(lua_State* L) /*** * * @function Spring.GetPressedScans - * @return table where keys are scanCodes or scan names + * @return table scans where keys are scanCodes or scan names */ int LuaUnsyncedRead::GetPressedScans(lua_State* L) { @@ -4235,7 +4288,7 @@ int LuaUnsyncedRead::GetInvertQueueKey(lua_State* L) * * @function Spring.GetKeyCode * @param keySym string - * @return number keyCode + * @return integer keyCode */ int LuaUnsyncedRead::GetKeyCode(lua_State* L) { @@ -4247,7 +4300,7 @@ int LuaUnsyncedRead::GetKeyCode(lua_State* L) /*** * * @function Spring.GetKeySymbol - * @param keyCode number + * @param keyCode integer * @return string keyCodeName * @return string keyCodeDefaultName name when there are not aliases */ @@ -4263,7 +4316,7 @@ int LuaUnsyncedRead::GetKeySymbol(lua_State* L) /*** * * @function Spring.GetScanSymbol - * @param scanCode number + * @param scanCode integer * @return string scanCodeName * @return string scanCodeDefaultName name when there are not aliases */ @@ -4368,7 +4421,7 @@ int LuaUnsyncedRead::GetActionHotKeys(lua_State* L) /*** * * @function Spring.GetGroupList - * @return table? where keys are groupIDs and values are counts + * @return table? groupCounts */ int LuaUnsyncedRead::GetGroupList(lua_State* L) { @@ -4396,7 +4449,7 @@ int LuaUnsyncedRead::GetGroupList(lua_State* L) /*** * * @function Spring.GetSelectedGroup - * @return integer groupID -1 when no group selected + * @return GroupID groupID -1 when no group selected */ int LuaUnsyncedRead::GetSelectedGroup(lua_State* L) { @@ -4408,8 +4461,8 @@ int LuaUnsyncedRead::GetSelectedGroup(lua_State* L) /*** * * @function Spring.GetUnitGroup - * @param unitID integer - * @return integer? groupID + * @param unitID UnitID + * @return GroupID? groupID */ int LuaUnsyncedRead::GetUnitGroup(lua_State* L) { @@ -4443,8 +4496,8 @@ static inline const CGroup* GetGroupFromArg(lua_State* L, int arg) /*** * * @function Spring.GetGroupUnits - * @param groupID integer - * @return number[]? unitIDs + * @param groupID GroupID + * @return UnitID[]? unitIDs */ int LuaUnsyncedRead::GetGroupUnits(lua_State* L) { @@ -4460,8 +4513,8 @@ int LuaUnsyncedRead::GetGroupUnits(lua_State* L) /*** * * @function Spring.GetGroupUnitsSorted - * @param groupID integer - * @return table? where keys are unitDefIDs and values are unitIDs + * @param groupID GroupID + * @return table? unitsIDs */ int LuaUnsyncedRead::GetGroupUnitsSorted(lua_State* L) { @@ -4477,8 +4530,8 @@ int LuaUnsyncedRead::GetGroupUnitsSorted(lua_State* L) /*** * * @function Spring.GetGroupUnitsCounts - * @param groupID integer - * @return table? where keys are unitDefIDs and values are counts + * @param groupID GroupID + * @return table? unitsCounts */ int LuaUnsyncedRead::GetGroupUnitsCounts(lua_State* L) { @@ -4494,8 +4547,8 @@ int LuaUnsyncedRead::GetGroupUnitsCounts(lua_State* L) /*** * * @function Spring.GetGroupUnitsCount - * @param groupID integer - * @return number? groupSize + * @param groupID GroupID + * @return integer? groupSize */ int LuaUnsyncedRead::GetGroupUnitsCount(lua_State* L) { @@ -4519,9 +4572,9 @@ int LuaUnsyncedRead::GetGroupUnitsCount(lua_State* L) * @class Roster * @x_helper * @field name string - * @field playerID integer - * @field teamID integer - * @field allyTeamID integer + * @field playerID PlayerID + * @field teamID TeamID + * @field allyTeamID AllyTeamID * @field spectator boolean * @field cpuUsage number in order to find the progress, use: cpuUsage&0x1 if it's PC or BO, cpuUsage& 0xFE to get path res, (cpuUsage>>8)*1000 for the progress * @field pingTime number if -1, the player is pathfinding @@ -4531,7 +4584,7 @@ int LuaUnsyncedRead::GetGroupUnitsCount(lua_State* L) /*** * * @function Spring.GetPlayerRoster - * @param sortType number? return unsorted if unspecified. Disabled = 0, Allies = 1, TeamID = 2, PlayerName = 3, PlayerCPU = 4, PlayerPing = 5 + * @param sortType integer? return unsorted if unspecified. Disabled = 0, Allies = 1, TeamID = 2, PlayerName = 3, PlayerCPU = 4, PlayerPing = 5 * @param showPathingPlayers boolean? (Default: `false`) * @return Roster[]? playerTable */ @@ -4567,9 +4620,9 @@ int LuaUnsyncedRead::GetPlayerRoster(lua_State* L) /*** * * @function Spring.GetPlayerTraffic - * @param playerID integer + * @param playerID PlayerID * @param packetID integer? - * @return number traffic + * @return integer traffic */ int LuaUnsyncedRead::GetPlayerTraffic(lua_State* L) { @@ -4619,12 +4672,12 @@ int LuaUnsyncedRead::GetPlayerTraffic(lua_State* L) /*** * * @function Spring.GetPlayerStatistics - * @param playerID integer - * @return number? mousePixels nil when invalid playerID - * @return number mouseClicks - * @return number keyPresses - * @return number numCommands - * @return number unitCommands + * @param playerID PlayerID + * @return integer? mousePixels nil when invalid playerID + * @return integer mouseClicks + * @return integer keyPresses + * @return integer numCommands + * @return integer unitCommands */ int LuaUnsyncedRead::GetPlayerStatistics(lua_State* L) { @@ -4749,8 +4802,9 @@ int LuaUnsyncedRead::GetConfigParams(lua_State* L) * * @function Spring.GetConfigInt * @param name string - * @param default number? (Default: `0`) - * @return number? configInt + * @param default integer Default value if `name` is not found + * @return integer + * @overload fun(name: string): integer? */ int LuaUnsyncedRead::GetConfigInt(lua_State* L) { @@ -4770,8 +4824,9 @@ int LuaUnsyncedRead::GetConfigInt(lua_State* L) * * @function Spring.GetConfigFloat * @param name string - * @param default number? (Default: `0`) - * @return number? configFloat + * @param default number Default value if `name` is not found + * @return number + * @overload fun(name: string): number? */ int LuaUnsyncedRead::GetConfigFloat(lua_State* L) { @@ -4791,8 +4846,9 @@ int LuaUnsyncedRead::GetConfigFloat(lua_State* L) * * @function Spring.GetConfigString * @param name string - * @param default string? (Default: `""`) - * @return number? configString + * @param default string Default value if `name` is not found + * @return string + * @overload fun(name: string): string? */ int LuaUnsyncedRead::GetConfigString(lua_State* L) { @@ -4811,7 +4867,7 @@ int LuaUnsyncedRead::GetConfigString(lua_State* L) /*** * * @function Spring.GetLogSections - * @return table sections where keys are names and loglevel are values. E.g. `{ "KeyBindings" = LOG.INFO, "Font" = LOG.INFO, "Sound" = LOG.WARNING, ... }` + * @return table sections where keys are names and loglevel are values. E.g. `{ "KeyBindings" = LOG.INFO, "Font" = LOG.INFO, "Sound" = LOG.WARNING, ... }` */ int LuaUnsyncedRead::GetLogSections(lua_State* L) { const int numLogSections = log_filter_section_getNumRegisteredSections(); @@ -4840,7 +4896,7 @@ int LuaUnsyncedRead::GetLogSections(lua_State* L) { * * @function Spring.GetAllGroundDecals * - * @return number[] decalIDs + * @return DecalID[] decalIDs */ int LuaUnsyncedRead::GetAllGroundDecals(lua_State* L) { @@ -4873,7 +4929,7 @@ int LuaUnsyncedRead::GetAllGroundDecals(lua_State* L) /*** * * @function Spring.GetGroundDecalMiddlePos - * @param decalID integer + * @param decalID DecalID * @return number? posX * @return number posZ */ @@ -4894,7 +4950,7 @@ int LuaUnsyncedRead::GetGroundDecalMiddlePos(lua_State* L) /*** * * @function Spring.GetGroundDecalQuadPos - * @param decalID integer + * @param decalID DecalID * @return number? posTL.x * @return number posTL.z * @return number posTR.x @@ -4927,7 +4983,7 @@ int LuaUnsyncedRead::GetGroundDecalQuadPos(lua_State* L) /*** * * @function Spring.GetGroundDecalSizeAndHeight - * @param decalID integer + * @param decalID DecalID * @return number? sizeX * @return number sizeY * @return number projCubeHeight @@ -4951,7 +5007,7 @@ int LuaUnsyncedRead::GetGroundDecalSizeAndHeight(lua_State* L) /*** * * @function Spring.GetGroundDecalRotation - * @param decalID integer + * @param decalID DecalID * @return number? rotation Rotation in radians. */ int LuaUnsyncedRead::GetGroundDecalRotation(lua_State* L) @@ -4970,7 +5026,7 @@ int LuaUnsyncedRead::GetGroundDecalRotation(lua_State* L) /*** * * @function Spring.GetGroundDecalTexture - * @param decalID integer + * @param decalID DecalID * @param isMainTex boolean? (Default: `true`) If `false`, return the normal/glow map. * @return string? texture */ @@ -5012,8 +5068,8 @@ int LuaUnsyncedRead::GetGroundDecalTextures(lua_State* L) /*** * - * @function Spring.SetGroundDecalTextureParams - * @param decalID integer + * @function Spring.GetGroundDecalTextureParams + * @param decalID DecalID * @return number? texWrapDistance If non-zero, sets the mode to repeat the texture along the left-right direction of the decal every texWrapFactor elmos. * @return number texTraveledDistance Shifts the texture repetition defined by texWrapFactor so the texture of a next line in the continuous multiline can start where the previous finished. For that it should collect all elmo lengths of the previously set multiline segments. */ @@ -5034,7 +5090,7 @@ int LuaUnsyncedRead::GetGroundDecalTextureParams(lua_State* L) /*** * * @function Spring.GetGroundDecalAlpha - * @param decalID integer + * @param decalID DecalID * @return number? alpha Between 0 and 1 * @return number alphaFalloff Between 0 and 1, per second */ @@ -5057,7 +5113,7 @@ int LuaUnsyncedRead::GetGroundDecalAlpha(lua_State* L) * * If all three equal 0, the decal follows the normals of ground at midpoint * - * @param decalID integer + * @param decalID DecalID * @return number? normal.x * @return number normal.y * @return number normal.z @@ -5081,7 +5137,7 @@ int LuaUnsyncedRead::GetGroundDecalNormal(lua_State* L) * @function Spring.GetGroundDecalTint * Gets the tint of the ground decal. * A color of (0.5, 0.5, 0.5, 0.5) is effectively no tint - * @param decalID integer + * @param decalID DecalID * @return number? tintR * @return number tintG * @return number tintB @@ -5107,7 +5163,7 @@ int LuaUnsyncedRead::GetGroundDecalTint(lua_State* L) * * @function Spring.GetGroundDecalMisc * Returns less important parameters of a ground decal - * @param decalID integer + * @param decalID DecalID * @return number? dotElimExp * @return number refHeight * @return number minHeight @@ -5135,7 +5191,7 @@ int LuaUnsyncedRead::GetGroundDecalMisc(lua_State* L) * * Min can be not equal to max for "gradient" style decals, e.g. unit tracks * - * @param decalID integer + * @param decalID DecalID * @return number? creationFrameMin * @return number creationFrameMax */ @@ -5155,7 +5211,7 @@ int LuaUnsyncedRead::GetGroundDecalCreationFrame(lua_State* L) /*** * @function Spring.GetGroundDecalOwner - * @param decalID integer + * @param decalID DecalID * @return integer? value If owner is a unit, then this is `unitID`, if owner is * a feature it is `featureID + MAX_UNITS`. If there is no owner, then `nil`. */ @@ -5177,7 +5233,7 @@ int LuaUnsyncedRead::GetGroundDecalOwner(lua_State* L) * * @function Spring.GetGroundDecalGlowParams * Gets the glow parameters of the ground decal. - * @param decalID integer + * @param decalID DecalID * @return number? glow Between 0 and 1 * @return number glowFalloff Between 0 and 1, per second */ @@ -5201,7 +5257,7 @@ int LuaUnsyncedRead::GetGroundDecalGlowParams(lua_State* L) * * @function Spring.GetGroundDecalUserData * Gets the user defined decal data. - * @param decalID integer + * @param decalID DecalID * @param udQuad integer vec4 index, must be within [0;1] for now * @return number? x * @return number y @@ -5231,7 +5287,7 @@ int LuaUnsyncedRead::GetGroundDecalUserData(lua_State* L) /*** * * @function Spring.GetGroundDecalType - * @param decalID integer + * @param decalID DecalID * @return "explosion"|"plate"|"lua"|"track"|"unknown"|nil type */ int LuaUnsyncedRead::GetGroundDecalType(lua_State* L) @@ -5274,7 +5330,7 @@ int LuaUnsyncedRead::GetGroundDecalType(lua_State* L) * * @function Spring.GetSyncedGCInfo * @param collectGC boolean? (Default: `false`) collect before returning metric - * @return number? GC values are expressed in Kbytes: #bytes/2^10 + * @return integer? GC values are expressed in Kbytes: #bytes/2^10 */ int LuaUnsyncedRead::GetSyncedGCInfo(lua_State* L) { if (luaRules == nullptr) @@ -5300,8 +5356,11 @@ int LuaUnsyncedRead::GetSyncedGCInfo(lua_State* L) { /*** * * @function Spring.SolveNURBSCurve - * @param groupID integer - * @return number[]? unitIDs + * @param degree integer Degree of the curve. + * @param controlPoints number[] Flat array of `x, y, z, weight` quadruples; its length must be a multiple of 4. + * @param knots number[] Knot vector. + * @param segments integer Number of segments to evaluate. + * @return number[] points Flat array of `x, y, z` triples along the curve. */ int LuaUnsyncedRead::SolveNURBSCurve(lua_State* L) { diff --git a/rts/Lua/LuaUnsyncedRead.h b/rts/Lua/LuaUnsyncedRead.h index 2d26b5544fa..3926912fdca 100644 --- a/rts/Lua/LuaUnsyncedRead.h +++ b/rts/Lua/LuaUnsyncedRead.h @@ -32,6 +32,7 @@ class LuaUnsyncedRead { static int GetGameSecondsInterpolated(lua_State* L); static int GetLastUpdateSeconds(lua_State* L); static int GetVideoCapturingMode(lua_State* L); + static int GetPrevFrameSyncChecksum(lua_State* L); static int GetNumDisplays(lua_State* L); static int GetViewGeometry(lua_State* L); diff --git a/rts/Lua/LuaUtils.cpp b/rts/Lua/LuaUtils.cpp index 6b86bd3e8a1..caebc42cc70 100644 --- a/rts/Lua/LuaUtils.cpp +++ b/rts/Lua/LuaUtils.cpp @@ -762,9 +762,9 @@ int LuaUtils::PushModelRadius(lua_State* L, const SolidObjectDef* def, bool isUn int LuaUtils::PushFeatureModelDrawType(lua_State* L, const FeatureDef* def) { switch (def->drawType) { - case DRAWTYPE_NONE: { HSTR_PUSH(L, "none"); } break; - case DRAWTYPE_MODEL: { HSTR_PUSH(L, "model"); } break; - default: { HSTR_PUSH(L, "tree"); } break; + case DRAWTYPE_NONE: { LuaPushString(L, "none"); } break; + case DRAWTYPE_MODEL: { LuaPushString(L, "model"); } break; + default: { LuaPushString(L, "tree"); } break; } return 1; @@ -805,30 +805,30 @@ int LuaUtils::PushModelTable(lua_State* L, const SolidObjectDef* def) { if (model != nullptr) { // unit, or non-tree feature - HSTR_PUSH_NUMBER(L, "minx", model->mins.x); - HSTR_PUSH_NUMBER(L, "miny", model->mins.y); - HSTR_PUSH_NUMBER(L, "minz", model->mins.z); - HSTR_PUSH_NUMBER(L, "maxx", model->maxs.x); - HSTR_PUSH_NUMBER(L, "maxy", model->maxs.y); - HSTR_PUSH_NUMBER(L, "maxz", model->maxs.z); - - HSTR_PUSH_NUMBER(L, "midx", model->relMidPos.x); - HSTR_PUSH_NUMBER(L, "midy", model->relMidPos.y); - HSTR_PUSH_NUMBER(L, "midz", model->relMidPos.z); + LuaPushNamedNumber(L, "minx", model->mins.x); + LuaPushNamedNumber(L, "miny", model->mins.y); + LuaPushNamedNumber(L, "minz", model->mins.z); + LuaPushNamedNumber(L, "maxx", model->maxs.x); + LuaPushNamedNumber(L, "maxy", model->maxs.y); + LuaPushNamedNumber(L, "maxz", model->maxs.z); + + LuaPushNamedNumber(L, "midx", model->relMidPos.x); + LuaPushNamedNumber(L, "midy", model->relMidPos.y); + LuaPushNamedNumber(L, "midz", model->relMidPos.z); } else { - HSTR_PUSH_NUMBER(L, "minx", 0.0f); - HSTR_PUSH_NUMBER(L, "miny", 0.0f); - HSTR_PUSH_NUMBER(L, "minz", 0.0f); - HSTR_PUSH_NUMBER(L, "maxx", 0.0f); - HSTR_PUSH_NUMBER(L, "maxy", 0.0f); - HSTR_PUSH_NUMBER(L, "maxz", 0.0f); - - HSTR_PUSH_NUMBER(L, "midx", 0.0f); - HSTR_PUSH_NUMBER(L, "midy", 0.0f); - HSTR_PUSH_NUMBER(L, "midz", 0.0f); + LuaPushNamedNumber(L, "minx", 0.0f); + LuaPushNamedNumber(L, "miny", 0.0f); + LuaPushNamedNumber(L, "minz", 0.0f); + LuaPushNamedNumber(L, "maxx", 0.0f); + LuaPushNamedNumber(L, "maxy", 0.0f); + LuaPushNamedNumber(L, "maxz", 0.0f); + + LuaPushNamedNumber(L, "midx", 0.0f); + LuaPushNamedNumber(L, "midy", 0.0f); + LuaPushNamedNumber(L, "midz", 0.0f); } - HSTR_PUSH(L, "textures"); + LuaPushString(L, "textures"); lua_createtable(L, 0, model != nullptr ? 2 : 0); if (model != nullptr) { @@ -850,16 +850,16 @@ int LuaUtils::PushColVolTable(lua_State* L, const CollisionVolume* vol) { lua_createtable(L, 0, 11); switch (vol->GetVolumeType()) { case CollisionVolume::COLVOL_TYPE_ELLIPSOID: - HSTR_PUSH_CSTRING(L, "type", "ellipsoid"); + LuaPushNamedString(L, "type", "ellipsoid"); break; case CollisionVolume::COLVOL_TYPE_CYLINDER: - HSTR_PUSH_CSTRING(L, "type", "cylinder"); + LuaPushNamedString(L, "type", "cylinder"); break; case CollisionVolume::COLVOL_TYPE_BOX: - HSTR_PUSH_CSTRING(L, "type", "box"); + LuaPushNamedString(L, "type", "box"); break; case CollisionVolume::COLVOL_TYPE_SPHERE: - HSTR_PUSH_CSTRING(L, "type", "sphere"); + LuaPushNamedString(L, "type", "sphere"); break; } @@ -917,7 +917,7 @@ int LuaUtils::ParseColVolData(lua_State* L, int idx, CollisionVolume* vol) void LuaUtils::PushCommandParamsTable(lua_State* L, const Command& cmd, bool subtable) { if (subtable) - HSTR_PUSH(L, "params"); + LuaPushString(L, "params"); lua_createtable(L, cmd.GetNumParams(), 0); @@ -950,16 +950,16 @@ void LuaUtils::PushCommandParamsTable(lua_State* L, const Command& cmd, bool sub void LuaUtils::PushCommandOptionsTable(lua_State* L, const Command& cmd, bool subtable) { if (subtable) - HSTR_PUSH(L, "options"); + LuaPushString(L, "options"); lua_createtable(L, 0, 7); - HSTR_PUSH_NUMBER(L, "coded", cmd.GetOpts()); - HSTR_PUSH_BOOL(L, "alt", !!(cmd.GetOpts() & ALT_KEY )); - HSTR_PUSH_BOOL(L, "ctrl", !!(cmd.GetOpts() & CONTROL_KEY )); - HSTR_PUSH_BOOL(L, "shift", !!(cmd.GetOpts() & SHIFT_KEY )); - HSTR_PUSH_BOOL(L, "right", !!(cmd.GetOpts() & RIGHT_MOUSE_KEY)); - HSTR_PUSH_BOOL(L, "meta", !!(cmd.GetOpts() & META_KEY )); - HSTR_PUSH_BOOL(L, "internal", !!(cmd.GetOpts() & INTERNAL_ORDER )); + LuaPushNamedNumber(L, "coded", cmd.GetOpts()); + LuaPushNamedBool(L, "alt", !!(cmd.GetOpts() & ALT_KEY )); + LuaPushNamedBool(L, "ctrl", !!(cmd.GetOpts() & CONTROL_KEY )); + LuaPushNamedBool(L, "shift", !!(cmd.GetOpts() & SHIFT_KEY )); + LuaPushNamedBool(L, "right", !!(cmd.GetOpts() & RIGHT_MOUSE_KEY)); + LuaPushNamedBool(L, "meta", !!(cmd.GetOpts() & META_KEY )); + LuaPushNamedBool(L, "internal", !!(cmd.GetOpts() & INTERNAL_ORDER )); if (subtable) lua_rawset(L, -3); @@ -1598,20 +1598,20 @@ void LuaUtils::PushCommandDesc(lua_State* L, const SCommandDescription& cd) lua_checkstack(L, 1 + 1 + 1 + 1); lua_createtable(L, 0, numTblKeys); - HSTR_PUSH_NUMBER(L, "id", cd.id); - HSTR_PUSH_NUMBER(L, "type", cd.type); - HSTR_PUSH_STRING(L, "name", cd.name); - HSTR_PUSH_STRING(L, "action", cd.action); - HSTR_PUSH_STRING(L, "tooltip", cd.tooltip); - HSTR_PUSH_STRING(L, "texture", cd.iconname); - HSTR_PUSH_STRING(L, "cursor", cd.mouseicon); - HSTR_PUSH_BOOL(L, "queueing", cd.queueing); - HSTR_PUSH_BOOL(L, "hidden", cd.hidden); - HSTR_PUSH_BOOL(L, "disabled", cd.disabled); - HSTR_PUSH_BOOL(L, "showUnique", cd.showUnique); - HSTR_PUSH_BOOL(L, "onlyTexture", cd.onlyTexture); - - HSTR_PUSH(L, "params"); + LuaPushNamedNumber(L, "id", cd.id); + LuaPushNamedNumber(L, "type", cd.type); + LuaPushNamedString(L, "name", cd.name); + LuaPushNamedString(L, "action", cd.action); + LuaPushNamedString(L, "tooltip", cd.tooltip); + LuaPushNamedString(L, "texture", cd.iconname); + LuaPushNamedString(L, "cursor", cd.mouseicon); + LuaPushNamedBool (L, "queueing", cd.queueing); + LuaPushNamedBool (L, "hidden", cd.hidden); + LuaPushNamedBool (L, "disabled", cd.disabled); + LuaPushNamedBool (L, "showUnique", cd.showUnique); + LuaPushNamedBool (L, "onlyTexture", cd.onlyTexture); + + LuaPushString(L, "params"); lua_createtable(L, 0, numParams); diff --git a/rts/Lua/LuaUtils.h b/rts/Lua/LuaUtils.h index 1ff85177a1c..4c40fcb44d1 100644 --- a/rts/Lua/LuaUtils.h +++ b/rts/Lua/LuaUtils.h @@ -12,6 +12,7 @@ #include "LuaInclude.h" #include "LuaHandle.h" #include "LuaDefs.h" +#include "System/StringHash.h" // FIXME: use fwd-decls #include "System/EventClient.h" #include "Sim/Units/CommandAI/Command.h" @@ -247,7 +248,18 @@ static void PushObjectDefProxyTable( lua_rawset(L, -3); // set the proxy table } +// Note: The `const char (&key) [N]` overloads are a specialization for string literals to allow hashing at compile time. +// For a `char buf[N]` the hasher will assume its a C style string of size N terminated at index N-1. +// This can lead to hashing after the null terminator if the string only uses part of the buffer. +// Use a std::string if you want to pass in dynamic keys. +template +static inline void LuaPushNamedNil(lua_State* L, const char (&key) [N]) +{ + lua_pushhstring(L, CompileTimeHash(key), key, N - 1); + lua_pushnil(L); + lua_rawset(L, -3); +} static inline void LuaPushNamedNil(lua_State* L, const string& key) @@ -257,6 +269,13 @@ static inline void LuaPushNamedNil(lua_State* L, lua_rawset(L, -3); } +template +static inline void LuaPushNamedBool(lua_State* L, const char (&key) [N], bool value) +{ + lua_pushhstring(L, CompileTimeHash(key), key, N - 1); + lua_pushboolean(L, value); + lua_rawset(L, -3); +} static inline void LuaPushNamedBool(lua_State* L, const string& key, bool value) { @@ -265,6 +284,13 @@ static inline void LuaPushNamedBool(lua_State* L, const string& key, bool value) lua_rawset(L, -3); } +template +static inline void LuaPushNamedNumber(lua_State* L, const char (&key) [N], lua_Number value) +{ + lua_pushhstring(L, CompileTimeHash(key), key, N - 1); + lua_pushnumber(L, value); + lua_rawset(L, -3); +} static inline void LuaPushNamedNumber(lua_State* L, const string& key, lua_Number value) { @@ -273,6 +299,13 @@ static inline void LuaPushNamedNumber(lua_State* L, const string& key, lua_Numbe lua_rawset(L, -3); } +template +static inline void LuaPushNamedChar(lua_State* L, const char (&key) [N], char value) +{ + lua_pushhstring(L, CompileTimeHash(key), key, N - 1); + lua_pushlstring(L, &value, 1); + lua_rawset(L, -3); +} static inline void LuaPushNamedChar(lua_State* L, char const *name, char value) { @@ -281,6 +314,22 @@ static inline void LuaPushNamedChar(lua_State* L, char const *name, char value) lua_rawset(L, -3); } +template +static inline void LuaPushNamedString(lua_State* L, const char (&key) [N0], const char (&value) [N1]) +{ + lua_pushhstring(L, CompileTimeHash(key), key, N0 - 1); + lua_pushhstring(L, CompileTimeHash(value), value, N1 - 1); + lua_rawset(L, -3); +} + +template +static inline void LuaPushNamedString(lua_State* L, const char (&key) [N], const string& value) +{ + lua_pushhstring(L, CompileTimeHash(key), key, N - 1); + lua_pushsstring(L, value); + lua_rawset(L, -3); +} + static inline void LuaPushNamedString(lua_State* L, const string& key, const string& value) { lua_pushsstring(L, key); @@ -288,6 +337,13 @@ static inline void LuaPushNamedString(lua_State* L, const string& key, const str lua_rawset(L, -3); } +template +static inline void LuaPushNamedCFunc(lua_State* L, const char (&key) [N], lua_CFunction func) +{ + lua_pushhstring(L, CompileTimeHash(key), key, N - 1); + lua_pushcfunction(L, func); + lua_rawset(L, -3); +} static inline void LuaPushNamedCFunc(lua_State* L, const string& key, lua_CFunction func) { @@ -303,6 +359,17 @@ static inline void LuaPushRawNamedCFunc(lua_State* L, const char* key, lua_CFunc lua_rawset(L, -3); } +template +static inline void LuaPushString(lua_State* L, const char (&str)[N]) +{ + static_assert(N > 1); + lua_pushhstring(L, CompileTimeHash(str), str, N - 1); +} + +static inline void LuaPushString(lua_State* L, const string& str) { + lua_pushsstring(L, str); +} + #define REGISTER_LUA_CFUNC(func) LuaPushRawNamedCFunc(L, #func, func) #define REGISTER_NAMED_LUA_CFUNC(name, func) LuaPushRawNamedCFunc(L, name, func) #define REGISTER_SCOPED_LUA_CFUNC(scope, func) LuaPushRawNamedCFunc(L, #func, scope::func) diff --git a/rts/Lua/LuaVAOImpl.cpp b/rts/Lua/LuaVAOImpl.cpp index 8d182b0fd01..d350a8fc016 100644 --- a/rts/Lua/LuaVAOImpl.cpp +++ b/rts/Lua/LuaVAOImpl.cpp @@ -368,11 +368,11 @@ LuaVAOImpl::DrawCheckResult LuaVAOImpl::DrawCheck(GLenum mode, const DrawCheckIn /*** * * @function VAO:DrawArrays - * @param glEnum number primitivesMode - * @param vertexCount number? - * @param vertexFirst number? - * @param instanceCount number? - * @param instanceFirst number? + * @param glEnum GL primitivesMode + * @param vertexCount integer? + * @param vertexFirst integer? + * @param instanceCount integer? + * @param instanceFirst integer? * @return nil */ void LuaVAOImpl::DrawArrays(GLenum mode, sol::optional vertCountOpt, sol::optional vertexFirstOpt, sol::optional instanceCountOpt, sol::optional instanceFirstOpt) @@ -405,12 +405,12 @@ void LuaVAOImpl::DrawArrays(GLenum mode, sol::optional vertCountOpt, sol::o /*** * * @function VAO:DrawElements - * @param glEnum number primitivesMode - * @param drawCount number? - * @param baseIndex number? - * @param instanceCount number? - * @param baseVertex number? - * @param baseInstance number? + * @param glEnum GL primitivesMode + * @param drawCount integer? + * @param baseIndex integer? + * @param instanceCount integer? + * @param baseVertex integer? + * @param baseInstance integer? * @return nil */ void LuaVAOImpl::DrawElements(GLenum mode, sol::optional indCountOpt, sol::optional indElemOffsetOpt, sol::optional instanceCountOpt, sol::optional baseVertexOpt, sol::optional instanceFirstOpt) @@ -467,8 +467,8 @@ void LuaVAOImpl::ClearSubmission() /*** * * @function VAO:AddUnitsToSubmission - * @param unitIDs number|number[] - * @return number submittedCount + * @param unitIDs UnitID|UnitID[] + * @return integer submittedCount */ int LuaVAOImpl::AddUnitsToSubmission(int id) { return AddObjectsToSubmissionImpl(id); } int LuaVAOImpl::AddUnitsToSubmission(const sol::stack_table& ids) { return AddObjectsToSubmissionImpl(ids); } @@ -477,8 +477,8 @@ int LuaVAOImpl::AddUnitsToSubmission(const sol::stack_table& ids) { return AddO /*** * * @function VAO:AddFeaturesToSubmission - * @param featureIDs number|number[] - * @return number submittedCount + * @param featureIDs FeatureID|FeatureID[] + * @return integer submittedCount */ int LuaVAOImpl::AddFeaturesToSubmission(int id) { return AddObjectsToSubmissionImpl(id); } int LuaVAOImpl::AddFeaturesToSubmission(const sol::stack_table& ids) { return AddObjectsToSubmissionImpl(ids); } @@ -487,8 +487,8 @@ int LuaVAOImpl::AddFeaturesToSubmission(const sol::stack_table& ids) { return Ad /*** * * @function VAO:AddUnitDefsToSubmission - * @param unitDefIDs number|number[] - * @return number submittedCount + * @param unitDefIDs UnitDefID|UnitDefID[] + * @return integer submittedCount */ int LuaVAOImpl::AddUnitDefsToSubmission(int id) { return AddObjectsToSubmissionImpl(id); } int LuaVAOImpl::AddUnitDefsToSubmission(const sol::stack_table& ids) { return AddObjectsToSubmissionImpl(ids); } @@ -497,8 +497,8 @@ int LuaVAOImpl::AddUnitDefsToSubmission(const sol::stack_table& ids) { return Ad /*** * * @function VAO:AddFeatureDefsToSubmission - * @param featureDefIDs number|number[] - * @return number submittedCount + * @param featureDefIDs FeatureDefID|FeatureDefID[] + * @return integer submittedCount */ int LuaVAOImpl::AddFeatureDefsToSubmission(int id) { return AddObjectsToSubmissionImpl(id); } int LuaVAOImpl::AddFeatureDefsToSubmission(const sol::stack_table& ids) { return AddObjectsToSubmissionImpl(ids); } @@ -507,7 +507,7 @@ int LuaVAOImpl::AddFeatureDefsToSubmission(const sol::stack_table& ids) { return /*** * * @function VAO:RemoveFromSubmission - * @param index number + * @param index integer * @return nil */ void LuaVAOImpl::RemoveFromSubmission(int idx) @@ -517,13 +517,15 @@ void LuaVAOImpl::RemoveFromSubmission(int idx) return; } - if (idx != submitCmds.size() - 1) + // swap-remove; every remaining command already satisfies baseInstance == index, + // so only the moved command needs its baseInstance fixed up + if (idx != submitCmds.size() - 1) { submitCmds[idx] = submitCmds.back(); + submitCmds[idx].baseInstance = static_cast(idx); + } submitCmds.pop_back(); - for (baseInstance = 0; baseInstance < submitCmds.size(); ++baseInstance) { - submitCmds[baseInstance].baseInstance = baseInstance; - } + baseInstance = static_cast(submitCmds.size()); } diff --git a/rts/Lua/LuaVBOImpl.cpp b/rts/Lua/LuaVBOImpl.cpp index 9b1cfa00ee7..0cf3ad038d1 100644 --- a/rts/Lua/LuaVBOImpl.cpp +++ b/rts/Lua/LuaVBOImpl.cpp @@ -524,10 +524,10 @@ bool LuaVBOImpl::DefineElementArray(const sol::optional attribDefAr * enter your data into the Lua array correctly. * * @function VBO:Define - * @param size number The maximum number of elements this VBO can have. - * @param attribs number|VBOAttributeDef[] + * @param size integer The maximum number of elements this VBO can have. + * @param attribs integer|VBOAttributeDef[] * - * When number, the maximum number of elements this VBO can have. + * When integer, the maximum number of elements this VBO can have. * * Otherwise, an array of arrays specifying the layout. * @@ -585,9 +585,9 @@ void LuaVBOImpl::Define(const int elementsCount, const sol::optional LuaVBOImpl::GetBufferSize() { @@ -670,10 +670,10 @@ size_t LuaVBOImpl::Upload(const sol::stack_table& luaTblData, sol::optional * from specified attribute will be downloaded - otherwise all attributes are * downloaded * @param elementOffset integer? (Default: `0`) download data starting from this element - * @param elementCount number? number of elements to download + * @param elementCount integer? number of elements to download * @param forceGPURead boolean? (Default: `false`) force downloading the data from GPU buffer as opposed * to using shadow RAM buffer - * @return [number, ...][] vboData + * @return number[] vboData */ sol::as_table_t> LuaVBOImpl::Download(sol::optional attribIdxOpt, sol::optional elemOffsetOpt, sol::optional elemCountOpt, sol::optional forceGPUReadOpt) { @@ -1176,7 +1176,7 @@ size_t LuaVBOImpl::UploadImpl(const std::vector& dataVec, uint32_t elemOffs * * Also fills in VBO definition data as they're set for engine models (no need to do VBO:Define()). * - * @return nil|number buffer size in bytes + * @return integer? size buffer size in bytes */ size_t LuaVBOImpl::ModelsVBO() { @@ -1207,11 +1207,11 @@ size_t LuaVBOImpl::ModelsVBO() * , aux1 { 0u } * ``` * - * @param unitDefIDs number|number[] + * @param unitDefIDs UnitDefID|UnitDefID[] * @param attrID integer * @param teamIdOpt integer? * @param elementOffset integer? - * @return [number,number,number,number] instanceData + * @return [integer,integer,integer,integer] instanceData * @return integer elementOffset * @return integer attrID */ @@ -1246,11 +1246,11 @@ size_t LuaVBOImpl::InstanceDataFromUnitDefIDs(const sol::stack_table& ids, int a * , aux1 { 0u } * ``` * - * @param featureDefIDs number|number[] + * @param featureDefIDs FeatureDefID|FeatureDefID[] * @param attrID integer * @param teamIdOpt integer? * @param elementOffset integer? - * @return [number,number,number,number] instanceData + * @return [integer,integer,integer,integer] instanceData * @return integer elementOffset * @return integer attrID */ @@ -1286,11 +1286,11 @@ size_t LuaVBOImpl::InstanceDataFromFeatureDefIDs(const sol::stack_table& ids, in * , aux1 { 0u } * ``` * - * @param unitIDs number|number[] + * @param unitIDs UnitID|UnitID[] * @param attrID integer * @param teamIdOpt integer? * @param elementOffset integer? - * @return [number,number,number,number] instanceData + * @return [integer,integer,integer,integer] instanceData * @return integer elementOffset * @return integer attrID */ @@ -1314,11 +1314,11 @@ size_t LuaVBOImpl::InstanceDataFromUnitIDs(const sol::stack_table& ids, int attr * global per unit/feature uniform SSBO (unused for Unit/FeatureDefs), as * well as some auxiliary data such as palette index and number of pieces. * - * @param featureIDs number|number[] + * @param featureIDs FeatureID|FeatureID[] * @param attrID integer * @param teamIdOpt integer? * @param elementOffset integer? - * @return [number,number,number,number] instanceData + * @return [integer,integer,integer,integer] instanceData * @return integer elementOffset * @return integer attrID */ @@ -1336,11 +1336,11 @@ size_t LuaVBOImpl::InstanceDataFromFeatureIDs(const sol::stack_table& ids, int a /*** * * @function VBO:MatrixDataFromProjectileIDs - * @param projectileIDs integer|integer[] + * @param projectileIDs ProjectileID|ProjectileID[] * @param attrID integer * @param teamIdOpt integer? * @param elementOffset integer? - * @return number[] matDataVec 4x4 matrix + * @return number[] matDataVec Flattened 4x4 matrix(es) * @return integer elemOffset * @return integer|[integer,integer,integer,integer] attrID */ @@ -1426,8 +1426,8 @@ int LuaVBOImpl::BindBufferRangeImpl(GLuint bindingIndex, const sol::optional elemOffsetOpt, const sol::optional elemCountOpt, const sol::optional targetOpt) @@ -1441,9 +1441,9 @@ int LuaVBOImpl::BindBufferRange(const GLuint index, const sol::optional ele * @function VBO:UnbindBufferRange * @param index integer * @param elementOffset integer? - * @param elementCount number? - * @param target number? glEnum - * @return number bindingIndex when successful, -1 otherwise + * @param elementCount integer? + * @param target GL? glEnum + * @return integer bindingIndex when successful, -1 otherwise */ int LuaVBOImpl::UnbindBufferRange(const GLuint index, const sol::optional elemOffsetOpt, const sol::optional elemCountOpt, const sol::optional targetOpt) { @@ -1493,6 +1493,17 @@ bool LuaVBOImpl::CopyTo(const std::shared_ptr& destVBO, int copySize auto result = vbo->CopyTo(*destVBO->vbo, static_cast(copySizeInBytes)); + // VBO::CopyTo only moves GPU->GPU. We need to also copy over the CPU-side bufferData. + if (result && bufferData != nullptr && destVBO->bufferData != nullptr && copySizeInBytes > 0) { + const auto n = std::min({ + static_cast(copySizeInBytes), + bufferSizeInBytes, + destVBO->bufferSizeInBytes + }); + if (n > 0) + memcpy(destVBO->bufferData, bufferData, n); + } + if (!wasBound) vbo->Unbind(); diff --git a/rts/Lua/LuaVFS.cpp b/rts/Lua/LuaVFS.cpp index 1c2bab2796b..b2587365f9a 100644 --- a/rts/Lua/LuaVFS.cpp +++ b/rts/Lua/LuaVFS.cpp @@ -149,57 +149,57 @@ bool LuaVFS::PushCommon(lua_State* L) { /*** @field VFS.RAW "r" Only select uncompressed files. */ - HSTR_PUSH_CSTRING(L, "RAW", SPRING_VFS_RAW); + LuaPushNamedString(L, "RAW", SPRING_VFS_RAW); /*** @field VFS.GAME "M" */ - HSTR_PUSH_CSTRING(L, "GAME", SPRING_VFS_MOD); // synonym to MOD + LuaPushNamedString(L, "GAME", SPRING_VFS_MOD); // synonym to MOD /*** @field VFS.MAP "m" */ - HSTR_PUSH_CSTRING(L, "MAP", SPRING_VFS_MAP); + LuaPushNamedString(L, "MAP", SPRING_VFS_MAP); /*** @field VFS.BASE "b" */ - HSTR_PUSH_CSTRING(L, "BASE", SPRING_VFS_BASE); + LuaPushNamedString(L, "BASE", SPRING_VFS_BASE); /*** @field VFS.MENU "e" */ - HSTR_PUSH_CSTRING(L, "MENU", SPRING_VFS_MENU); + LuaPushNamedString(L, "MENU", SPRING_VFS_MENU); /*** @field VFS.ZIP "Mmeb" Only select compressed files (`.sdz`, `.sd7`). */ - HSTR_PUSH_CSTRING(L, "ZIP", SPRING_VFS_ZIP); + LuaPushNamedString(L, "ZIP", SPRING_VFS_ZIP); /*** @field VFS.RAW_FIRST "rMmeb" Try uncompressed files first, then compressed. */ - HSTR_PUSH_CSTRING(L, "RAW_FIRST", SPRING_VFS_RAW_FIRST); + LuaPushNamedString(L, "RAW_FIRST", SPRING_VFS_RAW_FIRST); /*** @field VFS.ZIP_FIRST "Mmebr" Try compressed files first, then uncompressed. */ - HSTR_PUSH_CSTRING(L, "ZIP_FIRST", SPRING_VFS_ZIP_FIRST); + LuaPushNamedString(L, "ZIP_FIRST", SPRING_VFS_ZIP_FIRST); /*** * @deprecated * @field VFS.MOD "M" Older spelling for `VFS.GAME` */ - HSTR_PUSH_CSTRING(L, "MOD", SPRING_VFS_MOD); + LuaPushNamedString(L, "MOD", SPRING_VFS_MOD); /*** * @deprecated * @field VFS.RAW_ONLY "r" */ - HSTR_PUSH_CSTRING(L, "RAW_ONLY", SPRING_VFS_RAW); // backwards compatibility + LuaPushNamedString(L, "RAW_ONLY", SPRING_VFS_RAW); // backwards compatibility /*** * @deprecated * @field VFS.ZIP_ONLY "Mmeb" */ - HSTR_PUSH_CSTRING(L, "ZIP_ONLY", SPRING_VFS_ZIP); // backwards compatibility - - HSTR_PUSH_CFUNC(L, "PackU8", PackU8); - HSTR_PUSH_CFUNC(L, "PackU16", PackU16); - HSTR_PUSH_CFUNC(L, "PackU32", PackU32); - HSTR_PUSH_CFUNC(L, "PackS8", PackS8); - HSTR_PUSH_CFUNC(L, "PackS16", PackS16); - HSTR_PUSH_CFUNC(L, "PackS32", PackS32); - HSTR_PUSH_CFUNC(L, "PackF32", PackF32); - HSTR_PUSH_CFUNC(L, "UnpackU8", UnpackU8); - HSTR_PUSH_CFUNC(L, "UnpackU16", UnpackU16); - HSTR_PUSH_CFUNC(L, "UnpackU32", UnpackU32); - HSTR_PUSH_CFUNC(L, "UnpackS8", UnpackS8); - HSTR_PUSH_CFUNC(L, "UnpackS16", UnpackS16); - HSTR_PUSH_CFUNC(L, "UnpackS32", UnpackS32); - HSTR_PUSH_CFUNC(L, "UnpackF32", UnpackF32); + LuaPushNamedString(L, "ZIP_ONLY", SPRING_VFS_ZIP); // backwards compatibility + + LuaPushNamedCFunc(L, "PackU8", PackU8); + LuaPushNamedCFunc(L, "PackU16", PackU16); + LuaPushNamedCFunc(L, "PackU32", PackU32); + LuaPushNamedCFunc(L, "PackS8", PackS8); + LuaPushNamedCFunc(L, "PackS16", PackS16); + LuaPushNamedCFunc(L, "PackS32", PackS32); + LuaPushNamedCFunc(L, "PackF32", PackF32); + LuaPushNamedCFunc(L, "UnpackU8", UnpackU8); + LuaPushNamedCFunc(L, "UnpackU16", UnpackU16); + LuaPushNamedCFunc(L, "UnpackU32", UnpackU32); + LuaPushNamedCFunc(L, "UnpackS8", UnpackS8); + LuaPushNamedCFunc(L, "UnpackS16", UnpackS16); + LuaPushNamedCFunc(L, "UnpackS32", UnpackS32); + LuaPushNamedCFunc(L, "UnpackF32", UnpackF32); // compression should be safe in synced context - HSTR_PUSH_CFUNC(L, "ZlibCompress", ZlibCompress); - HSTR_PUSH_CFUNC(L, "ZlibDecompress", ZlibDecompress); - HSTR_PUSH_CFUNC(L, "CalculateHash", CalculateHash); + LuaPushNamedCFunc(L, "ZlibCompress", ZlibCompress); + LuaPushNamedCFunc(L, "ZlibDecompress", ZlibDecompress); + LuaPushNamedCFunc(L, "CalculateHash", CalculateHash); return true; } @@ -209,11 +209,11 @@ bool LuaVFS::PushSynced(lua_State* L) { PushCommon(L); - HSTR_PUSH_CFUNC(L, "Include", SyncInclude); - HSTR_PUSH_CFUNC(L, "LoadFile", SyncLoadFile); - HSTR_PUSH_CFUNC(L, "FileExists", SyncFileExists); - HSTR_PUSH_CFUNC(L, "DirList", SyncDirList); - HSTR_PUSH_CFUNC(L, "SubDirs", SyncSubDirs); + LuaPushNamedCFunc(L, "Include", SyncInclude); + LuaPushNamedCFunc(L, "LoadFile", SyncLoadFile); + LuaPushNamedCFunc(L, "FileExists", SyncFileExists); + LuaPushNamedCFunc(L, "DirList", SyncDirList); + LuaPushNamedCFunc(L, "SubDirs", SyncSubDirs); return true; } @@ -223,21 +223,21 @@ bool LuaVFS::PushUnsynced(lua_State* L) { PushCommon(L); - HSTR_PUSH_CFUNC(L, "Include", UnsyncInclude); - HSTR_PUSH_CFUNC(L, "LoadFile", UnsyncLoadFile); - HSTR_PUSH_CFUNC(L, "FileExists", UnsyncFileExists); - HSTR_PUSH_CFUNC(L, "DirList", UnsyncDirList); - HSTR_PUSH_CFUNC(L, "SubDirs", UnsyncSubDirs); + LuaPushNamedCFunc(L, "Include", UnsyncInclude); + LuaPushNamedCFunc(L, "LoadFile", UnsyncLoadFile); + LuaPushNamedCFunc(L, "FileExists", UnsyncFileExists); + LuaPushNamedCFunc(L, "DirList", UnsyncDirList); + LuaPushNamedCFunc(L, "SubDirs", UnsyncSubDirs); - HSTR_PUSH_CFUNC(L, "GetFileAbsolutePath", GetFileAbsolutePath); - HSTR_PUSH_CFUNC(L, "GetArchiveContainingFile", GetArchiveContainingFile); + LuaPushNamedCFunc(L, "GetFileAbsolutePath", GetFileAbsolutePath); + LuaPushNamedCFunc(L, "GetArchiveContainingFile", GetArchiveContainingFile); - HSTR_PUSH_CFUNC(L, "UseArchive", UseArchive); - HSTR_PUSH_CFUNC(L, "CompressFolder", CompressFolder); + LuaPushNamedCFunc(L, "UseArchive", UseArchive); + LuaPushNamedCFunc(L, "CompressFolder", CompressFolder); // Removed due to sync unsafety, see commit 0ee88788931f9f0b195eb5f895f1092fde4211c0 - // HSTR_PUSH_CFUNC(L, "MapArchive", MapArchive); - // HSTR_PUSH_CFUNC(L, "UnmapArchive", UnmapArchive); + // LuaPushNamedCFunc(L, "MapArchive", MapArchive); + // LuaPushNamedCFunc(L, "UnmapArchive", UnmapArchive); return true; } @@ -1084,15 +1084,15 @@ int LuaVFS::PackS32(lua_State* L) { return PackType(L); } /*** * Convert signed 32-bit float(s) to binary string. - * @function VFS.PackS32 - * @param ... integer Numbers to pack. - * @return string + * @function VFS.PackF32 + * @param ... number Numbers to pack. + * @return string? */ /*** * Convert signed 32-bit float(s) to binary string. - * @function VFS.PackS32 - * @param numbers integer[] Numbers to pack. - * @return string + * @function VFS.PackF32 + * @param numbers number[] Numbers to pack. + * @return string? */ int LuaVFS::PackF32(lua_State* L) { return PackType(L); } diff --git a/rts/Lua/LuaVFSDownload.cpp b/rts/Lua/LuaVFSDownload.cpp index 118051ccd19..c08220c5319 100644 --- a/rts/Lua/LuaVFSDownload.cpp +++ b/rts/Lua/LuaVFSDownload.cpp @@ -312,6 +312,12 @@ bool LuaVFSDownload::PushEntries(lua_State* L) return true; } +/*** + * Queue an archive download through pr-downloader. + * @function VFS.DownloadArchive + * @param filename string Archive or rapid package name. + * @param category string One of `map`, `game`, or `engine`. + */ int LuaVFSDownload::DownloadArchive(lua_State* L) { const std::string& filename = luaL_checkstring(L, 1); @@ -337,12 +343,22 @@ int LuaVFSDownload::DownloadArchive(lua_State* L) return 0; } +/*** + * Abort a queued or active archive download. + * @function VFS.AbortDownload + * @param id integer Download queue identifier. + * @return boolean removed Whether a matching queued or active download was removed. + */ int LuaVFSDownload::AbortDownload(lua_State* L) { lua_pushboolean(L, downloadQueue.Remove(luaL_checkint(L, 1))); return 1; } +/*** + * Rescan all data directories for newly added archives. + * @function VFS.ScanAllDirs + */ int LuaVFSDownload::ScanAllDirs(lua_State* L) { archiveScanner->ScanAllDirs(); diff --git a/rts/Lua/LuaWeaponDefs.cpp b/rts/Lua/LuaWeaponDefs.cpp index 885feba6a1e..b2815d8cf77 100644 --- a/rts/Lua/LuaWeaponDefs.cpp +++ b/rts/Lua/LuaWeaponDefs.cpp @@ -245,11 +245,11 @@ static int DamagesArray(lua_State* L, const void* data) const DamageArray& d = *static_cast(data); lua_createtable(L, damageArrayHandler.GetNumTypes(), 5); - HSTR_PUSH_NUMBER(L, "impulseFactor", d.impulseFactor); - HSTR_PUSH_NUMBER(L, "impulseBoost", d.impulseBoost); - HSTR_PUSH_NUMBER(L, "craterMult", d.craterMult); - HSTR_PUSH_NUMBER(L, "craterBoost", d.craterBoost); - HSTR_PUSH_NUMBER(L, "paralyzeDamageTime", d.paralyzeDamageTime); + LuaPushNamedNumber(L, "impulseFactor", d.impulseFactor); + LuaPushNamedNumber(L, "impulseBoost", d.impulseBoost); + LuaPushNamedNumber(L, "craterMult", d.craterMult); + LuaPushNamedNumber(L, "craterBoost", d.craterBoost); + LuaPushNamedNumber(L, "paralyzeDamageTime", d.paralyzeDamageTime); // damage values for (int i = 0, n = damageArrayHandler.GetNumTypes(); i < n; i++) { @@ -266,34 +266,34 @@ static int VisualsTable(lua_State* L, const void* data) { const struct WeaponDef::Visuals& v = *static_cast(data); lua_createtable(L, 0, 28); - HSTR_PUSH_STRING(L, "modelName", modelLoader.FindModelPath(v.modelName)); - HSTR_PUSH_NUMBER(L, "colorR", v.color.x); - HSTR_PUSH_NUMBER(L, "colorG", v.color.y); - HSTR_PUSH_NUMBER(L, "colorB", v.color.z); - HSTR_PUSH_NUMBER(L, "color2R", v.color2.x); - HSTR_PUSH_NUMBER(L, "color2G", v.color2.y); - HSTR_PUSH_NUMBER(L, "color2B", v.color2.z); - HSTR_PUSH_BOOL (L, "smokeTrail", v.smokeTrail); - HSTR_PUSH_BOOL (L, "smokeTrailCastShadow", v.smokeTrailCastShadow); - HSTR_PUSH_NUMBER(L, "smokePeriod", v.smokePeriod); - HSTR_PUSH_NUMBER(L, "smokeTime", v.smokeTime); - HSTR_PUSH_NUMBER(L, "smokeSize", v.smokeSize); - HSTR_PUSH_NUMBER(L, "smokeColor", v.smokeColor); - HSTR_PUSH_NUMBER(L, "tileLength", v.tilelength); - HSTR_PUSH_NUMBER(L, "scrollSpeed", v.scrollspeed); - HSTR_PUSH_NUMBER(L, "pulseSpeed", v.pulseSpeed); - HSTR_PUSH_NUMBER(L, "laserFlareSize", v.laserflaresize); - HSTR_PUSH_NUMBER(L, "thickness", v.thickness); - HSTR_PUSH_NUMBER(L, "coreThickness", v.corethickness); - HSTR_PUSH_NUMBER(L, "beamDecay", v.beamdecay); - HSTR_PUSH_NUMBER(L, "stages", v.stages); - HSTR_PUSH_NUMBER(L, "sizeDecay", v.sizeDecay); - HSTR_PUSH_NUMBER(L, "alphaDecay", v.alphaDecay); - HSTR_PUSH_NUMBER(L, "separation", v.separation); - HSTR_PUSH_BOOL (L, "castShadow", v.castShadow); - HSTR_PUSH_BOOL (L, "noGap", v.noGap); - HSTR_PUSH_BOOL (L, "alwaysVisible", v.alwaysVisible); - HSTR_PUSH_BOOL (L, "beamWeapon", false); // DEPRECATED + LuaPushNamedString(L, "modelName", modelLoader.FindModelPath(v.modelName)); + LuaPushNamedNumber(L, "colorR", v.color.x); + LuaPushNamedNumber(L, "colorG", v.color.y); + LuaPushNamedNumber(L, "colorB", v.color.z); + LuaPushNamedNumber(L, "color2R", v.color2.x); + LuaPushNamedNumber(L, "color2G", v.color2.y); + LuaPushNamedNumber(L, "color2B", v.color2.z); + LuaPushNamedBool (L, "smokeTrail", v.smokeTrail); + LuaPushNamedBool (L, "smokeTrailCastShadow", v.smokeTrailCastShadow); + LuaPushNamedNumber(L, "smokePeriod", v.smokePeriod); + LuaPushNamedNumber(L, "smokeTime", v.smokeTime); + LuaPushNamedNumber(L, "smokeSize", v.smokeSize); + LuaPushNamedNumber(L, "smokeColor", v.smokeColor); + LuaPushNamedNumber(L, "tileLength", v.tilelength); + LuaPushNamedNumber(L, "scrollSpeed", v.scrollspeed); + LuaPushNamedNumber(L, "pulseSpeed", v.pulseSpeed); + LuaPushNamedNumber(L, "laserFlareSize", v.laserflaresize); + LuaPushNamedNumber(L, "thickness", v.thickness); + LuaPushNamedNumber(L, "coreThickness", v.corethickness); + LuaPushNamedNumber(L, "beamDecay", v.beamdecay); + LuaPushNamedNumber(L, "stages", v.stages); + LuaPushNamedNumber(L, "sizeDecay", v.sizeDecay); + LuaPushNamedNumber(L, "alphaDecay", v.alphaDecay); + LuaPushNamedNumber(L, "separation", v.separation); + LuaPushNamedBool (L, "castShadow", v.castShadow); + LuaPushNamedBool (L, "noGap", v.noGap); + LuaPushNamedBool (L, "alwaysVisible", v.alwaysVisible); + LuaPushNamedBool (L, "beamWeapon", false); // DEPRECATED return 1; } @@ -409,11 +409,11 @@ static int GuiSoundSetTable(lua_State* L, const void* data) const GuiSoundSetData& sound = soundSet.GetSoundData(i); - HSTR_PUSH_STRING(L, "name", sound.name); - HSTR_PUSH_NUMBER(L, "volume", sound.volume); + LuaPushNamedString(L, "name", sound.name); + LuaPushNamedNumber(L, "volume", sound.volume); if (!CLuaHandle::GetHandleSynced(L)) { - HSTR_PUSH_NUMBER(L, "id", sound.id); + LuaPushNamedNumber(L, "id", sound.id); } lua_rawset(L, -3); diff --git a/rts/Lua/LuaZip.cpp b/rts/Lua/LuaZip.cpp index daf209b7d67..ba0dc62eee3 100644 --- a/rts/Lua/LuaZip.cpp +++ b/rts/Lua/LuaZip.cpp @@ -36,6 +36,7 @@ #include "LuaZip.h" #include "LuaInclude.h" #include "LuaHashString.h" +#include "LuaUtils.h" #include "System/FileSystem/Archives/IArchive.h" #include "System/FileSystem/ArchiveLoader.h" #include "System/FileSystem/DataDirsAccess.h" @@ -79,7 +80,7 @@ bool LuaZipFileWriter::PushUnsynced(lua_State* L) CreateMetatable(L); // FIXME when this is enabled LuaGaia/LuaRules unsynced has access to it too! - //HSTR_PUSH_CFUNC(L, "CreateZipFileWriter", open); + // LuaPushNamedCFunc(L, "CreateZipFileWriter", open); return true; } @@ -93,10 +94,10 @@ bool LuaZipFileWriter::CreateMetatable(lua_State* L) lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); - HSTR_PUSH_CFUNC(L, "__gc", meta_gc); - HSTR_PUSH_CFUNC(L, "close", meta_gc); - HSTR_PUSH_CFUNC(L, "open", meta_open); - HSTR_PUSH_CFUNC(L, "write", meta_write); + LuaPushNamedCFunc(L, "__gc", meta_gc); + LuaPushNamedCFunc(L, "close", meta_gc); + LuaPushNamedCFunc(L, "open", meta_open); + LuaPushNamedCFunc(L, "write", meta_write); lua_pop(L, 1); return true; @@ -238,7 +239,7 @@ bool LuaZipFileReader::PushUnsynced(lua_State* L) CreateMetatable(L); // FIXME when this is enabled LuaGaia/LuaRules unsynced has access to it too! - //HSTR_PUSH_CFUNC(L, "CreateZipFileReader", open); + // LuaPushNamedCFunc(L, "CreateZipFileReader", open); return true; } @@ -252,10 +253,10 @@ bool LuaZipFileReader::CreateMetatable(lua_State* L) lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); - HSTR_PUSH_CFUNC(L, "__gc", meta_gc); - HSTR_PUSH_CFUNC(L, "close", meta_gc); - HSTR_PUSH_CFUNC(L, "open", meta_open); - HSTR_PUSH_CFUNC(L, "read", meta_read); + LuaPushNamedCFunc(L, "__gc", meta_gc); + LuaPushNamedCFunc(L, "close", meta_gc); + LuaPushNamedCFunc(L, "open", meta_open); + LuaPushNamedCFunc(L, "read", meta_read); lua_pop(L, 1); return true; diff --git a/rts/Lua/library/Types.lua b/rts/Lua/library/Types.lua index e7d8e0d5ee3..7fc423fd63d 100644 --- a/rts/Lua/library/Types.lua +++ b/rts/Lua/library/Types.lua @@ -109,6 +109,94 @@ ---@field height number? Camera distance from the ground. (ta) ---@field oldHeight number? Camera distance from the ground, cannot be changed. (rot) +-------------------------------------------------------------------------------- +-- Object IDs +-------------------------------------------------------------------------------- + +---Identifier of a unit currently present in the simulation. +--- +---IDs are drawn from a pool and are recycled, so the ID of a dead unit may +---later be handed out to a different unit. +--- +---@alias UnitID integer + +---Identifier of a unit definition, i.e. an index into `UnitDefs`. +--- +---Valid IDs start at `1`; `0` is not a valid unit definition. +--- +---@alias UnitDefID integer + +---Identifier of a feature currently present in the simulation. +--- +---IDs are drawn from a pool and are recycled, so the ID of a destroyed feature +---may later be handed out to a different feature. +--- +---@alias FeatureID integer + +---Identifier of a feature definition, i.e. an index into `FeatureDefs`. +--- +---@alias FeatureDefID integer + +---Identifier of a solid object, i.e. either a unit or a feature. +--- +---Unit and feature IDs live in separate ranges, so which of the two is meant +---follows from context: `Spring.UnitRendering` functions take a unit ID where +---`Spring.FeatureRendering` functions take a feature ID, and callins that pass +---an object ID pass the type alongside it. +--- +---@alias ObjectID UnitID|FeatureID + +---Identifier of a projectile currently present in the simulation. +--- +---Synced and unsynced projectiles are numbered independently. +--- +---@alias ProjectileID integer + +---Identifier of a weapon definition, i.e. an index into `WeaponDefs`, +---or a negated `CSolidObject::DamageType` +--- +---@alias WeaponDefID integer + +---Identifier of a ground decal. +--- +---@alias DecalID integer + +-------------------------------------------------------------------------------- +-- Player and team IDs +-------------------------------------------------------------------------------- + +---Identifier of a team. +--- +---Teams are numbered from `0`; `Spring.GetGaiaTeamID` returns the Gaia team. +--- +---@alias TeamID integer + +---Identifier of an allyteam. +--- +---Allyteams are numbered from `0`. +--- +---@alias AllyTeamID integer + +---Identifier of a player. +--- +---Players are numbered from `0`. Note that a player is a human client, which is +---not the same thing as a team. +--- +---@alias PlayerID integer + +-------------------------------------------------------------------------------- +-- Unit groups +-------------------------------------------------------------------------------- + +---Identifier of a unit (control) group. +--- +---Groups are per-team. IDs `0` to `9` are the hot-key groups that players can +---create and select directly; IDs from `10` up are "special" groups that can +---only be created programmatically. Interfaces that accept or return a group +---for a unit use `-1` to mean "no group". +--- +---@alias GroupID integer + -------------------------------------------------------------------------------- -- Resources -------------------------------------------------------------------------------- diff --git a/rts/Map/Generation/BlankMapGenerator.cpp b/rts/Map/Generation/BlankMapGenerator.cpp index d86fca00466..88c0f0c07ac 100644 --- a/rts/Map/Generation/BlankMapGenerator.cpp +++ b/rts/Map/Generation/BlankMapGenerator.cpp @@ -11,6 +11,8 @@ #include "System/FileSystem/VFSHandler.h" #include "System/Log/ILog.h" +#include "fmt/ranges.h" + #include "lib/squish/squish.h" #include @@ -20,6 +22,27 @@ #include // strcpy,memset #include +namespace { + +std::string EscapeLuaString(const std::string& input) +{ + std::string escaped; + escaped.reserve(input.size() + 2); + + escaped += '\"'; + for (char c: input) { + if (c == '\\' || c == '"') + escaped += '\\'; + + escaped += c; + } + escaped += '\"'; + + return escaped; +} + +} // namespace + #include "System/Misc/TracyDefs.h" CBlankMapGenerator::CBlankMapGenerator(const CGameSetup* setup) @@ -211,6 +234,13 @@ void CBlankMapGenerator::GenerateSMF(CVirtualFile* fileSMF) void CBlankMapGenerator::GenerateMapInfo(CVirtualFile* fileMapInfo) { RECOIL_DETAILED_TRACY_ZONE; + const auto& mapOpts = setup->GetMapOptionsCont(); + auto GetBlankMapOpt = [&](const std::string& key) -> std::string { + const std::string blankMapKey = "blank_map_" + key; + const std::string *const optValue = Recoil::map_try_get(mapOpts, blankMapKey); + return optValue ? *optValue : ""; + }; + //Open template mapinfo.lua const std::string luaTemplate = "mapgenerator/mapinfo_template.lua"; CFileHandler fh(luaTemplate, SPRING_VFS_PWD_ALL); @@ -228,11 +258,36 @@ void CBlankMapGenerator::GenerateMapInfo(CVirtualFile* fileMapInfo) } startPosString = ss.str(); + std::array splatTexScaleValues = {{"0.02", "0.02", "0.02", "0.02"}}; + std::array splatTexMultValues = {{"1.0" , "1.0" , "1.0" , "1.0" }}; + for (int i = 0; i < 4; ++i) { + if (const std::string texScale = GetBlankMapOpt(IntToString(i + 1, "splattexscale%i")); !texScale.empty()) + splatTexScaleValues[i] = texScale; + if (const std::string texMult = GetBlankMapOpt(IntToString(i + 1, "splattexmult%i")); !texMult.empty()) + splatTexMultValues [i] = texMult; + } + + bool splatDetailNormalDiffuseAlpha = StringToBool(GetBlankMapOpt("splatdetailnormaldiffusealpha")); + + const auto StringOrNil = [] (const std::string &str) { + return str.empty() ? "nil" : EscapeLuaString(str); + }; + //Replace tags in mapinfo.lua luaInfo = StringReplace(luaInfo, "${NAME}", setup->mapName); luaInfo = StringReplace(luaInfo, "${DESCRIPTION}", mapDescription); luaInfo = StringReplace(luaInfo, "${START_POSITIONS}", startPosString); + luaInfo = StringReplace(luaInfo, "${SPLAT_TEXSCALES}", fmt::format("{}", fmt::join(splatTexScaleValues, ", "))); + luaInfo = StringReplace(luaInfo, "${SPLAT_TEXMULTS}", fmt::format("{}", fmt::join(splatTexMultValues, ", "))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_TEX}", StringOrNil(GetBlankMapOpt("splatdetailtex"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DISTR_TEX}", StringOrNil(GetBlankMapOpt("splatdistr"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_NORMAL_TEX_1}", StringOrNil(GetBlankMapOpt("splatdetailnormaltex1"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_NORMAL_TEX_2}", StringOrNil(GetBlankMapOpt("splatdetailnormaltex2"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_NORMAL_TEX_3}", StringOrNil(GetBlankMapOpt("splatdetailnormaltex3"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_NORMAL_TEX_4}", StringOrNil(GetBlankMapOpt("splatdetailnormaltex4"))); + luaInfo = StringReplace(luaInfo, "${SPLAT_DETAIL_NORMAL_DIFFUSE_ALPHA}", splatDetailNormalDiffuseAlpha ? "true" : "false"); + //Copy to filebuffer fileMapInfo->buffer.assign(luaInfo.begin(), luaInfo.end()); } diff --git a/rts/Map/SMF/SMFReadMap.cpp b/rts/Map/SMF/SMFReadMap.cpp index 4b007810a9c..436e7e27944 100644 --- a/rts/Map/SMF/SMFReadMap.cpp +++ b/rts/Map/SMF/SMFReadMap.cpp @@ -78,9 +78,6 @@ CSMFReadMap::CSMFReadMap(const std::string& mapName): CEventClient("[CSMFReadMap haveSplatNormalDistribTexture |= !texName.empty(); } - // Detail Normal Splatting requires at least one splatDetailNormalTexture and a distribution texture - haveSplatNormalDistribTexture &= !mapInfo->smf.splatDistrTexName.empty(); - ParseHeader(); LoadHeightMap(); CReadMap::Initialize(); @@ -252,17 +249,27 @@ void CSMFReadMap::CreateSpecularTex() void CSMFReadMap::CreateSplatDetailTextures() { RECOIL_DETAILED_TRACY_ZONE; - if (!haveSplatDetailDistribTexture) + if (!haveSplatDetailDistribTexture && !haveSplatNormalDistribTexture) return; + if (haveSplatNormalDistribTexture && !haveSplatDetailDistribTexture) { + LOG_L(L_DEBUG, "[CSMFReadMap::%s] DNTS active without complete classic splat pair; using fallback splatDetailTex/splatDistrTex", __func__); + } + { CBitmap splatDetailTexBM; + const bool haveSplatDetailTexName = !mapInfo->smf.splatDetailTexName.empty(); // if a map supplies an intensity- AND a distribution-texture for // detail-splat blending, the regular detail-texture is not used // default detail-texture should be all-grey - if (!splatDetailTexBM.Load(mapInfo->smf.splatDetailTexName)) { - LOG_L(L_WARNING, "[CSMFReadMap::%s] Invalid SMF splatDetailTex %s. Creating fallback texture", __func__, mapInfo->smf.splatDetailTexName.c_str()); + if (!haveSplatDetailTexName || !splatDetailTexBM.Load(mapInfo->smf.splatDetailTexName)) { + if (haveSplatDetailTexName) { + LOG_L(L_WARNING, "[CSMFReadMap::%s] Invalid SMF splatDetailTex %s. Creating fallback texture", __func__, mapInfo->smf.splatDetailTexName.c_str()); + } else { + LOG_L(L_DEBUG, "[CSMFReadMap::%s] Missing SMF splatDetailTex. Creating fallback texture", __func__); + } + splatDetailTexBM.AllocDummy(SColor(127, 127, 127, 127)); } @@ -272,9 +279,15 @@ void CSMFReadMap::CreateSplatDetailTextures() { CBitmap splatDistrTexBM; + const bool haveSplatDistrTexName = !mapInfo->smf.splatDistrTexName.empty(); + + if (!haveSplatDistrTexName || !splatDistrTexBM.Load(mapInfo->smf.splatDistrTexName)) { + if (haveSplatDistrTexName) { + LOG_L(L_WARNING, "[CSMFReadMap::%s] Invalid SMF splatDistrTex %s. Creating fallback texture", __func__, mapInfo->smf.splatDistrTexName.c_str()); + } else { + LOG_L(L_DEBUG, "[CSMFReadMap::%s] Missing SMF splatDistrTex. Creating fallback texture", __func__); + } - if (!splatDistrTexBM.Load(mapInfo->smf.splatDistrTexName)) { - LOG_L(L_WARNING, "[CSMFReadMap::%s] Invalid SMF splatDistrTex %s. Creating fallback texture", __func__, mapInfo->smf.splatDistrTexName.c_str()); splatDistrTexBM.AllocDummy(SColor(255, 0, 0, 0)); } @@ -286,6 +299,8 @@ void CSMFReadMap::CreateSplatDetailTextures() if (!haveSplatNormalDistribTexture) return; + uint32_t loadedSplatNormals = 0; + for (size_t i = 0; i < mapInfo->smf.splatDetailNormalTexNames.size(); i++) { if (i == NUM_SPLAT_DETAIL_NORMALS) break; @@ -302,8 +317,11 @@ void CSMFReadMap::CreateSplatDetailTextures() splatNormalTextures[i].SetRawTexID(splatDetailNormalTextureBM.CreateMipMapTexture(texAnisotropyLevels[true], 0.0f, 0)); splatNormalTextures[i].SetRawSize(int2(splatDetailNormalTextureBM.xsize, splatDetailNormalTextureBM.ysize)); + loadedSplatNormals += (splatNormalTextures[i].GetID() != 0); } + LOG_L(L_DEBUG, "[CSMFReadMap::%s] Loaded %u DNTS splat normal textures", __func__, loadedSplatNormals); + } diff --git a/rts/Map/SMF/SMFRenderState.cpp b/rts/Map/SMF/SMFRenderState.cpp index b116563bd87..67ec03ff6e4 100644 --- a/rts/Map/SMF/SMFRenderState.cpp +++ b/rts/Map/SMF/SMFRenderState.cpp @@ -94,6 +94,12 @@ void SMFRenderStateGLSL::Update( for (uint32_t n = GLSL_SHADER_FWD_ADV; n <= GLSL_SHADER_DFR_ADV; n++) { glslShaders[n]->LoadFromID(luaMapShaderData->shaderIDs[n - 1]); } + + // currShader is null from Init() (GLSL_SHADER_FWD_STD is never created for + // the Lua state), so re-evaluate it now that program IDs changed; otherwise + // HasValidShader(Normal) stays false and a forward-only Lua map shader is + // never selected until a deferred Lua draw happens to run first + SetCurrentShader(smfGroundDrawer, DrawPass::Normal); } else { assert(luaMapShaderData == nullptr); diff --git a/rts/Menu/SelectionWidget.cpp b/rts/Menu/SelectionWidget.cpp index 7fbbf7b64f4..058dbebb40c 100644 --- a/rts/Menu/SelectionWidget.cpp +++ b/rts/Menu/SelectionWidget.cpp @@ -1,6 +1,7 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ #include "SelectionWidget.h" +#include "System/LoadSave/DemoFileExtension.h" ///[maint]#ifndef HEADLESS #include @@ -104,7 +105,15 @@ void SelectionWidget::ShowDemoList(const std::function const std::string dir = FileSystem::EnsurePathSepAtEnd("demos"); // FIXME: names overflow the box - for (const std::string& demo: dataDirsAccess.FindFiles(cwd + dir, "*.sdfz", 0)) { + const auto exts = GetDemoFileExtensions(); + std::string joined; + for (const auto& ext : exts) { + if (!joined.empty()) + joined += ','; + joined += ext; + } + const auto pattern = std::format("*.{{{}}}", joined); + for (const std::string& demo : dataDirsAccess.FindFiles(cwd + dir, pattern, 0)) { curSelect->list->AddItem(demo.substr(demo.find(dir) + 6), ""); } diff --git a/rts/Net/GameServer.cpp b/rts/Net/GameServer.cpp index 4f8a7800c50..9206f9a3c86 100644 --- a/rts/Net/GameServer.cpp +++ b/rts/Net/GameServer.cpp @@ -404,7 +404,6 @@ void CGameServer::SkipTo(int targetFrameNum) { const bool wasPaused = isPaused; - if (!gameHasStarted) { return; } if (serverFrameNum >= targetFrameNum) { return; } if (demoReader == nullptr) { return; } @@ -412,13 +411,16 @@ void CGameServer::SkipTo(int targetFrameNum) CommandMessage endMsg("skip end", SERVER_PLAYER); Broadcast(std::shared_ptr(startMsg.Pack())); + if (!gameHasStarted) + StartGame(true); // this skips the countdown + // fast-read and send demo data // // note that we must maintain ourselves // since we do we NOT go through ::Update when skipping while (SendDemoData(targetFrameNum)) { gameTime = GetDemoTime(); - modGameTime = demoReader->GetModGameTime() + 0.001f; + modGameTime = demoReader->GetNextDemoReadTime() + 0.001f; if (udpListener == nullptr) { continue; } if ((serverFrameNum % 20) != 0) { continue; } @@ -3094,6 +3096,12 @@ unsigned CGameServer::BindConnection( } } + // inform player of the current frame + if (gameHasStarted) { + CBaseNetProtocol::PacketType progressPacket = CBaseNetProtocol::Get().SendCurrentFrameProgress(serverFrameNum); + newPlayer.SendData(progressPacket); + } + // finally send player all packets he missed until now for (const std::shared_ptr& p: packetCache) newPlayer.SendData(p); diff --git a/rts/Net/NetCommands.cpp b/rts/Net/NetCommands.cpp index 124d44b8175..55d5d0071d8 100644 --- a/rts/Net/NetCommands.cpp +++ b/rts/Net/NetCommands.cpp @@ -623,6 +623,12 @@ void CGame::ClientReadNet() ASSERT_SYNCED(CSyncChecker::GetChecksum()); clientNet->Send(CBaseNetProtocol::Get().SendSyncResponse(gu->myPlayerNum, gs->frameNum, CSyncChecker::GetChecksum())); + // Cache the just-closed frame's checksum so Lua can read a + // stable value during the next sim frame via + // Spring.GetPrevFrameSyncChecksum(). Must run before the + // 4096-frame reset below so we capture the pre-reset value. + CSyncChecker::SetPrevChecksum(CSyncChecker::GetChecksum()); + // buffer all checksums, so we can check sync later between demo & local if (haveServerDemo) localSyncChecksums[gs->frameNum] = CSyncChecker::GetChecksum(); diff --git a/rts/Rendering/Common/ModelDrawerState.cpp b/rts/Rendering/Common/ModelDrawerState.cpp index b732a8d22c7..3aaa4c8c94c 100644 --- a/rts/Rendering/Common/ModelDrawerState.cpp +++ b/rts/Rendering/Common/ModelDrawerState.cpp @@ -263,6 +263,10 @@ CModelDrawerStateGL4::CModelDrawerStateGL4() modelShaders[n]->SetFlag("GBUFFER_MISCTEX_IDX", GL::GeometryBuffer::ATTACHMENT_MISCTEX); modelShaders[n]->SetFlag("GBUFFER_ZVALTEX_IDX", GL::GeometryBuffer::ATTACHMENT_ZVALTEX); + // name the matrix-mode values the shader compares against, so it reads modes by name + modelShaders[n]->SetFlag("MATMODE_STATIC", static_cast(ShaderMatrixModes::STATIC_MATMODE)); + modelShaders[n]->SetFlag("MATMODE_ARRAY", static_cast(ShaderMatrixModes::ARRAY_MATMODE)); + modelShaders[n]->Link(); modelShaders[n]->Enable(); modelShaders[n]->Disable(); diff --git a/rts/Rendering/Env/GrassDrawer.cpp b/rts/Rendering/Env/GrassDrawer.cpp index c667db7452a..b286943b5de 100644 --- a/rts/Rendering/Env/GrassDrawer.cpp +++ b/rts/Rendering/Env/GrassDrawer.cpp @@ -373,7 +373,7 @@ void CGrassDrawer::EnableShader(const GrassShaderProgram type) { grassShader->SetUniform3v("ambientLightColor", &sunLighting->modelAmbientColor.x); grassShader->SetUniform3v("diffuseLightColor", &sunLighting->modelDiffuseColor.x); grassShader->SetUniform3v("specularLightColor", &sunLighting->modelSpecularColor.x); - grassShader->SetUniform3v("sunDir", &mapInfo->light.sunDir.x); + grassShader->SetUniform3v("sunDir", &ISky::GetSky()->GetLight()->GetLightDir().x); } @@ -1062,5 +1062,3 @@ void CGrassDrawer::UnsyncedHeightMapUpdate(const SRectangle& rect) } } } - - diff --git a/rts/Rendering/Env/ISky.cpp b/rts/Rendering/Env/ISky.cpp index 1e44ecabeb0..b662d8293e5 100644 --- a/rts/Rendering/Env/ISky.cpp +++ b/rts/Rendering/Env/ISky.cpp @@ -89,6 +89,30 @@ void ISky::SetSky() } } +void ISky::SetSkyLuaTexture(const MapTextureData& td) +{ + if (sky == nullptr) + return; + + /* TODO: consider if perhaps there should be some way to set the + * sky to one of the other classes (CModernSky, etc) via Lua. */ + + if (td.id != 0u && dynamic_cast(sky.get()) == nullptr) { + auto luaSky = std::make_unique(td.id, td.size.x, td.size.y); + + if (luaSky->IsValid()) { + sky = std::move(luaSky); + return; + } + + LOG_L(L_WARNING, "[ISky::%s] failed to create SkyBox from Lua texture (%u), keeping current sky", __func__, td.id); + return; + } + + /* FIXME: td.id == 0 reaches here. Untested in recent times */ + sky->SetLuaTexture(td); +} + void ISky::SetSkyAxisAngle(const float4& skyAxisAngleRaw) { auto axis = float3{ skyAxisAngleRaw.x, skyAxisAngleRaw.y, skyAxisAngleRaw.z }; diff --git a/rts/Rendering/Env/ISky.h b/rts/Rendering/Env/ISky.h index e0632e96d5d..62fc19d188c 100644 --- a/rts/Rendering/Env/ISky.h +++ b/rts/Rendering/Env/ISky.h @@ -50,6 +50,7 @@ class ISky void SetUpdated() { updated = true; } public: static void SetSky(); + static void SetSkyLuaTexture(const MapTextureData& td); static auto& GetSky() { return sky; } static void KillSky() { sky = nullptr; } public: diff --git a/rts/Rendering/IconHandler.h b/rts/Rendering/IconHandler.h index 6c67749d739..ec0e5bce6e3 100644 --- a/rts/Rendering/IconHandler.h +++ b/rts/Rendering/IconHandler.h @@ -13,7 +13,7 @@ #include "Rendering/GL/RenderBuffersFwd.h" #include "Rendering/Textures/TextureAtlas.h" -class UnitDef; +struct UnitDef; class CTextureRenderAtlas; namespace icon { diff --git a/rts/Rendering/Models/3DModelVAO.cpp b/rts/Rendering/Models/3DModelVAO.cpp index 7be3e00733f..ae9d82d799c 100644 --- a/rts/Rendering/Models/3DModelVAO.cpp +++ b/rts/Rendering/Models/3DModelVAO.cpp @@ -286,14 +286,28 @@ void S3DModelVAO::DrawElements(GLenum prim, uint32_t vboIndxStart, uint32_t vboI glDrawElements(prim, vboIndxCount, GL_UNSIGNED_INT, indxVBO.GetPtr(vboIndxStart * sizeof(uint32_t))); } +bool S3DModelVAO::EmplaceInstance(uint32_t indexStart, uint32_t indexCount, uint32_t traIndex, uint16_t paletteIndex, uint16_t numPieces, uint32_t uniIndex, uint32_t bposeIndex) +{ + RECOIL_DETAILED_TRACY_ZONE; + if (traIndex == TransformsMemStorage::INVALID_INDEX || bposeIndex == TransformsMemStorage::INVALID_INDEX) + return false; + + modelDataToInstance[SIndexAndCount{ indexStart, indexCount }].emplace_back(SInstanceData( + traIndex, + paletteIndex, + numPieces, + uniIndex, + bposeIndex + )); + + return true; +} + template bool S3DModelVAO::AddToSubmissionImpl(const TObj* obj, uint32_t indexStart, uint32_t indexCount, uint16_t paletteIndex) { RECOIL_DETAILED_TRACY_ZONE; const auto traIndex = transformsUploader.GetElemOffset(obj); - if (traIndex == TransformsMemStorage::INVALID_INDEX) - return false; - const auto uniIndex = modelUniformsStorage.GetObjOffset(obj); //doesn't need to exist for defs and models. Don't check for validity uint16_t numPieces = 0; @@ -307,19 +321,14 @@ bool S3DModelVAO::AddToSubmissionImpl(const TObj* obj, uint32_t indexStart, uint bposeIndex = transformsUploader.GetElemOffset(obj->model); } - if (bposeIndex == TransformsMemStorage::INVALID_INDEX) - return false; - - auto& modelInstanceData = modelDataToInstance[SIndexAndCount{ indexStart, indexCount }]; - modelInstanceData.emplace_back(SInstanceData( + return EmplaceInstance( + indexStart, indexCount, static_cast(traIndex), paletteIndex, numPieces, static_cast(uniIndex), static_cast(bposeIndex) - )); - - return true; + ); } bool S3DModelVAO::AddToSubmission(const S3DModel* model, uint16_t paletteIndex) @@ -363,6 +372,22 @@ bool S3DModelVAO::AddToSubmission(const UnitDef* unitDef, uint16_t paletteIndex) return AddToSubmissionImpl(unitDef, model->indxStart, model->indxCount, paletteIndex); } +bool S3DModelVAO::AddStaticInstance(const S3DModel* model, uint32_t worldTransformOffset, uint16_t paletteIndex) +{ + RECOIL_DETAILED_TRACY_ZONE; + assert(model); + + // the world transform is the caller-supplied slot; pieces are read from the model bind pose + return EmplaceInstance( + model->indxStart, model->indxCount, + worldTransformOffset, + paletteIndex, + static_cast(model->numPieces), + static_cast(modelUniformsStorage.GetObjOffset(model)), + static_cast(transformsUploader.GetElemOffset(model)) + ); +} + void S3DModelVAO::Submit(GLenum mode, bool bindUnbind) { diff --git a/rts/Rendering/Models/3DModelVAO.hpp b/rts/Rendering/Models/3DModelVAO.hpp index 4a5a6f359d1..e64f3fba517 100644 --- a/rts/Rendering/Models/3DModelVAO.hpp +++ b/rts/Rendering/Models/3DModelVAO.hpp @@ -71,6 +71,9 @@ class S3DModelVAO { bool AddToSubmission(const CFeature* feature); bool AddToSubmission(const UnitDef* unitDef, uint16_t paletteIndex); + + bool AddStaticInstance(const S3DModel* model, uint32_t worldTransformOffset, uint16_t paletteIndex); + void Submit(GLenum mode = GL_TRIANGLES, bool bindUnbind = false); bool SubmitImmediately(const S3DModel* model, uint16_t paletteIndex, GLenum mode = GL_TRIANGLES, bool bindUnbind = false); @@ -102,6 +105,17 @@ class S3DModelVAO { uint32_t indexCount, uint16_t paletteIndex ); + // build one SInstanceData from already-resolved offsets and queue it for the next Submit(); + // returns false (drawing nothing) if the world transform or bind pose is unavailable. + bool EmplaceInstance( + uint32_t indexStart, + uint32_t indexCount, + uint32_t traIndex, + uint16_t paletteIndex, + uint16_t numPieces, + uint32_t uniIndex, + uint32_t bposeIndex + ); void EnableAttribs(bool inst) const; void DisableAttribs() const; private: diff --git a/rts/Rendering/Shaders/Shader.h b/rts/Rendering/Shaders/Shader.h index 90fa492d76d..8f0945ce655 100644 --- a/rts/Rendering/Shaders/Shader.h +++ b/rts/Rendering/Shaders/Shader.h @@ -131,6 +131,10 @@ namespace Shader { // not needed for pre-compiled programs shaderObjs.clear(); + // cached locations/values belong to the previous program; without this, + // uniforms set by name (e.g. texSquare) target stale locations after a + // Spring.SetMapShader program swap + uniformStates.clear(); } /// create the whole shader from a lua file diff --git a/rts/Rendering/Textures/TextureRenderAtlas.cpp b/rts/Rendering/Textures/TextureRenderAtlas.cpp index 87a275507ac..425a63f5085 100644 --- a/rts/Rendering/Textures/TextureRenderAtlas.cpp +++ b/rts/Rendering/Textures/TextureRenderAtlas.cpp @@ -356,11 +356,11 @@ bool CTextureRenderAtlas::CreateAtlasTexture() if (atlasRendered) return true; - LOG_L(L_INFO, "CTextureRenderAtlas::%s()[0] atlas=%s FBO::ready=%d", __func__, atlasName.c_str(), FBO::IsReady()); - if (!FBO::IsReady()) return false; + LOG_L(L_INFO, "CTextureRenderAtlas::%s()[0] atlas=%s FBO::ready=%d", __func__, atlasName.c_str(), FBO::IsReady()); + const auto numLevels = atlasAllocator->GetNumTexLevels(); const auto numPages = atlasAllocator->GetNumPages(); diff --git a/rts/Rendering/Textures/nv_dds.cpp b/rts/Rendering/Textures/nv_dds.cpp index a4792607e77..d055e0c30a7 100644 --- a/rts/Rendering/Textures/nv_dds.cpp +++ b/rts/Rendering/Textures/nv_dds.cpp @@ -8,11 +8,9 @@ // // Description: // -// Loads DDS images (DXTC1, DXTC3, DXTC5, RGB (888, 888X), and RGBA (8888) are -// supported) for use in OpenGL. Image is flipped when its loaded as DX images -// are stored with different coordinate system. If file has mipmaps and/or -// cubemaps then these are loaded as well. Volume textures can be loaded as -// well but they must be uncompressed. +// Loads DDS images for use in OpenGL. DXT1, DXT3, DXT5, BC4, BC5, BC7, +// RGB (888, 888X), and RGBA (8888) are supported. Images are flipped to match +// OpenGL coordinates, except BC7. Volume textures must be uncompressed. // // When multiple textures are loaded (i.e a volume or cubemap texture), // additional faces can be accessed using the array operator. @@ -349,6 +347,24 @@ bool CDDSImage::load(string filename, bool flipImage) file.Read(&ddsh.dwCaps2, tmp); file.Read(&ddsh.dwReserved2, tmp*3); + const bool hasDX10Header = ((swabDWord(ddsh.ddspf.dwFlags) & DDSF_FOURCC) && swabDWord(ddsh.ddspf.dwFourCC) == FOURCC_DX10); + DDS_HEADER_DXT10 ddsh10 = {0, 0, 0, 1, 0}; + if (hasDX10Header) + { + if (file.Read(&ddsh10.dxgiFormat, tmp) != tmp || + file.Read(&ddsh10.resourceDimension, tmp) != tmp || + file.Read(&ddsh10.miscFlag, tmp) != tmp || + file.Read(&ddsh10.arraySize, tmp) != tmp || + file.Read(&ddsh10.miscFlags2, tmp) != tmp) + return false; + + ddsh10.dxgiFormat = swabDWord(ddsh10.dxgiFormat); + ddsh10.resourceDimension = swabDWord(ddsh10.resourceDimension); + ddsh10.miscFlag = swabDWord(ddsh10.miscFlag); + ddsh10.arraySize = swabDWord(ddsh10.arraySize); + ddsh10.miscFlags2 = swabDWord(ddsh10.miscFlags2); + } + // if in VFS, read post-header data directly from buffer if (file.IsBuffered()) { fileBuf = std::move(file.GetBuffer()); @@ -374,16 +390,29 @@ bool CDDSImage::load(string filename, bool flipImage) ddsh.dwCaps1 = swabDWord(ddsh.dwCaps1); ddsh.dwCaps2 = swabDWord(ddsh.dwCaps2); + if (hasDX10Header && + (ddsh10.arraySize != 1 || + (ddsh10.resourceDimension != DX10_DIMENSION_TEXTURE2D && ddsh10.resourceDimension != DX10_DIMENSION_TEXTURE3D) || + ((ddsh10.miscFlag & DX10_MISC_TEXTURECUBE) && ddsh10.resourceDimension != DX10_DIMENSION_TEXTURE2D))) + return false; + // default to flat texture type (1D, 2D, or rectangle) m_type = TextureFlat; - // check if image is a cubemap - if (ddsh.dwCaps2 & DDSF_CUBEMAP) - m_type = TextureCubemap; - - // check if image is a volume texture - if ((ddsh.dwCaps2 & DDSF_VOLUME) && (ddsh.dwDepth > 0)) - m_type = Texture3D; + if (hasDX10Header) { + if (ddsh10.miscFlag & DX10_MISC_TEXTURECUBE) + m_type = TextureCubemap; + else if (ddsh10.resourceDimension == DX10_DIMENSION_TEXTURE3D) + m_type = Texture3D; + } else { + // check if image is a cubemap + if (ddsh.dwCaps2 & DDSF_CUBEMAP) + m_type = TextureCubemap; + + // check if image is a volume texture + if ((ddsh.dwCaps2 & DDSF_VOLUME) && (ddsh.dwDepth > 0)) + m_type = Texture3D; + } // figure out what the image format is if (ddsh.ddspf.dwFlags & DDSF_FOURCC) @@ -402,6 +431,51 @@ bool CDDSImage::load(string filename, bool flipImage) m_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; m_components = 4; break; + case FOURCC_ATI1: + case FOURCC_BC4U: + m_format = GL_COMPRESSED_RED_RGTC1; + m_components = 1; + break; + case FOURCC_ATI2: + case FOURCC_BC5U: + m_format = GL_COMPRESSED_RG_RGTC2; + m_components = 2; + break; + case FOURCC_DX10: + switch (ddsh10.dxgiFormat) + { + case DXGI_FORMAT_BC1_UNORM: + m_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + m_components = 3; + break; + case DXGI_FORMAT_BC2_UNORM: + m_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + m_components = 4; + break; + case DXGI_FORMAT_BC3_UNORM: + m_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + m_components = 4; + break; + case DXGI_FORMAT_BC4_UNORM: + m_format = GL_COMPRESSED_RED_RGTC1; + m_components = 1; + break; + case DXGI_FORMAT_BC5_UNORM: + m_format = GL_COMPRESSED_RG_RGTC2; + m_components = 2; + break; + case DXGI_FORMAT_BC7_UNORM: + m_format = GL_COMPRESSED_RGBA_BPTC_UNORM; + m_components = 4; + break; + case DXGI_FORMAT_BC7_UNORM_SRGB: + m_format = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; + m_components = 4; + break; + default: + return false; + } + break; default: //fclose(fp); return false; @@ -435,6 +509,11 @@ bool CDDSImage::load(string filename, bool flipImage) return false; } + if (flipImage && (m_format == GL_COMPRESSED_RGBA_BPTC_UNORM || m_format == GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM)) { + LOG_L(L_WARNING, "[nv_dds] cannot vertically flip BC7 image \"%s\", author it pre-flipped", filename.c_str()); + flipImage = false; + } + // store primary surface width/height/depth unsigned int width = ddsh.dwWidth; unsigned int height = ddsh.dwHeight; @@ -578,6 +657,12 @@ bool CDDSImage::save(std::string filename, bool flipImage) const assert(m_valid); assert(m_type != TextureNone); + if (is_compressed() && + m_format != GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && + m_format != GL_COMPRESSED_RGBA_S3TC_DXT3_EXT && + m_format != GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) + return false; + DDS_HEADER ddsh; unsigned int headerSize = sizeof(DDS_HEADER); memset(&ddsh, 0, headerSize); @@ -727,7 +812,11 @@ bool CDDSImage::is_compressed() const RECOIL_DETAILED_TRACY_ZONE; return ((m_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) || (m_format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) || - (m_format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)); + (m_format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) || + (m_format == GL_COMPRESSED_RED_RGTC1) || + (m_format == GL_COMPRESSED_RG_RGTC2) || + (m_format == GL_COMPRESSED_RGBA_BPTC_UNORM) || + (m_format == GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM)); } #ifndef HEADLESS @@ -969,7 +1058,7 @@ inline unsigned int CDDSImage::size_dxtc(unsigned int width, unsigned int height { RECOIL_DETAILED_TRACY_ZONE; return ((width+3)/4)*((height+3)/4)* - (m_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ? 8 : 16); + ((m_format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || m_format == GL_COMPRESSED_RED_RGTC1) ? 8 : 16); } /////////////////////////////////////////////////////////////////////////////// @@ -1030,6 +1119,14 @@ void CDDSImage::flip(CSurface &surface) const blocksize = 16; flipblocks = &CDDSImage::flip_blocks_dxtc5; break; + case GL_COMPRESSED_RED_RGTC1: + blocksize = 8; + flipblocks = &CDDSImage::flip_blocks_bc4; + break; + case GL_COMPRESSED_RG_RGTC2: + blocksize = 16; + flipblocks = &CDDSImage::flip_blocks_bc5; + break; default: return; } @@ -1219,6 +1316,40 @@ void CDDSImage::flip_blocks_dxtc5(DXTColBlock *line, unsigned int numBlocks) con } } +/////////////////////////////////////////////////////////////////////////////// +// flip a line of BC4 blocks +void CDDSImage::flip_blocks_bc4(DXTColBlock *line, unsigned int numBlocks) const +{ + RECOIL_DETAILED_TRACY_ZONE; + DXT5AlphaBlock *curblock = reinterpret_cast(line); + + for (unsigned int i = 0; i < numBlocks; i++) + { + flip_dxt5_alpha(curblock); + + curblock++; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// flip a line of BC5 blocks +void CDDSImage::flip_blocks_bc5(DXTColBlock *line, unsigned int numBlocks) const +{ + RECOIL_DETAILED_TRACY_ZONE; + DXT5AlphaBlock *curblock = reinterpret_cast(line); + + for (unsigned int i = 0; i < numBlocks; i++) + { + flip_dxt5_alpha(curblock); + + curblock++; + + flip_dxt5_alpha(curblock); + + curblock++; + } +} + /////////////////////////////////////////////////////////////////////////////// // CTexture implementation /////////////////////////////////////////////////////////////////////////////// diff --git a/rts/Rendering/Textures/nv_dds.h b/rts/Rendering/Textures/nv_dds.h index 1daec4cd83e..fb80331d23e 100644 --- a/rts/Rendering/Textures/nv_dds.h +++ b/rts/Rendering/Textures/nv_dds.h @@ -51,6 +51,25 @@ namespace nv_dds const unsigned int FOURCC_DXT1 = 0x31545844; //(MAKEFOURCC('D','X','T','1')) const unsigned int FOURCC_DXT3 = 0x33545844; //(MAKEFOURCC('D','X','T','3')) const unsigned int FOURCC_DXT5 = 0x35545844; //(MAKEFOURCC('D','X','T','5')) + const unsigned int FOURCC_ATI1 = 0x31495441; //(MAKEFOURCC('A','T','I','1')), BC4 + const unsigned int FOURCC_BC4U = 0x55344342; //(MAKEFOURCC('B','C','4','U')) + const unsigned int FOURCC_ATI2 = 0x32495441; //(MAKEFOURCC('A','T','I','2')), BC5 + const unsigned int FOURCC_BC5U = 0x55354342; //(MAKEFOURCC('B','C','5','U')) + const unsigned int FOURCC_DX10 = 0x30315844; //(MAKEFOURCC('D','X','1','0')) + + // Supported DXGI formats + const unsigned int DXGI_FORMAT_BC1_UNORM = 71; + const unsigned int DXGI_FORMAT_BC2_UNORM = 74; + const unsigned int DXGI_FORMAT_BC3_UNORM = 77; + const unsigned int DXGI_FORMAT_BC4_UNORM = 80; + const unsigned int DXGI_FORMAT_BC5_UNORM = 83; + const unsigned int DXGI_FORMAT_BC7_UNORM = 98; + const unsigned int DXGI_FORMAT_BC7_UNORM_SRGB = 99; + + // DDS_HEADER_DXT10 values + const unsigned int DX10_DIMENSION_TEXTURE2D = 3; + const unsigned int DX10_DIMENSION_TEXTURE3D = 4; + const unsigned int DX10_MISC_TEXTURECUBE = 0x00000004; struct DXTColBlock { @@ -85,6 +104,15 @@ namespace nv_dds unsigned int dwABitMask; }; + struct DDS_HEADER_DXT10 + { + unsigned int dxgiFormat; + unsigned int resourceDimension; + unsigned int miscFlag; + unsigned int arraySize; + unsigned int miscFlags2; + }; + struct DDS_HEADER { unsigned int dwSize; @@ -333,6 +361,8 @@ namespace nv_dds void flip_blocks_dxtc1(DXTColBlock *line, unsigned int numBlocks) const; void flip_blocks_dxtc3(DXTColBlock *line, unsigned int numBlocks) const; void flip_blocks_dxtc5(DXTColBlock *line, unsigned int numBlocks) const; + void flip_blocks_bc4(DXTColBlock *line, unsigned int numBlocks) const; + void flip_blocks_bc5(DXTColBlock *line, unsigned int numBlocks) const; void flip_dxt5_alpha(DXT5AlphaBlock *block) const; bool write_texture(const CTexture &texture, FILE *fp) const; diff --git a/rts/Rendering/Units/UnitDrawer.cpp b/rts/Rendering/Units/UnitDrawer.cpp index a439038dabe..9a99cdf6d09 100644 --- a/rts/Rendering/Units/UnitDrawer.cpp +++ b/rts/Rendering/Units/UnitDrawer.cpp @@ -2,6 +2,8 @@ #include "UnitDrawer.h" +#include + #include "Game/Camera.h" #include "Game/CameraHandler.h" #include "Game/Game.h" @@ -822,7 +824,7 @@ void CUnitDrawerGLSL::DrawAlphaObjects(int modelType, bool drawReflection, bool CModelDrawerHelper::BindModelTypeTexture(modelType, mdlRenderer.GetObjectBinKey(i)); for (auto* o : mdlRenderer.GetObjectBin(i)) { - DrawAlphaUnit(o, modelType, thisPassMask, false); + DrawAlphaUnit(o, thisPassMask); } } @@ -869,66 +871,35 @@ void CUnitDrawerGLSL::DrawGhostedBuildings(int modelType) const glColor4f(0.6f, 0.6f, 0.6f, IModelDrawerState::alphaValues.y); // buildings that died while ghosted - for (GhostSolidObject* dgb : deadGhostedBuildings) { - if (camera->InView(dgb->pos, dgb->GetModel()->GetDrawRadius())) { - glPushMatrix(); - glTranslatef3(dgb->pos); - glRotatef(dgb->facing * 90.0f, 0, 1, 0); + for (const GhostSolidObject* dgb : deadGhostedBuildings) { + const S3DModel* model = dgb->GetModel(); + if (!camera->InView(dgb->pos, model->GetDrawRadius())) + continue; - CModelDrawerHelper::BindModelTypeTexture(modelType, dgb->GetModel()->textureType); - SetTeamColor(dgb->team, IModelDrawerState::alphaValues.y); + glPushMatrix(); + glTranslatef3(dgb->pos); + glRotatef(dgb->facing * 90.0f, 0, 1, 0); - dgb->GetModel()->DrawStatic(); - glPopMatrix(); - } - } + CModelDrawerHelper::BindModelTypeTexture(modelType, model->textureType); + SetTeamColor(dgb->team, IModelDrawerState::alphaValues.y); - for (CUnit* lgb : liveGhostedBuildings) { - DrawAlphaUnit(lgb, modelType, DrawFlags::SO_ALPHAF_FLAG, true); + model->DrawStatic(); + glPopMatrix(); } -} - -void CUnitDrawerGLSL::DrawOpaqueUnit(CUnit* unit, uint8_t thisPassMask) const -{ - RECOIL_DETAILED_TRACY_ZONE; - if (!ShouldDrawOpaqueUnit(unit, thisPassMask)) - return; - - // draw the unit with the default (non-Lua) material - SetTeamColor(unit->team); - DrawUnitTrans(unit, 0, 0, false, false); -} - -void CUnitDrawerGLSL::DrawUnitShadow(CUnit* unit) const -{ - RECOIL_DETAILED_TRACY_ZONE; - if (ShouldDrawUnitShadow(unit)) - DrawUnitTrans(unit, 0, 0, false, false); -} -void CUnitDrawerGLSL::DrawAlphaUnit(CUnit* unit, int modelType, uint8_t thisPassMask, bool drawGhostBuildingsPass) const -{ - RECOIL_DETAILED_TRACY_ZONE; - if (!drawGhostBuildingsPass && !ShouldDrawAlphaUnit(unit, thisPassMask)) - return; + // buildings that left LOS but are still alive + for (const auto& lgb : liveGhostedBuildings) { + const CUnit* unit = lgb.unit; - const unsigned short losStatus = unit->losStatus[gu->myAllyTeam]; - - if (drawGhostBuildingsPass) { // check for decoy models const UnitDef* decoyDef = unit->unitDef->decoyDef; - const S3DModel* model = nullptr; - - if (decoyDef == nullptr) { - model = unit->model; - } - else { - model = decoyDef->LoadModel(); - } + const S3DModel* model = (decoyDef == nullptr) ? unit->model : decoyDef->LoadModel(); // FIXME: needs a second pass if (model->type != modelType) - return; + continue; + + const unsigned short losStatus = unit->losStatus[gu->myAllyTeam]; // ghosted enemy units if (losStatus & LOS_CONTRADAR) { @@ -948,17 +919,45 @@ void CUnitDrawerGLSL::DrawAlphaUnit(CUnit* unit, int modelType, uint8_t thisPass // not actually cloaked CModelDrawerHelper::BindModelTypeTexture(modelType, model->textureType); - SetTeamColor(unit->team, (losStatus & LOS_CONTRADAR) ? IModelDrawerState::alphaValues.z : IModelDrawerState::alphaValues.y); + // color with the team the unit was last seen under, not the live unit's current team + const float ghostAlpha = (losStatus & LOS_CONTRADAR) ? IModelDrawerState::alphaValues.z : IModelDrawerState::alphaValues.y; + SetTeamColor(lgb.team, ghostAlpha); model->DrawStatic(); glPopMatrix(); glColor4f(1.0f, 1.0f, 1.0f, IModelDrawerState::alphaValues.x); - return; } +} + +void CUnitDrawerGLSL::DrawOpaqueUnit(CUnit* unit, uint8_t thisPassMask) const +{ + RECOIL_DETAILED_TRACY_ZONE; + if (!ShouldDrawOpaqueUnit(unit, thisPassMask)) + return; + + // draw the unit with the default (non-Lua) material + SetTeamColor(unit->team); + DrawUnitTrans(unit, 0, 0, false, false); +} + +void CUnitDrawerGLSL::DrawUnitShadow(CUnit* unit) const +{ + RECOIL_DETAILED_TRACY_ZONE; + if (ShouldDrawUnitShadow(unit)) + DrawUnitTrans(unit, 0, 0, false, false); +} + +void CUnitDrawerGLSL::DrawAlphaUnit(CUnit* unit, uint8_t thisPassMask) const +{ + RECOIL_DETAILED_TRACY_ZONE; + if (!ShouldDrawAlphaUnit(unit, thisPassMask)) + return; if (unit->GetIsIcon()) return; + const unsigned short losStatus = unit->losStatus[gu->myAllyTeam]; + if ((losStatus & LOS_INLOS) || gu->spectatingFullView) { SetTeamColor(unit->team, IModelDrawerState::alphaValues.x); DrawUnitTrans(unit, 0, 0, false, false); @@ -1738,92 +1737,119 @@ void CUnitDrawerGL4::DrawAlphaObjects(int modelType, bool drawReflection, bool d smv.Submit(GL_TRIANGLES, false); } - // void CGLUnitDrawer::DrawGhostedBuildings(int modelType) - if (gu->spectatingFullView) - return; + smv.Unbind(); - const auto& deadGhostBuildings = modelDrawerData->GetDeadGhostBuildings(gu->myAllyTeam, modelType); + // living and dead ghosted buildings + if (!gu->spectatingFullView) + DrawGhostedBuildings(modelType); +} - const auto oldMM = modelDrawerState->SetMatrixMode(ShaderMatrixModes::STATIC_MATMODE); - // deadGhostedBuildings - { - modelDrawerState->SetColorMultiplier(0.6f, 0.6f, 0.6f, IModelDrawerState::alphaValues.y); - modelDrawerState->SetTeamColor(0, IModelDrawerState::alphaValues.y); //teamID doesn't matter here +void CUnitDrawerGL4::DrawGhostedBuildings(int modelType) const +{ + RECOIL_DETAILED_TRACY_ZONE; - int prevModelType = -1; - int prevTexType = -1; - for (const auto* dgb : deadGhostBuildings) { - if (!camera->InView(dgb->pos, dgb->GetModel()->GetDrawRadius())) - continue; + auto& smv = S3DModelVAO::GetInstance(); + smv.Bind(); + + // Ghost buildings are static (no animation, never move), so each gets a single world-transform + // slot in the transforms SSBO and is drawn batched through ARRAY_MATMODE - one multidraw per + // (color bucket x texture type) instead of one immediate draw per ghost. + const auto oldMM = modelDrawerState->SetMatrixMode(ShaderMatrixModes::ARRAY_MATMODE); - static CMatrix44f staticWorldMat; + struct GhostInstance { + const S3DModel* model; + uint32_t worldTransformOffset; + uint16_t paletteIndex; // color the ghost was last seen under (see LiveGhostBuilding / GhostSolidObject) + }; + // bind the texture once per group, accumulate, then one Submit (=one multidraw) per texture type. + // buckets are reused across frames (see clearBuckets) so a screen full of ghosts does not realloc + // its per-texture vectors every frame; empty buckets (a texture no longer on screen) are skipped. + const auto flushGhosts = [&](const std::map>& byTex) { + for (const auto& [texType, instances] : byTex) { + if (instances.empty()) + continue; + CModelDrawerHelper::BindModelTypeTexture(modelType, texType); + for (const auto& gi : instances) + smv.AddStaticInstance(gi.model, gi.worldTransformOffset, gi.paletteIndex); + smv.Submit(GL_TRIANGLES, false); + } + }; + // clear the mapped vectors (keeping their capacity) instead of clearing the map (which would free them) + const auto clearBuckets = [](std::map>& byTex) { + for (auto& [texType, instances] : byTex) + instances.clear(); + }; - staticWorldMat.LoadIdentity(); - staticWorldMat.Translate(dgb->pos); + // deadGhostedBuildings (single color state) + { + const auto& deadGhostBuildings = modelDrawerData->GetDeadGhostBuildings(gu->myAllyTeam, modelType); - staticWorldMat.RotateY(-dgb->facing * math::DEG_TO_RAD * 90.0f); + static std::map> byTex; + clearBuckets(byTex); + bool any = false; + for (const auto* dgb : deadGhostBuildings) { + const S3DModel* model = dgb->GetModel(); + if (!camera->InView(dgb->pos, model->GetDrawRadius())) + continue; + if (!dgb->worldTransformAlloc.Valid()) + continue; - if (prevModelType != modelType || prevTexType != dgb->GetModel()->textureType) { - prevModelType = modelType; prevTexType = dgb->GetModel()->textureType; - CModelDrawerHelper::BindModelTypeTexture(modelType, dgb->GetModel()->textureType); //inefficient rendering, but w/e - } + byTex[model->textureType].push_back({ model, static_cast(dgb->worldTransformAlloc.GetOffset()), dgb->paletteIndex }); + any = true; + } - modelDrawerState->SetStaticModelMatrix(staticWorldMat); - smv.SubmitImmediately(dgb->GetModel(), static_cast(dgb->team)); //need to submit immediately every model because of static per-model matrix + if (any) { + modelDrawerState->SetColorMultiplier(0.6f, 0.6f, 0.6f, IModelDrawerState::alphaValues.y); + modelDrawerState->SetTeamColor(0, IModelDrawerState::alphaValues.y); //teamID is per-instance + flushGhosts(byTex); } } - // liveGhostedBuildings + // liveGhostedBuildings (two color states: normal and CONTRADAR) { const auto& liveGhostedBuildings = modelDrawerData->GetLiveGhostBuildings(gu->myAllyTeam, modelType); - int prevModelType = -1; - int prevTexType = -1; - for (const auto* lgb : liveGhostedBuildings) { - if (!camera->InView(lgb->pos, lgb->model->GetDrawRadius())) + static std::map> byTexNormal; + static std::map> byTexContradar; + clearBuckets(byTexNormal); + clearBuckets(byTexContradar); + bool anyNormal = false; + bool anyContradar = false; + + for (const auto& lgb : liveGhostedBuildings) { + const CUnit* u = lgb.unit; + if (!camera->InView(u->pos, u->model->GetDrawRadius())) continue; // check for decoy models - const UnitDef* decoyDef = lgb->unitDef->decoyDef; - const S3DModel* model = nullptr; - - if (decoyDef == nullptr) { - model = lgb->model; - } - else { - model = decoyDef->LoadModel(); - } + const UnitDef* decoyDef = u->unitDef->decoyDef; + const S3DModel* model = (decoyDef == nullptr) ? u->model : decoyDef->LoadModel(); // FIXME: needs a second pass if (model->type != modelType) continue; - static CMatrix44f staticWorldMat; - - staticWorldMat.LoadIdentity(); - staticWorldMat.Translate(lgb->pos); - - staticWorldMat.RotateY(-lgb->buildFacing * math::DEG_TO_RAD * 90.0f); - - const unsigned short losStatus = lgb->losStatus[gu->myAllyTeam]; - - // ghosted enemy units - if (losStatus & LOS_CONTRADAR) { - modelDrawerState->SetColorMultiplier(0.9f, 0.9f, 0.9f, IModelDrawerState::alphaValues.z); - modelDrawerState->SetTeamColor(lgb->team, IModelDrawerState::alphaValues.z); - } - else { - modelDrawerState->SetColorMultiplier(0.6f, 0.6f, 0.6f, IModelDrawerState::alphaValues.y); - modelDrawerState->SetTeamColor(lgb->team, IModelDrawerState::alphaValues.y); - } + const size_t xfOffset = modelDrawerData->GetLiveGhostTransform(u); + if (xfOffset == TransformsMemStorage::INVALID_INDEX) + continue; - if (prevModelType != modelType || prevTexType != model->textureType) { - prevModelType = modelType; prevTexType = model->textureType; - CModelDrawerHelper::BindModelTypeTexture(modelType, model->textureType); //inefficient rendering, but w/e - } + const unsigned short losStatus = u->losStatus[gu->myAllyTeam]; + const bool contradar = (losStatus & LOS_CONTRADAR); + // bucket with the palette the unit was last seen under, not the live unit's current one + (contradar ? byTexContradar : byTexNormal)[model->textureType] + .push_back({ model, static_cast(xfOffset), lgb.paletteIndex }); + (contradar ? anyContradar : anyNormal) = true; + } - modelDrawerState->SetStaticModelMatrix(staticWorldMat); - smv.SubmitImmediately(model, static_cast(lgb->team)); //need to submit immediately every model because of static per-model matrix + if (anyNormal) { + modelDrawerState->SetColorMultiplier(0.6f, 0.6f, 0.6f, IModelDrawerState::alphaValues.y); + modelDrawerState->SetTeamColor(0, IModelDrawerState::alphaValues.y); + flushGhosts(byTexNormal); + } + if (anyContradar) { + modelDrawerState->SetColorMultiplier(0.9f, 0.9f, 0.9f, IModelDrawerState::alphaValues.z); + modelDrawerState->SetTeamColor(0, IModelDrawerState::alphaValues.z); + flushGhosts(byTexContradar); } } diff --git a/rts/Rendering/Units/UnitDrawer.h b/rts/Rendering/Units/UnitDrawer.h index d6c6935afc6..97106632f76 100644 --- a/rts/Rendering/Units/UnitDrawer.h +++ b/rts/Rendering/Units/UnitDrawer.h @@ -167,7 +167,7 @@ class CUnitDrawerGLSL : public CUnitDrawerBase { void DrawOpaqueUnit(CUnit* unit, uint8_t thisPassMask) const; void DrawUnitShadow(CUnit* unit) const; - void DrawAlphaUnit(CUnit* unit, int modelType, uint8_t thisPassMask, bool drawGhostBuildingsPass) const; + void DrawAlphaUnit(CUnit* unit, uint8_t thisPassMask) const; void DrawOpaqueAIUnit(const CUnitDrawerData::TempDrawUnit& unit) const; void DrawAlphaAIUnit(const CUnitDrawerData::TempDrawUnit& unit) const; @@ -236,7 +236,7 @@ class CUnitDrawerGL4 final : public CUnitDrawerGLSL { void DrawOpaqueObjectsAux(int modelType) const override; void DrawOpaqueAIUnit(const CUnitDrawerData::TempDrawUnit& unit) const; - void DrawGhostedBuildings(int modelType) const override {} //implemented in-line + void DrawGhostedBuildings(int modelType) const override; void DrawUnitModelBeingBuiltShadow(const CUnit* unit, bool noLuaCall) const; void DrawUnitModelBeingBuiltOpaque(const CUnit* unit, bool noLuaCall) const; diff --git a/rts/Rendering/Units/UnitDrawerData.cpp b/rts/Rendering/Units/UnitDrawerData.cpp index 3f98205280a..dfd048a332a 100644 --- a/rts/Rendering/Units/UnitDrawerData.cpp +++ b/rts/Rendering/Units/UnitDrawerData.cpp @@ -29,9 +29,22 @@ #include "Map/ReadMap.h" #include "System/Misc/TracyDefs.h" +#include "System/Matrix44f.h" +#include "System/MathConstants.h" +#include "System/Transform.hpp" +#include "Rendering/Models/ModelsMemStorage.h" static FixedDynMemPoolT ghostMemPool; +// world transform of a (static) ghost building, matching the legacy staticModelMatrix construction +static Transform MakeGhostWorldTransform(const float3& pos, int facing) +{ + CMatrix44f m; + m.Translate(pos); + m.RotateY(-facing * math::HALFPI); + return Transform::FromMatrix(m); +} + /////////////////////////// CR_BIND_POOL(GhostSolidObject, ,ghostMemPool.allocMem, ghostMemPool.freeMem) @@ -48,13 +61,22 @@ CR_REG_METADATA(GhostSolidObject, ( CR_MEMBER(facing), CR_MEMBER(team), + CR_MEMBER(paletteIndex), CR_IGNORED(currentIconIndex), + CR_IGNORED(worldTransformAlloc), CR_IGNORED(model), CR_POSTLOAD(PostLoad) )) +CR_BIND(CUnitDrawerData::LiveGhostBuilding, ) +CR_REG_METADATA(CUnitDrawerData::LiveGhostBuilding, ( + CR_MEMBER(unit), + CR_MEMBER(paletteIndex), + CR_MEMBER(team) +)) + CR_BIND(CUnitDrawerData::TempDrawUnit, ) CR_REG_METADATA(CUnitDrawerData::TempDrawUnit, ( CR_MEMBER(unitDefId), @@ -88,6 +110,15 @@ void GhostSolidObject::PostLoad() RECOIL_DETAILED_TRACY_ZONE; model = nullptr; GetModel(); + + // the GPU world transform slot is render-only state; re-create it from the saved pos/facing + InitWorldTransform(); +} + +void GhostSolidObject::InitWorldTransform() +{ + worldTransformAlloc = ScopedTransformMemAlloc(1); + worldTransformAlloc.UpdateForced(0, MakeGhostWorldTransform(pos, facing)); } const S3DModel* GhostSolidObject::GetModel() const @@ -157,6 +188,7 @@ CUnitDrawerData::~CUnitDrawerData() if (tmpGso->DecRef()) continue; + // worldTransformAlloc frees its slot in ~GhostSolidObject (ghostMemPool.free below) // might be the gbOwner of a decal; groundDecals is deleted after us groundDecals->GhostDestroyed(tmpGso); ghostMemPool.free(tmpGso); @@ -165,6 +197,7 @@ CUnitDrawerData::~CUnitDrawerData() lgb.clear(); } } + liveGhostTransforms.clear(); // each entry's ScopedTransformMemAlloc frees its slot on erase assert(ghostMemPool.allocs() == 0); ghostMemPool.clear(); @@ -216,6 +249,8 @@ void CUnitDrawerData::Update() updateBody(unit); } + UpdateLiveGhostTransforms(); + if ((useDistToGroundForIcons = (camHandler->GetCurrentController()).GetUseDistToGroundForIcons())) { const float3& camPos = camera->GetPos(); // use the height at the current camera position @@ -617,6 +652,7 @@ bool CUnitDrawerData::UpdateUnitGhosts(const CUnit* unit, const bool addNewGhost gso->facing = u->buildFacing; gso->dir = u->frontdir; gso->team = u->team; + gso->paletteIndex = u->paletteIndex; gso->radius = u->radius; gso->GetModel(); @@ -625,6 +661,8 @@ bool CUnitDrawerData::UpdateUnitGhosts(const CUnit* unit, const bool addNewGhost gso->iconRadius = u->iconRadius; + gso->InitWorldTransform(); + groundDecals->GhostCreated(u, gso); } @@ -640,7 +678,8 @@ bool CUnitDrawerData::UpdateUnitGhosts(const CUnit* unit, const bool addNewGhost } - spring::VectorErase(savedData.liveGhostBuildings[allyTeam][MDL_TYPE(u)], u); + spring::VectorEraseIf(savedData.liveGhostBuildings[allyTeam][MDL_TYPE(u)], + [u](const LiveGhostBuilding& lgb) { return lgb.unit == u; }); } return addedOwnAllyTeam; } @@ -674,7 +713,8 @@ void CUnitDrawerData::UnitEnteredLos(const CUnit* unit, int allyTeam) CUnit* u = const_cast(unit); //cleanup if (unit->leavesGhost) - spring::VectorErase(savedData.liveGhostBuildings[allyTeam][MDL_TYPE(unit)], u); + spring::VectorEraseIf(savedData.liveGhostBuildings[allyTeam][MDL_TYPE(unit)], + [u](const LiveGhostBuilding& lgb) { return lgb.unit == u; }); if (allyTeam != gu->myAllyTeam) return; @@ -687,8 +727,15 @@ void CUnitDrawerData::UnitLeftLos(const CUnit* unit, int allyTeam) RECOIL_DETAILED_TRACY_ZONE; CUnit* u = const_cast(unit); //cleanup - if (unit->leavesGhost) - spring::VectorInsertUnique(savedData.liveGhostBuildings[allyTeam][MDL_TYPE(unit)], u, true); + if (unit->leavesGhost) { + // snapshot the color the unit is last seen under so a later team change (while out of LOS) + // does not recolor its ghost. keep the earliest snapshot if it re-fires without re-entering. + auto& lgbs = savedData.liveGhostBuildings[allyTeam][MDL_TYPE(unit)]; + const bool alreadyGhosted = std::any_of(lgbs.begin(), lgbs.end(), + [u](const LiveGhostBuilding& lgb) { return lgb.unit == u; }); + if (!alreadyGhosted) + lgbs.push_back({ u, u->paletteIndex, static_cast(u->team) }); + } if (allyTeam != gu->myAllyTeam) return; @@ -696,6 +743,37 @@ void CUnitDrawerData::UnitLeftLos(const CUnit* unit, int allyTeam) UpdateCurrentUnitIcon(unit); } +void CUnitDrawerData::UpdateLiveGhostTransforms() +{ + RECOIL_DETAILED_TRACY_ZONE; + // Maintain one world-transform slot per live ghost building drawn for the local allyTeam. + // Ghosts are static, so each slot is filled once on first sight; entries not seen this sweep + // (units that regained LOS, died, or belong to a different allyTeam now) are freed. + const int stamp = ++liveGhostSweepStamp; + + for (int modelType = MODELTYPE_3DO; modelType < MODELTYPE_CNT; modelType++) { + for (const auto& lgb : savedData.liveGhostBuildings[gu->myAllyTeam][modelType]) { + const CUnit* u = lgb.unit; + const auto it = liveGhostTransforms.find(u); + if (it == liveGhostTransforms.end()) { + ScopedTransformMemAlloc alloc(1); + alloc.UpdateForced(0, MakeGhostWorldTransform(u->pos, u->buildFacing)); + liveGhostTransforms.emplace(u, std::make_pair(std::move(alloc), stamp)); + } + else { + it->second.second = stamp; + } + } + } + + for (auto it = liveGhostTransforms.begin(); it != liveGhostTransforms.end(); ) { + if (it->second.second != stamp) + it = liveGhostTransforms.erase(it); // ScopedTransformMemAlloc frees the slot on erase + else + ++it; + } +} + void CUnitDrawerData::UnitLeavesGhostChanged(const CUnit* unit, const bool leaveDeadGhost) { if (unit->leavesGhost) { @@ -732,6 +810,7 @@ void CUnitDrawerData::PlayerChanged(int playerID) void CUnitDrawerData::RemoveDeadGhost(GhostSolidObject* gso, std::vector& dgb, int index) { if (!gso->DecRef()) { + // worldTransformAlloc frees its slot in ~GhostSolidObject (ghostMemPool.free) groundDecals->GhostDestroyed(gso); ghostMemPool.free(gso); } diff --git a/rts/Rendering/Units/UnitDrawerData.h b/rts/Rendering/Units/UnitDrawerData.h index 36630545555..d5ae046da63 100644 --- a/rts/Rendering/Units/UnitDrawerData.h +++ b/rts/Rendering/Units/UnitDrawerData.h @@ -23,6 +23,8 @@ class GhostSolidObject { bool DecRef() { return ((refCount--) > 1); } const S3DModel* GetModel() const; void PostLoad(); + // (re)allocate and fill the batched-draw world transform slot from pos/facing + void InitWorldTransform(); public: std::string modelName; @@ -34,8 +36,16 @@ class GhostSolidObject { int refCount; int facing; //FIXME replaced with dir-vector just legacy decal drawer uses this + + // color identity captured when the ghost was created; a ghost keeps the color it was last + // seen under. team drives the legacy (GLSL) team-color path, paletteIndex drives the GL4 + // per-instance palette (equal to team unless a custom Lua color palette was assigned). uint8_t team; + uint16_t paletteIndex; + size_t currentIconIndex; + + ScopedTransformMemAlloc worldTransformAlloc; private: mutable const S3DModel* model; }; @@ -86,6 +96,16 @@ class CUnitDrawerData : public CUnitDrawerDataBase { private: mutable const UnitDef* unitDef; }; + // a still-alive building that left an observer's LOS. The unit is drawn as a ghost using the + // color it was last seen under (snapshotted here), so it does not silently recolor if the live + // unit changes team while out of LOS. team feeds the legacy (GLSL) team-color path, paletteIndex + // feeds the GL4 per-instance palette (equal to team unless a custom Lua palette was assigned). + struct LiveGhostBuilding { + CR_DECLARE_STRUCT(LiveGhostBuilding) + CUnit* unit = nullptr; + uint16_t paletteIndex = 0; + uint8_t team = 0; + }; struct SavedData { CR_DECLARE_STRUCT(SavedData) @@ -97,7 +117,7 @@ class CUnitDrawerData : public CUnitDrawerDataBase { std::vector, MODELTYPE_CNT>> deadGhostBuildings; /// buildings that left LOS but are still alive - std::vector, MODELTYPE_CNT>> liveGhostBuildings; + std::vector, MODELTYPE_CNT>> liveGhostBuildings; }; public: CUnitDrawerData(bool& mtModelDrawer_); @@ -151,6 +171,12 @@ class CUnitDrawerData : public CUnitDrawerDataBase { return savedData.liveGhostBuildings[allyTeam][modelType]; } + // world transform slot (in transformsMemStorage) for a live ghost building; INVALID_INDEX if none + size_t GetLiveGhostTransform(const CUnit* unit) const { + const auto it = liveGhostTransforms.find(unit); + return (it != liveGhostTransforms.end()) ? it->second.first.GetOffset(false) : TransformsMemStorage::INVALID_INDEX; + } + auto* GetSavedData() { return &savedData; } const auto* GetSavedData() const { return &savedData; } protected: @@ -191,6 +217,13 @@ class CUnitDrawerData : public CUnitDrawerDataBase { S3DModel* GetUnitModel(const CUnit* unit) const; void RemoveDeadGhost(GhostSolidObject* gso, std::vector& dgb, int index); + // rebuilds the per-unit world transform slots for live ghost buildings of the local allyTeam. + // scan-based so it self-heals across savegame load, allyTeam/spectator changes and leavesGhost toggles. + void UpdateLiveGhostTransforms(); + // maps unit -> { RAII-owned world transform slot, last sweep stamp seen } + spring::unordered_map> liveGhostTransforms; + int liveGhostSweepStamp = 0; + // icons bool useDistToGroundForIcons; float sqCamDistToGroundForIcons; diff --git a/rts/Rml/Backends/RmlUi_Backend.cpp b/rts/Rml/Backends/RmlUi_Backend.cpp index 70383aa17fb..a46ae11982f 100644 --- a/rts/Rml/Backends/RmlUi_Backend.cpp +++ b/rts/Rml/Backends/RmlUi_Backend.cpp @@ -115,6 +115,9 @@ class BackendState : public Rml::Plugin { RmlGui::SVG::DynamicSVGPlugin* svgPlugin; Rml::UniquePtr> element_lua_texture_instancer; + + // Deferred element deletion: elements removed during event processing are kept alive here + std::vector pending_deletes; }; static Rml::UniquePtr state; @@ -124,6 +127,14 @@ bool RmlInitialized() return state && state->initialized; } +// Deferred element deletion helper - called from Lua bindings +// Elements are kept alive until RmlGui::Update() clears them +void AddPendingDelete(Rml::ElementPtr element) +{ + if (RmlInitialized() && element) + state->pending_deletes.push_back(std::move(element)); +} + bool RmlGui::Initialize() { LOG_L(L_INFO, "[RmlUi::%s] Beginning RmlUi Initialization", __func__); @@ -380,6 +391,9 @@ void RmlGui::Update() } state->contexts_to_remove.clear(); } + + // Clear deferred element deletions - safe point outside event processing + state->pending_deletes.clear(); } void RmlGui::RenderFrame() diff --git a/rts/Rml/SolLua/bind/Context.cpp b/rts/Rml/SolLua/bind/Context.cpp index db0dcb8960b..5cc64899396 100644 --- a/rts/Rml/SolLua/bind/Context.cpp +++ b/rts/Rml/SolLua/bind/Context.cpp @@ -33,6 +33,7 @@ #include "../plugin/SolLuaDataModel.h" #include "../plugin/SolLuaDocument.h" +#include "Rml/Backends/RmlUi_Backend.h" #include "sol2/sol.hpp" #include @@ -131,7 +132,7 @@ struct lua_iterator_state sol::state_view l{s}; int index = 0; int count = keytable.size(); - while (keytable.get(++index).get_type() != sol::type::nil && index <= count) { + while (keytable.get(++index).get_type() != sol::type::lua_nil && index <= count) { this->keys.emplace_back(sol::object(l, sol::in_place, index)); } } else { @@ -199,7 +200,7 @@ createNewIndexFunction(std::shared_ptr data, const } if (value.is()) { auto value_raw = value.as().raw_get("__raw"); - if (value_raw != sol::nil && value_raw.is()) { + if (value_raw != sol::lua_nil && value_raw.is()) { // new value is a datamodel proxy, so get the underlying table to assign prop.as().raw_set(solkey, value_raw.as().call(value)); } else { @@ -290,7 +291,7 @@ sol::table openDataModel(Rml::Context& self, const Rml::String& name, sol::objec } if (value.is()) { auto value_raw = value.as().raw_get("__raw"); - if (value_raw != sol::nil && value_raw.is()) { + if (value_raw != sol::lua_nil && value_raw.is()) { // new value is a datamodel proxy, so get the underlying table to assign data->Table.raw_set(key, value_raw.as().call(value)); } else { @@ -427,7 +428,12 @@ void bind_context(sol::table& namespace_table, SolLuaPlugin* slp) * @function RmlUi.Context:Render * @return boolean */ - "Render", &Rml::Context::Render, + "Render", [](Rml::Context& self) { + RmlGui::BeginFrame(); + bool result = self.Render(); + RmlGui::PresentFrame(); + return result; + }, /*** * Closes all documents currently loaded with the context. * @function RmlUi.Context:UnloadAllDocuments diff --git a/rts/Rml/SolLua/bind/Element.cpp b/rts/Rml/SolLua/bind/Element.cpp index e2edf19159a..0e8b1fd7982 100644 --- a/rts/Rml/SolLua/bind/Element.cpp +++ b/rts/Rml/SolLua/bind/Element.cpp @@ -36,6 +36,8 @@ #include +// Forward declaration for deferred element deletion +extern void AddPendingDelete(Rml::ElementPtr element); namespace Rml::SolLua { @@ -48,6 +50,24 @@ namespace Rml::SolLua self.AddEventListener(event, e, in_capture_phase); } + void setInnerRMLSafe(Rml::Element& self, const Rml::String& rml) + { + // Manually remove all DOM children and defer their deletion + // This prevents use-after-free when Lua holds references to children + while (self.GetNumChildren()) + { + Rml::Element* child = self.GetChild(0); + // RemoveChild returns an ElementPtr which owns the child + Rml::ElementPtr removed = self.RemoveChild(child); + // Store it for deferred deletion + AddPendingDelete(std::move(removed)); + } + + // Now set the new content + if (!rml.empty()) + self.SetInnerRML(rml); + } + void addEventListener(Rml::Element& self, const Rml::String& event, const Rml::String& code, sol::this_state s) { auto state = sol::state_view{ s }; @@ -158,7 +178,7 @@ namespace Rml::SolLua void Set(const sol::this_state L, const std::string& name, const sol::object& value) { - if (value.get_type() == sol::type::nil) { + if (value.get_type() == sol::type::lua_nil) { m_element->RemoveProperty(name); return; } @@ -453,7 +473,7 @@ namespace Rml::SolLua /*** * Is a screen-space point within this element? * @function RmlUi.Element:IsPointWithinElement - * @param point RmlUi.Vector2i + * @param point RmlUi.Vector2f * @return boolean */ "IsPointWithinElement", &Rml::Element::IsPointWithinElement, @@ -497,7 +517,7 @@ namespace Rml::SolLua /*** @field RmlUi.Element.id string ID of this element, in the context of ``. */ "id", sol::property(&Rml::Element::GetId, &Rml::Element::SetId), /*** @field RmlUi.Element.inner_rml string Gets or sets the inner RML (markup) content of the element. */ - "inner_rml", sol::property(sol::resolve(&Rml::Element::GetInnerRML), &Rml::Element::SetInnerRML), + "inner_rml", sol::property(sol::resolve(&Rml::Element::GetInnerRML), &functions::setInnerRMLSafe), /*** @field RmlUi.Element.scroll_left integer Gets or sets the number of pixels that the content of the element is scrolled from the left. */ "scroll_left", sol::property(&Rml::Element::GetScrollLeft, &Rml::Element::SetScrollLeft), /*** @field RmlUi.Element.scroll_top integer Gets or sets the number of pixels that the content of the element is scrolled from the top. */ diff --git a/rts/Rml/SolLua/bind/bind.cpp b/rts/Rml/SolLua/bind/bind.cpp index 21eabe9d46c..98ce9fec37c 100644 --- a/rts/Rml/SolLua/bind/bind.cpp +++ b/rts/Rml/SolLua/bind/bind.cpp @@ -39,7 +39,7 @@ namespace Rml::SolLua sol::object makeObjectFromVariant(const Rml::Variant* variant, sol::state_view s) { - if (!variant) return sol::make_object(s, sol::nil); + if (!variant) return sol::make_object(s, sol::lua_nil); switch (variant->GetType()) { @@ -69,10 +69,10 @@ namespace Rml::SolLua case Rml::Variant::VOIDPTR: return sol::make_object(s, variant->Get()); default: - return sol::make_object(s, sol::nil); + return sol::make_object(s, sol::lua_nil); } - return sol::make_object(s, sol::nil); + return sol::make_object(s, sol::lua_nil); } } // end namespace Rml::SolLua diff --git a/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp b/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp index d50575e93cc..39aee4ef964 100644 --- a/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp +++ b/rts/Rml/SolLua/plugin/SolLuaEventListener.cpp @@ -115,11 +115,28 @@ namespace Rml::SolLua void SolLuaEventListener::OnDetach(Rml::Element* element) { - delete this; + // Mark as detached but don't delete immediately. + // Deletion will happen when ProcessEvent completes or on next ProcessEvent call. + m_detached = true; + m_element = nullptr; } void SolLuaEventListener::ProcessEvent(Rml::Event& event) { + // If we were detached, delete ourselves now that it's safe + if (m_detached) + { + delete this; + return; + } + + // Check if element is still valid (may have been destroyed during event processing) + if (m_element == nullptr) + return; + + if (m_element->GetContext() == nullptr) + return; + auto document = dynamic_cast(m_element->GetOwnerDocument()); if (document != nullptr && m_func.valid()) { @@ -139,6 +156,10 @@ namespace Rml::SolLua ErrorHandler(m_func.lua_state(), std::move(result)); } } + + // After processing, check if we were detached during the callback + if (m_detached) + delete this; } } // namespace Rml::SolLua diff --git a/rts/Rml/SolLua/plugin/SolLuaEventListener.h b/rts/Rml/SolLua/plugin/SolLuaEventListener.h index 5ed59c81a71..3702b0ebb7d 100644 --- a/rts/Rml/SolLua/plugin/SolLuaEventListener.h +++ b/rts/Rml/SolLua/plugin/SolLuaEventListener.h @@ -35,7 +35,6 @@ #include #include - namespace Rml { class Element; @@ -56,6 +55,7 @@ namespace Rml::SolLua private: sol::protected_function m_func; Rml::Element *m_element; + bool m_detached = false; }; } // namespace Rml::SolLua diff --git a/rts/Sim/Misc/LosHandler.cpp b/rts/Sim/Misc/LosHandler.cpp index a132ddeb382..5b92c0d1762 100644 --- a/rts/Sim/Misc/LosHandler.cpp +++ b/rts/Sim/Misc/LosHandler.cpp @@ -951,22 +951,24 @@ bool CLosHandler::InRadar(const float3 pos, int allyTeam) const bool CLosHandler::InRadar(const CUnit* unit, int allyTeam) const { RECOIL_DETAILED_TRACY_ZONE; - // unit is discoverable by sonar + // first attempt to discover with sonar: + // unit is discoverable by sonar only if not sonarStealth or not sonarJammed if (unit->IsInWater()) { if ((!unit->sonarStealth || unit->beingBuilt) && sonar.InSight(unit->pos, allyTeam) && - !InJammer(unit, allyTeam)) + !InSonarJammer(unit, allyTeam)) return true; } - // unit is completely submerged, only sonar can see it + // unit is InWater and UnderWater, but was not previously caught by sonar, skip it if (unit->IsUnderWater()) return false; - // radar stealth + // then attempt to discover with radar + // unit is radar stealth, can't be discovered if (unit->stealth && !unit->beingBuilt) return false; - + // use radar jamming, not sonar jamming here return (radar.InSight(unit->pos, allyTeam) && !InJammer(unit, allyTeam)); } @@ -976,13 +978,9 @@ bool CLosHandler::InJammer(const float3 pos, int allyTeam) const RECOIL_DETAILED_TRACY_ZONE; const int jammerAlly = modInfo.separateJammers ? allyTeam : 0; - if (pos.y < 0.0f) - return sonarJammer.InSight(pos, jammerAlly); - return jammer.InSight(pos, jammerAlly); } - bool CLosHandler::InJammer(const CUnit* unit, int allyTeam) const { RECOIL_DETAILED_TRACY_ZONE; @@ -990,11 +988,27 @@ bool CLosHandler::InJammer(const CUnit* unit, int allyTeam) const return false; //TODO handle ingame alliances + const int jammerAlly = modInfo.separateJammers ? unit->allyteam : 0; + + return jammer.InSight(unit->pos, jammerAlly); +} + +bool CLosHandler::InSonarJammer(const float3 pos, int allyTeam) const +{ + RECOIL_DETAILED_TRACY_ZONE; + const int jammerAlly = modInfo.separateJammers ? allyTeam : 0; + + return sonarJammer.InSight(pos, jammerAlly); +} +bool CLosHandler::InSonarJammer(const CUnit* unit, int allyTeam) const +{ + RECOIL_DETAILED_TRACY_ZONE; + if (allyTeam == unit->allyteam) + return false; + + //TODO handle ingame alliances const int jammerAlly = modInfo.separateJammers ? unit->allyteam : 0; - if (unit->IsUnderWater()) { - return sonarJammer.InSight(unit->pos, jammerAlly); - } - return jammer.InSight(unit->pos, jammerAlly); + return sonarJammer.InSight(unit->pos, jammerAlly); } diff --git a/rts/Sim/Misc/LosHandler.h b/rts/Sim/Misc/LosHandler.h index e30c4668bc1..9f17fa3ebc3 100644 --- a/rts/Sim/Misc/LosHandler.h +++ b/rts/Sim/Misc/LosHandler.h @@ -253,11 +253,16 @@ class CLosHandler : public CEventClient bool InRadar(const CUnit* unit, int allyTeam) const; - // returns whether a square is being radar- or sonar-jammed - // (even when the square is not in radar- or sonar-coverage) + // returns whether a square is being radar-jammed + // (even when the square is not in radar-coverage) bool InJammer(const float3 pos, int allyTeam) const; bool InJammer(const CUnit* unit, int allyTeam) const; + // returns whether a square is being sonar-jammed + // (even when the square is not in sonar-coverage) + bool InSonarJammer(const float3 pos, int allyTeam) const; + bool InSonarJammer(const CUnit* unit, int allyTeam) const; + bool InSeismicDistance(const CUnit* unit, int allyTeam) const { return seismic.InSight(unit->pos, allyTeam); diff --git a/rts/Sim/Misc/ModInfo.cpp b/rts/Sim/Misc/ModInfo.cpp index 39e9f183c0d..82ca63b5256 100644 --- a/rts/Sim/Misc/ModInfo.cpp +++ b/rts/Sim/Misc/ModInfo.cpp @@ -180,7 +180,7 @@ void CModInfo::Init(const std::string& modFileName) parser.Execute(); if (!parser.IsValid()) - LOG_L(L_ERROR, "[ModInfo::%s] error \"%s\" loading mod-rules, using defaults", __func__, parser.GetErrorLog().c_str()); + throw content_error(fmt::format("Failed to load gamedata/modrules.lua: {}", parser.GetErrorLog())); const LuaTable& root = parser.GetRoot(); diff --git a/rts/Sim/Misc/SmoothHeightMesh.cpp b/rts/Sim/Misc/SmoothHeightMesh.cpp index bba9ef103de..35c39aa83c7 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.cpp +++ b/rts/Sim/Misc/SmoothHeightMesh.cpp @@ -58,6 +58,29 @@ static float Interpolate(float x, float y, const int maxx, const int maxy, const return mix(hi1, hi2, dy); } + +// C1-continuous, unlike Interpolate whose gradient jumps at cell borders +static float SampleBicubic(float x, float y, const int maxx, const int maxy, const float res, const float* heightmap) +{ + RECOIL_DETAILED_TRACY_ZONE; + x = std::clamp(x / res, 0.0f, (float)maxx); + y = std::clamp(y / res, 0.0f, (float)maxy); + const int sx = std::min((int)x, maxx - 1); + const int sy = std::min((int)y, maxy - 1); + const float dx = (x - sx); + const float dy = (y - sy); + + // gather the read-only 4x4 neighbourhood around the cell, clamped at the edges + float patch[4][4]; + for (int j = 0; j < 4; ++j) { + const float* row = &heightmap[std::clamp(sy + j - 1, 0, maxy - 1) * maxx]; + for (int i = 0; i < 4; ++i) + patch[j][i] = row[std::clamp(sx + i - 1, 0, maxx - 1)]; + } + + return InterpolateBicubic(patch, dx, dy); +} + void SmoothHeightMesh::Init(int2 max, int res, int smoothRad) { RECOIL_DETAILED_TRACY_ZONE; @@ -135,6 +158,13 @@ float SmoothHeightMesh::GetHeightAboveWater(float x, float y) return std::max(0.0f, Interpolate(x, y, maxx, maxy, fresolution, &mesh[0])); } +float SmoothHeightMesh::GetHeightSmooth(float x, float y) +{ + RECOIL_DETAILED_TRACY_ZONE; + assert(!mesh.empty()); + return SampleBicubic(x, y, maxx, maxy, fresolution, &mesh[0]); +} + float SmoothHeightMesh::SetHeight(int index, float h) { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Sim/Misc/SmoothHeightMesh.h b/rts/Sim/Misc/SmoothHeightMesh.h index 888686af138..52007d66e42 100644 --- a/rts/Sim/Misc/SmoothHeightMesh.h +++ b/rts/Sim/Misc/SmoothHeightMesh.h @@ -22,7 +22,7 @@ namespace SmoothHeightMeshNamespace { */ class SmoothHeightMesh { - friend class SmoothHeightMeshDrawer; + friend struct SmoothHeightMeshDrawer; public: @@ -42,6 +42,7 @@ class SmoothHeightMesh float GetHeight(float x, float y); float GetHeightAboveWater(float x, float y); + float GetHeightSmooth(float x, float y); float SetHeight(int index, float h); float AddHeight(int index, float h); float SetMaxHeight(int index, float h); diff --git a/rts/Sim/Misc/Team.cpp b/rts/Sim/Misc/Team.cpp index 1c89bb3ec34..2486a94ed7e 100644 --- a/rts/Sim/Misc/Team.cpp +++ b/rts/Sim/Misc/Team.cpp @@ -45,6 +45,7 @@ CR_REG_METADATA(CTeam, ( CR_MEMBER(resPrevExpense), CR_MEMBER(resShare), CR_MEMBER(resDelayedShare), + CR_MEMBER(resExcessThisFrame), CR_MEMBER(resSent), CR_MEMBER(resPrevSent), CR_MEMBER(resReceived), @@ -156,7 +157,7 @@ void CTeam::AddMetal(float amount, bool useIncomeMultiplier) if (res.metal <= resStorage.metal) return; - resDelayedShare.metal += (res.metal - resStorage.metal); + resExcessThisFrame.metal += (res.metal - resStorage.metal); res.metal = resStorage.metal; } @@ -170,7 +171,7 @@ void CTeam::AddEnergy(float amount, bool useIncomeMultiplier) resIncome.energy += amount; if (res.energy > resStorage.energy) { - resDelayedShare.energy += (res.energy - resStorage.energy); + resExcessThisFrame.energy += (res.energy - resStorage.energy); res.energy = resStorage.energy; } } @@ -196,7 +197,7 @@ void CTeam::AddResources(SResourcePack amount, bool useIncomeMultiplier) if (res[i] <= resStorage[i]) continue; - resDelayedShare[i] += (res[i] - resStorage[i]); + resExcessThisFrame[i] += (res[i] - resStorage[i]); res[i] = resStorage[i]; } } diff --git a/rts/Sim/Misc/Team.h b/rts/Sim/Misc/Team.h index 6bc74267dc9..3a1c0fad8a2 100644 --- a/rts/Sim/Misc/Team.h +++ b/rts/Sim/Misc/Team.h @@ -88,6 +88,7 @@ class CTeam : public TeamBase SResourcePack resIncome, resPrevIncome; SResourcePack resExpense, resPrevExpense; SResourcePack resShare; + SResourcePack resExcessThisFrame; //< accumulates excess over a gameframe SResourcePack resDelayedShare; //< excess that might be shared next SlowUpdate SResourcePack resSent, resPrevSent; SResourcePack resReceived, resPrevReceived; diff --git a/rts/Sim/Misc/TeamHandler.cpp b/rts/Sim/Misc/TeamHandler.cpp index d8b5d4e5afc..f582e9f4b2a 100644 --- a/rts/Sim/Misc/TeamHandler.cpp +++ b/rts/Sim/Misc/TeamHandler.cpp @@ -7,6 +7,7 @@ #include "Game/GameSetup.h" #include "Sim/Misc/GlobalConstants.h" #include "Sim/Misc/GlobalSynced.h" +#include "System/EventHandler.h" #include "System/Misc/TracyDefs.h" @@ -113,9 +114,35 @@ void CTeamHandler::SetDefaultStartPositions(const CGameSetup* setup) } } +void CTeamHandler::HandleFrameExcess() +{ + std::map excesses; + for (const auto &team : teams) + excesses.emplace(team.teamNum, team.resExcessThisFrame); + + /* Note that `resDelayedShare` is a metaaccumulator, + * the reason to have this two-layer accumulation is + * that handling excess right when it happens would + * be too expensive (for example you can have tens of + * thousands of windgens each generating a resource + * instance), having the Lua event handled at slow + * update would reduce control, and having the engine + * handle excess natively outside slow update would + * be inconsistent with other native resource handling. */ + if (!eventHandler.ResourceExcess(excesses)) + for (auto &team : teams) + team.resDelayedShare += team.resExcessThisFrame; + + for (auto &team : teams) + team.resExcessThisFrame = 0.0f; +} + void CTeamHandler::GameFrame(int frameNum) { RECOIL_DETAILED_TRACY_ZONE; + + HandleFrameExcess(); + if ((frameNum % TEAM_SLOWUPDATE_RATE) != 0) return; diff --git a/rts/Sim/Misc/TeamHandler.h b/rts/Sim/Misc/TeamHandler.h index 2e0e2aa7a2b..da7499d6581 100644 --- a/rts/Sim/Misc/TeamHandler.h +++ b/rts/Sim/Misc/TeamHandler.h @@ -161,6 +161,7 @@ class CTeamHandler bool TransferTeamMaxUnits(CTeam* fromTeam, CTeam* toTeam, int transferAmnt); private: + void HandleFrameExcess(); /** * @brief gaia team diff --git a/rts/Sim/MoveTypes/Components/MoveTypesComponents.h b/rts/Sim/MoveTypes/Components/MoveTypesComponents.h index b0d1d8b4265..fa7380707bb 100644 --- a/rts/Sim/MoveTypes/Components/MoveTypesComponents.h +++ b/rts/Sim/MoveTypes/Components/MoveTypesComponents.h @@ -7,8 +7,8 @@ #include "System/Ecs/Components/BaseComponents.h" #include -struct CUnit; -struct CFeature; +class CUnit; +class CFeature; namespace MoveTypes { diff --git a/rts/Sim/MoveTypes/GroundMoveType.cpp b/rts/Sim/MoveTypes/GroundMoveType.cpp index 066dea2f634..8790ce9eb63 100644 --- a/rts/Sim/MoveTypes/GroundMoveType.cpp +++ b/rts/Sim/MoveTypes/GroundMoveType.cpp @@ -146,8 +146,11 @@ CR_REG_METADATA(CGroundMoveType, ( CR_MEMBER(forceFromStaticCollidees), CR_MEMBER(pathID), - CR_MEMBER(nextPathId), - CR_MEMBER(deletePathId), + // The ECS registry is not persisted across save/load, so a saved entity ID will collide with + // a newly-allocated entity in the fresh registry, causing FollowPath to destroy the wrong path + // or swap to an invalid one. + CR_IGNORED(nextPathId), + CR_IGNORED(deletePathId), CR_MEMBER(numIdlingUpdates), CR_MEMBER(numIdlingSlowUpdates), diff --git a/rts/Sim/MoveTypes/MoveDefHandler.h b/rts/Sim/MoveTypes/MoveDefHandler.h index fbec124d13c..9c8d1db5601 100644 --- a/rts/Sim/MoveTypes/MoveDefHandler.h +++ b/rts/Sim/MoveTypes/MoveDefHandler.h @@ -18,7 +18,7 @@ class CUnit; class LuaTable; namespace MoveTypes { - class CheckCollisionQuery; + struct CheckCollisionQuery; } namespace MoveDefs { diff --git a/rts/Sim/MoveTypes/Utils/UnitTrapCheckUtils.h b/rts/Sim/MoveTypes/Utils/UnitTrapCheckUtils.h index 7db24ab3835..da7354b01fb 100644 --- a/rts/Sim/MoveTypes/Utils/UnitTrapCheckUtils.h +++ b/rts/Sim/MoveTypes/Utils/UnitTrapCheckUtils.h @@ -3,8 +3,8 @@ #ifndef UNIT_TRAP_CHECK_UTILS_H__ #define UNIT_TRAP_CHECK_UTILS_H__ -struct CFeature; -struct CUnit; +class CFeature; +class CUnit; namespace MoveTypes { void RegisterFeatureForUnitTrapCheck(CFeature* object); diff --git a/rts/Sim/Objects/SolidObject.cpp b/rts/Sim/Objects/SolidObject.cpp index 8d3105cbc39..750de80c068 100644 --- a/rts/Sim/Objects/SolidObject.cpp +++ b/rts/Sim/Objects/SolidObject.cpp @@ -51,6 +51,8 @@ CR_REG_METADATA(CSolidObject, CR_MEMBER(team), CR_MEMBER(allyteam), + CR_MEMBER_UN(paletteIndex), + CR_MEMBER(creationFrame), CR_MEMBER(pieceHitFrames), diff --git a/rts/Sim/Path/HAPFS/PathSearch.h b/rts/Sim/Path/HAPFS/PathSearch.h index 1fae37cc5dd..fdb3bfc7d85 100644 --- a/rts/Sim/Path/HAPFS/PathSearch.h +++ b/rts/Sim/Path/HAPFS/PathSearch.h @@ -6,7 +6,7 @@ #include "System/float3.h" class CSolidObject; -class MoveDef; +struct MoveDef; namespace HAPFS { struct PathSearch { diff --git a/rts/Sim/Path/QTPFS/Components/PathSpeedModInfo.h b/rts/Sim/Path/QTPFS/Components/PathSpeedModInfo.h index 19f7c9285cc..4bde1a878b5 100644 --- a/rts/Sim/Path/QTPFS/Components/PathSpeedModInfo.h +++ b/rts/Sim/Path/QTPFS/Components/PathSpeedModInfo.h @@ -12,7 +12,7 @@ namespace QTPFS { -class INode; +struct INode; struct NodeLayerSpeedInfoSweep { static constexpr std::size_t page_size = MoveDefHandler::MAX_MOVE_DEFS; diff --git a/rts/Sim/Path/QTPFS/Path.h b/rts/Sim/Path/QTPFS/Path.h index 6a354b8a324..88ed9e14dc8 100644 --- a/rts/Sim/Path/QTPFS/Path.h +++ b/rts/Sim/Path/QTPFS/Path.h @@ -295,24 +295,15 @@ namespace QTPFS { points.clear(); points.resize(n); } - void CopyPoints(const IPath& p) { - AllocPoints(p.NumPoints()); - for (unsigned int n = 0; n < p.NumPoints(); n++) { - points[n] = p.GetPoint(n); - } - } + void CopyPoints(const IPath& p) { points = p.points; } + void AllocNodes(unsigned int n) { nodes.clear(); nodes.resize(n); } - void CopyNodes(const IPath& p) { - AllocNodes(p.nodes.size()); - for (unsigned int n = 0; n < p.nodes.size(); n++) { - nodes[n] = p.GetNode(n); - } - } + void CopyNodes(const IPath& p) { nodes = p.nodes; } // Function is for debugging and logging purposes only uint32_t CalculateHash() const { @@ -345,15 +336,15 @@ namespace QTPFS { spring_time GetSearchTime() const { return searchTime; } // Incomplete paths need to be rebuilt from time to time as the owner makes progress. - unsigned int GetRepathTriggerIndex() const { return repathAtPointIndex; } + uint32_t GetRepathTriggerIndex() const { return repathAtPointIndex; } void SetRepathTriggerIndex(unsigned int index) { repathAtPointIndex = index; } void ClearGetRepathTriggerIndex() { repathAtPointIndex = 0; } - float3 GetGoalPosition() const { return goalPosition; } + const float3& GetGoalPosition() const { return goalPosition; } void SetGoalPosition(float3 point) { goalPosition = point; } - unsigned int GetFirstNodeIdOfCleanPath() const { return firstNodeIdOfCleanPath; } + uint32_t GetFirstNodeIdOfCleanPath() const { return firstNodeIdOfCleanPath; } void SetFirstNodeIdOfCleanPath(int nodeId) { firstNodeIdOfCleanPath = nodeId; } bool IsRawPath() const { return isRawPath; } diff --git a/rts/Sim/Path/QTPFS/PathCache.cpp b/rts/Sim/Path/QTPFS/PathCache.cpp index 20f152006d5..bf1ae9a278b 100644 --- a/rts/Sim/Path/QTPFS/PathCache.cpp +++ b/rts/Sim/Path/QTPFS/PathCache.cpp @@ -40,9 +40,8 @@ static void GetRectangleCollisionVolume(const SRectangle& r, CollisionVolume& v, rm.z = ((r.z1 + r.z2) * SQUARE_SIZE) >> 1; rm.y = 0.0f; - #define CV CollisionVolume + using CV = CollisionVolume; v.InitShape(vScales, ZeroVector, CV::COLVOL_TYPE_BOX, CV::COLVOL_HITTEST_CONT, CV::COLVOL_AXIS_Y); - #undef CV } bool QTPFS::PathCache::MarkDeadPaths(const SRectangle& r, const NodeLayer& nodeLayer) { diff --git a/rts/Sim/Path/QTPFS/PathManager.cpp b/rts/Sim/Path/QTPFS/PathManager.cpp index 1aa5e9a36c5..b971d9c194b 100644 --- a/rts/Sim/Path/QTPFS/PathManager.cpp +++ b/rts/Sim/Path/QTPFS/PathManager.cpp @@ -933,6 +933,15 @@ bool QTPFS::PathManager::InitializeSearch(QTPFS::entity searchEntity) { assert((registry.any_of(pathEntity))); IPath* path = GetPath(pathEntity); assert(path->GetPathType() == pathType); + + // Somehow units can get wiped without triggering a delete. This is a catch for that until the + // cause can be found and resolved. + const CSolidObject* owner = path->GetOwner(); + if (owner != nullptr) { + if (owner->objectUsable == false) + return false; + } + search->Initialize(&nodeLayer, path->GetSourcePoint(), path->GetGoalPosition(), path->GetOwner()); path->SetHash(search->GetHash()); path->SetVirtualHash(search->GetPartialSearchHash()); @@ -1122,20 +1131,13 @@ bool QTPFS::PathManager::ExecuteSearch( int currentThread = ThreadPool::GetThreadNum(); assert(search != nullptr); + assert(search->initialized); // temp-path might have been removed already via // DeletePath before we got a chance to process it if (path == nullptr) return false; - // Somehow units can get wiped without triggering a delete. This is a catch for that until the - // cause can be found and resolved. - const CSolidObject* owner = path->GetOwner(); - if (owner != nullptr) { - if (owner->objectUsable == false) - return false; - } - assert(path->GetID() == search->GetID()); bool forceFullPath = false; @@ -1684,13 +1686,16 @@ unsigned int QTPFS::PathManager::ExecuteImmediateSearch(unsigned int pathId){ registry.remove(pathEntity); registry.remove(pathEntity); } else { - DeletePathEntity(pathEntity); pathId = 0; } } } - RemovePathSearch(pathEntity); + // If successful, just remove the path search, otherwise delete the path entity and its search. + if (pathId > 0) + RemovePathSearch(pathEntity); + else + DeletePathEntity(pathEntity); return pathId; } diff --git a/rts/Sim/Path/QTPFS/PathSearch.cpp b/rts/Sim/Path/QTPFS/PathSearch.cpp index 3f4fa65c1c6..b64130300c5 100644 --- a/rts/Sim/Path/QTPFS/PathSearch.cpp +++ b/rts/Sim/Path/QTPFS/PathSearch.cpp @@ -181,7 +181,7 @@ void QTPFS::PathSearch::InitializeThread(SearchThreadData* threadData, IPath* pa // auto *pathToRepair = ( tryPathRepair && registry.valid(QTPFS::entity(searchID)) ) // ? registry.try_get(QTPFS::entity(searchID)) : nullptr; // FIXME: race condition - // Path repairs need only search to the point of finding the beginning of the renaming clean part of the old path. + // Path repairs need only search to the point of finding the beginning of the remaining clean part of the old path. // Such searches are also restricted in the area they can search to avoid creating poor paths that would be better // off being recreated from scratch. doPathRepair = tryPathRepair diff --git a/rts/Sim/Units/CommandAI/BuilderCAI.cpp b/rts/Sim/Units/CommandAI/BuilderCAI.cpp index b747e342615..e1faddb7dd6 100644 --- a/rts/Sim/Units/CommandAI/BuilderCAI.cpp +++ b/rts/Sim/Units/CommandAI/BuilderCAI.cpp @@ -891,13 +891,13 @@ void CBuilderCAI::ExecuteGuard(Command& c) StopSlowGuard(); } return; - } else if (b->curReclaim && owner->unitDef->canReclaim) { + } else if (b->curReclaim && !b->curReclaim->detached && owner->unitDef->canReclaim) { StopSlowGuard(); if (!ReclaimObject(b->curReclaim)) { StopMove(); } return; - } else if (b->curResurrect && owner->unitDef->canResurrect) { + } else if (b->curResurrect && !b->curResurrect->detached && owner->unitDef->canResurrect) { StopSlowGuard(); if (!ResurrectObject(b->curResurrect)) { StopMove(); diff --git a/rts/Sim/Units/Scripts/CobInstance.cpp b/rts/Sim/Units/Scripts/CobInstance.cpp index f4028a80797..e073959df9b 100644 --- a/rts/Sim/Units/Scripts/CobInstance.cpp +++ b/rts/Sim/Units/Scripts/CobInstance.cpp @@ -42,6 +42,20 @@ /******************************************************************************/ /******************************************************************************/ +// COB scripts encode angles as TA units where a full turn is COBSCALE (65536), +// so any angle past a half turn exceeds the range of a signed short and is meant +// to wrap around (it is a circular 16-bit angle). Truncating the scaled value to +// int first is well defined for the bounded angles the sim feeds in, and the +// following int->short narrowing performs that modular wrap deterministically. +// Converting straight from float to short would be undefined behaviour once the +// value leaves short's range, and produced different results on arm64 vs x86, +// desyncing multiplayer. +static inline short RadAngleToCobShort(float radAngle) +{ + return static_cast(static_cast(radAngle * RAD2TAANG)); +} + + CR_BIND_DERIVED(CCobInstance, CUnitScript, ) CR_REG_METADATA(CCobInstance, ( @@ -237,7 +251,7 @@ void CCobInstance::WindChanged(float heading, float speed) { ZoneScoped; Call(COBFN_SetSpeed, int(speed * 3000.0f)); - Call(COBFN_SetDirection, short(heading * RAD2TAANG)); + Call(COBFN_SetDirection, RadAngleToCobShort(heading)); } @@ -387,8 +401,8 @@ void CCobInstance::StartBuilding(float heading, float pitch) std::array callinArgs; callinArgs[0] = 2; - callinArgs[1] = short(heading * RAD2TAANG); - callinArgs[2] = short( pitch * RAD2TAANG); + callinArgs[1] = RadAngleToCobShort(heading); + callinArgs[2] = RadAngleToCobShort(pitch); Call(COBFN_StartBuilding, callinArgs); } @@ -439,8 +453,8 @@ void CCobInstance::AimWeapon(int weaponNum, float heading, float pitch) std::array callinArgs; callinArgs[0] = 2; - callinArgs[1] = short(heading * RAD2TAANG); - callinArgs[2] = short( pitch * RAD2TAANG); + callinArgs[1] = RadAngleToCobShort(heading); + callinArgs[2] = RadAngleToCobShort(pitch); Call(COBFN_AimPrimary + COBFN_Weapon_Funcs * weaponNum, callinArgs, CBAimWeapon, weaponNum, nullptr); } diff --git a/rts/Sim/Units/Scripts/LuaUnitScript.cpp b/rts/Sim/Units/Scripts/LuaUnitScript.cpp index 1b6f4e7da4a..c600a0764fc 100644 --- a/rts/Sim/Units/Scripts/LuaUnitScript.cpp +++ b/rts/Sim/Units/Scripts/LuaUnitScript.cpp @@ -1023,6 +1023,13 @@ void CLuaUnitScript::EndBurst(int weaponNum) { ZoneScoped; Call(LUAFN_EndBurst, /******************************************************************************/ +/*** + * UnitScript API — controls unit animation, piece visibility, and COB values. + * Accessed via `Spring.UnitScript` (synced only). + * + * @see Spring.UnitScript + * @class UnitScriptTable + */ bool CLuaUnitScript::PushEntries(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1144,6 +1151,13 @@ static inline int ParseAxis(lua_State* L, const char* caller, int index) /******************************************************************************/ +/*** Create a Lua unit script for the given unit, replacing any existing script. + * + * @function UnitScriptTable.CreateScript + * @param unitID UnitID + * @param callIns table + * @return nil + */ int CLuaUnitScript::CreateScript(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1172,6 +1186,14 @@ int CLuaUnitScript::CreateScript(lua_State* L) } +/*** Update or remove a callIn on a unit script. + * + * @function UnitScriptTable.UpdateCallIn + * @param unitID UnitID + * @param callin string + * @param func function? + * @return nil + */ int CLuaUnitScript::UpdateCallIn(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1196,6 +1218,14 @@ int CLuaUnitScript::UpdateCallIn(lua_State* L) } +/*** Execute a function in the context of a unit's script environment. + * + * @function UnitScriptTable.CallAsUnit + * @param unitID UnitID + * @param func function + * @param ... any arguments passed to func + * @return any ... values returned by func + */ int CLuaUnitScript::CallAsUnit(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1264,6 +1294,14 @@ int CLuaUnitScript::GetUnitValue(lua_State* L, CUnitScript* script, int arg) } +/*** Get a COB/script value for a unit (synced gadget API). + * + * @function UnitScriptTable.GetUnitCOBValue + * @param unitID UnitID + * @param val integer COB value ID (use COB constants) + * @param ... number optional extra args for certain COB values + * @return integer value + */ int CLuaUnitScript::GetUnitCOBValue(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1276,6 +1314,12 @@ int CLuaUnitScript::GetUnitCOBValue(lua_State* L) } +/*** Get a COB/script value. Must be called from within a UnitScript callin. + * + * @function UnitScriptTable.GetUnitValue + * @param val integer COB value ID (use COB constants) + * @return integer value + */ int CLuaUnitScript::GetUnitValue(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1307,6 +1351,14 @@ int CLuaUnitScript::SetUnitValue(lua_State* L, CUnitScript* script, int arg) } +/*** Set a COB/script value for a unit (synced gadget API). + * + * @function UnitScriptTable.SetUnitCOBValue + * @param unitID UnitID + * @param val integer COB value ID (use COB constants) + * @param param integer|boolean value to set + * @return nil + */ int CLuaUnitScript::SetUnitCOBValue(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1319,6 +1371,13 @@ int CLuaUnitScript::SetUnitCOBValue(lua_State* L) } +/*** Set a COB/script value. Must be called from within a UnitScript callin. + * + * @function UnitScriptTable.SetUnitValue + * @param val integer COB value ID (use COB constants) + * @param param integer|boolean value to set + * @return nil + */ int CLuaUnitScript::SetUnitValue(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1329,6 +1388,13 @@ int CLuaUnitScript::SetUnitValue(lua_State* L) } +/*** Set visibility of a piece. Must be called from within a UnitScript callin. + * + * @function UnitScriptTable.SetPieceVisibility + * @param piece integer 1-indexed piece number + * @param visible boolean + * @return nil + */ int CLuaUnitScript::SetPieceVisibility(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1346,6 +1412,13 @@ int CLuaUnitScript::SetPieceVisibility(lua_State* L) } +/*** Emit a special effect at a piece. Must be called from within a UnitScript callin. + * + * @function UnitScriptTable.EmitSfx + * @param piece integer 1-indexed piece number + * @param type integer|string SFX type constant or CEG name + * @return nil + */ int CLuaUnitScript::EmitSfx(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1362,6 +1435,13 @@ int CLuaUnitScript::EmitSfx(lua_State* L) } +/*** Attach a unit to a piece of another unit. + * + * @function UnitScriptTable.AttachUnit + * @param piece integer 1-indexed piece number + * @param transporteeID UnitID + * @return nil + */ int CLuaUnitScript::AttachUnit(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1379,6 +1459,12 @@ int CLuaUnitScript::AttachUnit(lua_State* L) } +/*** Drop a transported unit. + * + * @function UnitScriptTable.DropUnit + * @param transporteeID UnitID + * @return nil + */ int CLuaUnitScript::DropUnit(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1396,6 +1482,13 @@ int CLuaUnitScript::DropUnit(lua_State* L) } +/*** Explode a piece with the given flags. + * + * @function UnitScriptTable.Explode + * @param piece integer 1-indexed piece number + * @param flags integer SFX explosion flags (bitfield) + * @return nil + */ int CLuaUnitScript::Explode(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1410,6 +1503,12 @@ int CLuaUnitScript::Explode(lua_State* L) } +/*** Show a flare at a piece. + * + * @function UnitScriptTable.ShowFlare + * @param piece integer 1-indexed piece number + * @return nil + */ int CLuaUnitScript::ShowFlare(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1423,6 +1522,15 @@ int CLuaUnitScript::ShowFlare(lua_State* L) } +/*** Start spinning a piece around an axis. + * + * @function UnitScriptTable.Spin + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @param speed number angular speed in radians/frame + * @param accel number? angular acceleration (Default: `0`, instant) + * @return nil + */ int CLuaUnitScript::Spin(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1440,6 +1548,14 @@ int CLuaUnitScript::Spin(lua_State* L) } +/*** Stop spinning a piece. + * + * @function UnitScriptTable.StopSpin + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @param decel number? angular deceleration (Default: `0`, instant) + * @return nil + */ int CLuaUnitScript::StopSpin(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1456,6 +1572,15 @@ int CLuaUnitScript::StopSpin(lua_State* L) } +/*** Turn a piece to an angle. If speed is 0 or omitted, turns instantly. + * + * @function UnitScriptTable.Turn + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @param destination number target angle in radians + * @param speed number? angular speed (Default: `0`, instant) + * @return nil + */ int CLuaUnitScript::Turn(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1479,6 +1604,15 @@ int CLuaUnitScript::Turn(lua_State* L) } +/*** Move a piece along an axis. If speed is 0 or omitted, moves instantly. + * + * @function UnitScriptTable.Move + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @param destination number target position + * @param speed number? movement speed (Default: `0`, instant) + * @return nil + */ int CLuaUnitScript::Move(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1501,6 +1635,14 @@ int CLuaUnitScript::Move(lua_State* L) return 0; } +/*** Scale a piece. If speed is 0 or omitted, scales instantly. + * + * @function UnitScriptTable.Scale + * @param piece integer 1-indexed piece number + * @param destination number target scale factor + * @param speed number? scaling speed (Default: `0`, instant) + * @return nil + */ int CLuaUnitScript::Scale(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1593,6 +1735,13 @@ int CLuaUnitScript::IsInAnimation(lua_State* L, const char* caller, AnimType typ } +/*** Check if a piece is currently turning. + * + * @function UnitScriptTable.IsInTurn + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @return boolean + */ int CLuaUnitScript::IsInTurn(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1600,6 +1749,13 @@ int CLuaUnitScript::IsInTurn(lua_State* L) } +/*** Check if a piece is currently moving. + * + * @function UnitScriptTable.IsInMove + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @return boolean + */ int CLuaUnitScript::IsInMove(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1607,12 +1763,25 @@ int CLuaUnitScript::IsInMove(lua_State* L) } +/*** Check if a piece is currently spinning. + * + * @function UnitScriptTable.IsInSpin + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @return boolean + */ int CLuaUnitScript::IsInSpin(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; return IsInAnimation(L, __func__, ASpin); } +/*** Check if a piece is currently scaling. + * + * @function UnitScriptTable.IsInScale + * @param piece integer 1-indexed piece number + * @return boolean + */ int CLuaUnitScript::IsInScale(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1645,6 +1814,13 @@ int CLuaUnitScript::WaitForAnimation(lua_State* L, const char* caller, AnimType } +/*** Check whether the calling thread needs to wait for a turn to finish. + * + * @function UnitScriptTable.WaitForTurn + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @return boolean needsWait + */ int CLuaUnitScript::WaitForTurn(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1652,12 +1828,25 @@ int CLuaUnitScript::WaitForTurn(lua_State* L) } +/*** Check whether the calling thread needs to wait for a move to finish. + * + * @function UnitScriptTable.WaitForMove + * @param piece integer 1-indexed piece number + * @param axis integer axis (1=x, 2=y, 3=z) + * @return boolean needsWait + */ int CLuaUnitScript::WaitForMove(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; return WaitForAnimation(L, __func__, AMove); } +/*** Check whether the calling thread needs to wait for a scale to finish. + * + * @function UnitScriptTable.WaitForScale + * @param piece integer 1-indexed piece number + * @return boolean needsWait + */ int CLuaUnitScript::WaitForScale(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1677,6 +1866,12 @@ int CLuaUnitScript::WaitForScale(lua_State* L) } +/*** Signal that the Killed callin has finished. Must be called from Killed. + * + * @function UnitScriptTable.SetDeathScriptFinished + * @param wreckLevel integer? (Default: `-1`) + * @return nil + */ int CLuaUnitScript::SetDeathScriptFinished(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1694,6 +1889,14 @@ int CLuaUnitScript::SetDeathScriptFinished(lua_State* L) /******************************************************************************/ +/*** Get the local translation of a piece relative to its rest position. + * + * @function UnitScriptTable.GetPieceTranslation + * @param piece integer 1-indexed piece number + * @return number x + * @return number y + * @return number z + */ int CLuaUnitScript::GetPieceTranslation(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1705,6 +1908,14 @@ int CLuaUnitScript::GetPieceTranslation(lua_State* L) } +/*** Get the rotation of a piece in radians. + * + * @function UnitScriptTable.GetPieceRotation + * @param piece integer 1-indexed piece number + * @return number rx + * @return number ry + * @return number rz + */ int CLuaUnitScript::GetPieceRotation(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1715,6 +1926,12 @@ int CLuaUnitScript::GetPieceRotation(lua_State* L) return ToLua(L, piece->GetRotation()); } +/*** Get the scale factor of a piece. + * + * @function UnitScriptTable.GetPieceScale + * @param piece integer 1-indexed piece number + * @return number scale + */ int CLuaUnitScript::GetPieceScale(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1727,6 +1944,17 @@ int CLuaUnitScript::GetPieceScale(lua_State* L) } +/*** Get the world-space emit position and direction of a piece. + * + * @function UnitScriptTable.GetPiecePosDir + * @param piece integer 1-indexed piece number + * @return number posX + * @return number posY + * @return number posZ + * @return number dirX + * @return number dirY + * @return number dirZ + */ int CLuaUnitScript::GetPiecePosDir(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; @@ -1749,6 +1977,11 @@ int CLuaUnitScript::GetPiecePosDir(lua_State* L) /******************************************************************************/ /******************************************************************************/ +/*** Get the unit ID of the currently executing unit script. + * + * @function UnitScriptTable.GetActiveUnitID + * @return UnitID? unitID + */ int CLuaUnitScript::GetActiveUnitID(lua_State* L) { RECOIL_DETAILED_TRACY_ZONE; diff --git a/rts/Sim/Units/Unit.cpp b/rts/Sim/Units/Unit.cpp index 9d2f32b304d..44ac4ab71e5 100644 --- a/rts/Sim/Units/Unit.cpp +++ b/rts/Sim/Units/Unit.cpp @@ -1,5 +1,6 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ +#include "System/RangesCompat.h" #include "UnitDef.h" #include "Unit.h" #include "UnitHandler.h" @@ -965,7 +966,7 @@ static auto SplitResourcePackIntoPositiveNegative (const SResourcePack &pack) { SResourcePack positive {0.0f}, negative {0.0f}; - for (auto [resourceID, value] : std::views::enumerate (pack)) { + for (auto [resourceID, value] : spring::views::enumerate (pack)) { if (value < 0.0f) negative[resourceID] = -value; else diff --git a/rts/Sim/Units/UnitTypes/Builder.cpp b/rts/Sim/Units/UnitTypes/Builder.cpp index 4cf3240b1cd..deb8c846b73 100644 --- a/rts/Sim/Units/UnitTypes/Builder.cpp +++ b/rts/Sim/Units/UnitTypes/Builder.cpp @@ -605,6 +605,15 @@ void CBuilder::SetRepairTarget(CUnit* target) void CBuilder::SetReclaimTarget(CSolidObject* target) { RECOIL_DETAILED_TRACY_ZONE; + // A target already being destroyed (detached) cannot have a death dependence + // registered (AddDeathDependence no-ops on detached objects), which would leave + // curReclaim dangling. This happens when ~CObject's DependentDied cascade re-enters + // reclaim logic mid-deletion (e.g. CBuilderCAI::ExecuteGuard reading a guardee's + // stale curReclaim) on the very feature being freed. Refuse the dead target. + assert(target != nullptr); + if (target->detached) + return; + if (dynamic_cast(target) != nullptr && !static_cast(target)->def->reclaimable) return; @@ -630,6 +639,11 @@ void CBuilder::SetReclaimTarget(CSolidObject* target) void CBuilder::SetResurrectTarget(CFeature* target) { RECOIL_DETAILED_TRACY_ZONE; + // see SetReclaimTarget: never depend on an object that is already being destroyed + assert(target != nullptr); + if (target->detached) + return; + if (curResurrect == target || target->udef == nullptr) return; @@ -646,6 +660,11 @@ void CBuilder::SetResurrectTarget(CFeature* target) void CBuilder::SetCaptureTarget(CUnit* target) { RECOIL_DETAILED_TRACY_ZONE; + // see SetReclaimTarget: never depend on an object that is already being destroyed + assert(target != nullptr); + if (target->detached) + return; + if (target == curCapture) return; diff --git a/rts/Sim/Weapons/Weapon.cpp b/rts/Sim/Weapons/Weapon.cpp index 21f304d3bf9..3424e1d5f5a 100644 --- a/rts/Sim/Weapons/Weapon.cpp +++ b/rts/Sim/Weapons/Weapon.cpp @@ -819,6 +819,12 @@ void CWeapon::HoldIfTargetInvalid() return; if (!TryTarget(currentTarget)) { + // BombDroppers must retain ground targets until their active salvo ends. + // Dropping one after the aircraft passes the target prevents the CAI from + // associating the completed salvo with its current attack command. + if (noAutoTarget && HavePosTarget() && salvoLeft > 0) + return; + DropCurrentTarget(); return; } diff --git a/rts/System/CMakeLists.txt b/rts/System/CMakeLists.txt index c8fae324574..00022fa567b 100644 --- a/rts/System/CMakeLists.txt +++ b/rts/System/CMakeLists.txt @@ -27,6 +27,7 @@ make_global_var(sources_engine_System_common "${CMAKE_CURRENT_SOURCE_DIR}/Input/MouseInput.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LoadSave/CregLoadSaveHandler.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LoadSave/Demo.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/LoadSave/DemoFileExtension.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LoadSave/DemoReader.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LoadSave/DemoRecorder.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/LoadSave/LoadSaveHandler.cpp" diff --git a/rts/System/EventClient.h b/rts/System/EventClient.h index 3e17d1171c2..6d938cebd5a 100644 --- a/rts/System/EventClient.h +++ b/rts/System/EventClient.h @@ -4,6 +4,7 @@ #define EVENT_CLIENT_H #include +#include #include #include #include @@ -35,6 +36,7 @@ struct BuildInfo; struct FeatureDef; class LuaMaterial; struct WeaponDef; +struct SResourcePack; #ifndef zipFile // might be defined through zip.h already @@ -122,6 +124,8 @@ class CEventClient virtual void TeamDied(int teamID) {} virtual void TeamChanged(int teamID) {} + virtual bool ResourceExcess(const std::map & excess) { return false; } + virtual void PlayerChanged(int playerID) {} virtual void PlayerAdded(int playerID) {} virtual void PlayerRemoved(int playerID, int reason) {} diff --git a/rts/System/EventHandler.cpp b/rts/System/EventHandler.cpp index d7969be7aa7..f815ae7f33e 100644 --- a/rts/System/EventHandler.cpp +++ b/rts/System/EventHandler.cpp @@ -581,6 +581,12 @@ void CEventHandler::TeamDied(int teamID) ITERATE_EVENTCLIENTLIST(TeamDied, teamID); } +bool CEventHandler::ResourceExcess(const std::map &excess) +{ + ZoneScoped; + return ControlIterateDefFalse(listResourceExcess, &CEventClient::ResourceExcess, excess); +} + void CEventHandler::TeamChanged(int teamID) { ZoneScoped; diff --git a/rts/System/EventHandler.h b/rts/System/EventHandler.h index e7432aaaf50..31d77790597 100644 --- a/rts/System/EventHandler.h +++ b/rts/System/EventHandler.h @@ -60,6 +60,8 @@ class CEventHandler void TeamDied(int teamID); void TeamChanged(int teamID); + bool ResourceExcess(const std::map & excess); + void PlayerChanged(int playerID); void PlayerAdded(int playerID); void PlayerRemoved(int playerID, int reason); diff --git a/rts/System/Events.def b/rts/System/Events.def index a0e069c27d3..e36f11dac3d 100644 --- a/rts/System/Events.def +++ b/rts/System/Events.def @@ -31,8 +31,12 @@ SETUP_EVENT(GamePaused, MANAGED_BIT) SETUP_EVENT(GameFrame, MANAGED_BIT) SETUP_EVENT(GameFramePost, MANAGED_BIT) + SETUP_EVENT(TeamDied, MANAGED_BIT) SETUP_EVENT(TeamChanged, MANAGED_BIT) + + SETUP_EVENT(ResourceExcess, MANAGED_BIT | CONTROL_BIT) + SETUP_EVENT(PlayerChanged, MANAGED_BIT | UNSYNCED_BIT) SETUP_EVENT(PlayerAdded, MANAGED_BIT | UNSYNCED_BIT) SETUP_EVENT(PlayerRemoved, MANAGED_BIT | UNSYNCED_BIT) diff --git a/rts/System/FileSystem/Archives/DirArchive.cpp b/rts/System/FileSystem/Archives/DirArchive.cpp index 829a56c4251..55242d39db0 100644 --- a/rts/System/FileSystem/Archives/DirArchive.cpp +++ b/rts/System/FileSystem/Archives/DirArchive.cpp @@ -42,7 +42,7 @@ CDirArchive::CDirArchive(const std::string& archiveName) // all variables here will use forward slashes, no need for conversion std::string rawFileName = dataDirsAccess.LocateFile(dirName + origName); - files.emplace_back(origName, std::move(rawFileName), -1, 0); + files.emplace_back(Files{origName, std::move(rawFileName), -1, 0}); // convert to lowercase and store lcNameIndex[StringToLower(std::move(origName))] = static_cast(files.size() - 1); diff --git a/rts/System/FileSystem/Archives/SevenZipArchive.cpp b/rts/System/FileSystem/Archives/SevenZipArchive.cpp index e40ce9e798d..d5994770067 100644 --- a/rts/System/FileSystem/Archives/SevenZipArchive.cpp +++ b/rts/System/FileSystem/Archives/SevenZipArchive.cpp @@ -147,12 +147,12 @@ CSevenZipArchive::CSevenZipArchive(const std::string& name) continue; } - const auto& fd = fileEntries.emplace_back( - i, //fp - SzArEx_GetFileSize(&db, i), // size + const auto& fd = fileEntries.emplace_back(FileEntry{ + static_cast(i), //fp + static_cast(SzArEx_GetFileSize(&db, i)), // size db.MTime.Vals ? static_cast(CTimeUtil::NTFSTimeToTime64(db.MTime.Vals[i].Low, db.MTime.Vals[i].High)) : 0, // modtime std::move(fileName.value()) // origName - ); + }); lcNameIndex.emplace(StringToLower(fd.origName), fileEntries.size() - 1); } diff --git a/rts/System/FileSystem/Archives/ZipArchive.cpp b/rts/System/FileSystem/Archives/ZipArchive.cpp index 37914269c49..e7b13e3e790 100644 --- a/rts/System/FileSystem/Archives/ZipArchive.cpp +++ b/rts/System/FileSystem/Archives/ZipArchive.cpp @@ -58,13 +58,13 @@ CZipArchive::CZipArchive(const std::string& archiveName) unz_file_pos fp{}; unzGetFilePos(zip, &fp); - const auto& fd = fileEntries.emplace_back( + const auto& fd = fileEntries.emplace_back(FileEntry{ std::move(fp), //fp - info.uncompressed_size, //size + static_cast(info.uncompressed_size), //size fName, //origName - info.crc, //crc + static_cast(info.crc), //crc static_cast(CTimeUtil::DosTimeToTime64(info.dosDate)) //modTime - ); + }); lcNameIndex.emplace(StringToLower(fd.origName), fileEntries.size() - 1); } diff --git a/rts/System/FileSystem/FileSystem.cpp b/rts/System/FileSystem/FileSystem.cpp index d1471810f48..f319d2199ca 100644 --- a/rts/System/FileSystem/FileSystem.cpp +++ b/rts/System/FileSystem/FileSystem.cpp @@ -61,7 +61,7 @@ namespace Impl { return std::string(reinterpret_cast(utf8.c_str())); } RECOIL_FORCE_INLINE std::string StoreUTF8AsString(const std::u8string_view& utf8) { - return std::string(reinterpret_cast(utf8.data())); + return std::string(reinterpret_cast(utf8.data()), utf8.size()); } RECOIL_FORCE_INLINE std::string StorePathAsString(const fs::path& path) { return StoreUTF8AsString(path.u8string()); diff --git a/rts/System/Input/KeyInput.cpp b/rts/System/Input/KeyInput.cpp index ea46846863e..c7599558340 100644 --- a/rts/System/Input/KeyInput.cpp +++ b/rts/System/Input/KeyInput.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -24,6 +25,10 @@ namespace KeyInput { static SDL_Keymod keyMods; + // keycodes forced down by debug.emulateKey*; re-applied on top of the SDL + // poll at the end of every Update so they survive the re-poll + static std::set emulatedKeyCodes; + bool IsKeyPressed(int keyCode) { const auto& pred = keyCmp; @@ -61,6 +66,19 @@ namespace KeyInput { iter->second = isPressed; } + // mark a code pressed, inserting it (keeping the vector sorted) if the poll + // never created a slot for it -- headless has no SDL keyboard, so keyVec/scanVec + // come back empty and plain SetKeyPressed would have nothing to flip + static void ForcePressed(std::vector& vec, int code) { + const auto iter = std::lower_bound(vec.begin(), vec.end(), Key{code, false}, keyCmp); + + if (iter != vec.end() && iter->first == code) { + iter->second = true; + } else { + vec.insert(iter, Key{code, true}); + } + } + void SetKeyModState(int mod, bool isPressed) { if (isPressed) { keyMods = SDL_Keymod(keyMods | mod); @@ -73,6 +91,26 @@ namespace KeyInput { return (keyMods & mod); } + bool IsKeyEmulated(int keyCode) { + return emulatedKeyCodes.contains(keyCode); + } + + void SetKeyEmulated(int keyCode, bool pressed) { + if (pressed) { + emulatedKeyCodes.insert(keyCode); + } else { + emulatedKeyCodes.erase(keyCode); + } + } + + const std::set& GetEmulatedKeys() { + return emulatedKeyCodes; + } + + void ClearEmulatedKeys() { + emulatedKeyCodes.clear(); + } + /** * Tests SDL keystates and sets values in key array */ @@ -108,6 +146,21 @@ namespace KeyInput { SetKeyPressed(SDL_SCANCODE_LCTRL , GetKeyModState(KMOD_CTRL )); SetKeyPressed(SDL_SCANCODE_LGUI , GetKeyModState(KMOD_GUI )); SetKeyPressed(SDL_SCANCODE_LSHIFT, GetKeyModState(KMOD_SHIFT)); + + // OR the emulated keys back in: the poll above only reflects real hardware, + // so anything held via debug.emulateKey* has to be re-applied here to show + // up in IsKeyPressed / GetKeyModState / GetPressedKeys + for (const int keyCode: emulatedKeyCodes) { + ForcePressed(keyVec, keyCode); + ForcePressed(scanVec, SDL_GetScancodeFromKey((SDL_Keycode)keyCode)); + + switch (keyCode) { + case SDLK_LALT: case SDLK_RALT: SetKeyModState(KMOD_ALT , true); break; + case SDLK_LCTRL: case SDLK_RCTRL: SetKeyModState(KMOD_CTRL , true); break; + case SDLK_LGUI: case SDLK_RGUI: SetKeyModState(KMOD_GUI , true); break; + case SDLK_LSHIFT: case SDLK_RSHIFT: SetKeyModState(KMOD_SHIFT, true); break; + } + } } const std::vector& GetPressedKeys() diff --git a/rts/System/Input/KeyInput.h b/rts/System/Input/KeyInput.h index 980192a56bb..e493cdcdf93 100644 --- a/rts/System/Input/KeyInput.h +++ b/rts/System/Input/KeyInput.h @@ -4,6 +4,7 @@ #define KEYBOARD_INPUT_H #include +#include namespace KeyInput { void Update(int fakeMetaKey); @@ -14,6 +15,12 @@ namespace KeyInput { void SetKeyModState(int mod, bool pressed); bool GetKeyModState(int mod); + // input emulation (debug.emulateKey*): keys forced down independent of hardware + bool IsKeyEmulated(int keyCode); + void SetKeyEmulated(int keyCode, bool pressed); + const std::set& GetEmulatedKeys(); + void ClearEmulatedKeys(); + typedef std::pair Key; const std::vector& GetPressedKeys(); diff --git a/rts/System/Input/MouseInput.cpp b/rts/System/Input/MouseInput.cpp index c7fbed6708c..c76f45ac146 100644 --- a/rts/System/Input/MouseInput.cpp +++ b/rts/System/Input/MouseInput.cpp @@ -75,14 +75,15 @@ bool IMouseInput::HandleSDLMouseEvent(const SDL_Event& event) case SDL_MOUSEBUTTONDOWN: { mousepos = int2(event.button.x, event.button.y); - if (mouse != nullptr) + // suppress if the button is already held via input emulation + if (mouse != nullptr && !mouse->IsButtonEmulated(event.button.button)) mouse->MousePress(mousepos.x, mousepos.y, event.button.button); } break; case SDL_MOUSEBUTTONUP: { mousepos = int2(event.button.x, event.button.y); - if (mouse != nullptr) + if (mouse != nullptr && !mouse->IsButtonEmulated(event.button.button)) mouse->MouseRelease(mousepos.x, mousepos.y, event.button.button); } break; diff --git a/rts/System/LoadSave/CregLoadSaveHandler.cpp b/rts/System/LoadSave/CregLoadSaveHandler.cpp index a6db8105d36..a3ed63a7beb 100644 --- a/rts/System/LoadSave/CregLoadSaveHandler.cpp +++ b/rts/System/LoadSave/CregLoadSaveHandler.cpp @@ -218,10 +218,9 @@ static void PrintSize(const char* txt, int size) static void ReadString(std::istream& s, std::string& str) { - char cstr[MAX_STRING_SIZE + 1]; - s.getline(cstr, sizeof(cstr) - 1, 0); - str.clear(); - str.append(cstr); + std::getline(s, str, '\0'); + if (str.length() > MAX_STRING_SIZE) + throw content_error("[creg::ReadString] string too long"); } diff --git a/rts/System/LoadSave/DemoFileExtension.cpp b/rts/System/LoadSave/DemoFileExtension.cpp new file mode 100644 index 00000000000..137e0fe404c --- /dev/null +++ b/rts/System/LoadSave/DemoFileExtension.cpp @@ -0,0 +1,61 @@ +/* This file is part of the Recoil engine (GPL v2 or later), see LICENSE.html */ + +#include "DemoFileExtension.h" + +#include +#include +#include +#include + +#include + +#include "demofile.h" +#include "System/StringUtil.h" + +// Tools do not link the config subsystem, and accept any extension anyway. +#ifndef TOOLS +#include "System/Config/ConfigHandler.h" + +CONFIG(std::string, DemoFileExtension).defaultValue("sdfz").description("Comma-separated list of replay file extensions. The first entry is used when recording; all entries are accepted when loading. Set by the lobby (e.g. 'barreplay,sdfz' for BAR)."); + +std::vector GetDemoFileExtensions() +{ + const auto configValue = configHandler->GetString("DemoFileExtension"); + + std::vector extensions; + + for (const auto& part : configValue | std::views::split(',')) { + auto extension = StringTrim(std::string(part.begin(), part.end())); + + if (!extension.empty() && extension.find_first_of("/\\.") == std::string::npos) + extensions.emplace_back(std::move(extension)); + } + + if (extensions.empty()) + extensions.emplace_back("sdfz"); + return extensions; +} +#endif + +bool IsDemoExtension(const std::string& ext) +{ +#ifdef TOOLS + return true; +#else + std::string lower = ext; + std::transform(lower.begin(), lower.end(), lower.begin(), [](unsigned char c) { return std::tolower(c); }); + const auto extensions = GetDemoFileExtensions(); + return std::ranges::contains(extensions, lower); +#endif +} + +bool ContentsLookLikeAReplay(const std::string& path) +{ + gzFile file = gzopen(path.c_str(), "rb"); + if (file == nullptr) + return false; + decltype(DemoFileHeader::magic) magic = {}; + const int bytesRead = gzread(file, magic, sizeof(magic)); + gzclose(file); + return (bytesRead == static_cast(sizeof(magic)) && memcmp(magic, DEMOFILE_MAGIC, sizeof(magic)) == 0); +} diff --git a/rts/System/LoadSave/DemoFileExtension.h b/rts/System/LoadSave/DemoFileExtension.h new file mode 100644 index 00000000000..b620bd28afc --- /dev/null +++ b/rts/System/LoadSave/DemoFileExtension.h @@ -0,0 +1,10 @@ +/* This file is part of the Recoil engine (GPL v2 or later), see LICENSE.html */ + +#pragma once + +#include +#include + +std::vector GetDemoFileExtensions(); +bool IsDemoExtension(const std::string& ext); +bool ContentsLookLikeAReplay(const std::string& path); diff --git a/rts/System/LoadSave/DemoReader.cpp b/rts/System/LoadSave/DemoReader.cpp index d999bb32a55..997db764af3 100644 --- a/rts/System/LoadSave/DemoReader.cpp +++ b/rts/System/LoadSave/DemoReader.cpp @@ -5,10 +5,13 @@ #include "Game/GameVersion.h" #include "Sim/Misc/GlobalConstants.h" +#include "DemoFileExtension.h" + #ifndef TOOLS #include "System/Config/ConfigHandler.h" CONFIG(bool, DisableDemoVersionCheck).defaultValue(false).description("Allow to play every replay file (may crash / cause undefined behaviour in replays)"); #endif + #include "System/Exceptions.h" #include "System/FileSystem/GZFileHandler.h" #include "System/FileSystem/FileSystem.h" @@ -53,8 +56,8 @@ CDemoReader::CDemoReader(const std::string& filename, float curTime): playbackDe { demoName = filename; - if (FileSystem::GetExtensionLowerCase(filename) != "sdfz") - throw content_error("Unknown demo extension: " + FileSystem::GetExtensionLowerCase(filename)); + if (!IsDemoExtension(FileSystem::GetExtensionLowerCase(filename))) + throw content_error("Unsupported demo extension: " + FileSystem::GetExtensionLowerCase(filename)); // file not found -> exception if (!playbackDemo->FileExists()) diff --git a/rts/System/LoadSave/DemoRecorder.cpp b/rts/System/LoadSave/DemoRecorder.cpp index 9ee78245b51..55e3781e2b2 100644 --- a/rts/System/LoadSave/DemoRecorder.cpp +++ b/rts/System/LoadSave/DemoRecorder.cpp @@ -6,11 +6,13 @@ #include #include "DemoRecorder.h" +#include "DemoFileExtension.h" #include "base64.h" #include "Game/GameVersion.h" #include "Sim/Misc/TeamStatistics.h" #include "System/TimeUtil.h" #include "System/StringUtil.h" +#include "System/Config/ConfigHandler.h" #include "System/FileSystem/DataDirsAccess.h" #include "System/FileSystem/FileSystem.h" #include "System/FileSystem/FileQueryFlags.h" @@ -160,12 +162,13 @@ void CDemoRecorder::SetName(const std::string& mapName, const std::string& modNa // oss << FileSystem::GetBasename(modName); // oss << "_"; oss << engineVersionName; - buf << oss.str() << ".sdfz"; + const std::string ext = "." + GetDemoFileExtensions()[0]; + buf << oss.str() << ext; int n = 0; while (FileSystem::FileExists(buf.str()) && (n < 99)) { buf.str(""); // clears content - buf << oss.str() << "_" << n++ << ".sdfz"; + buf << oss.str() << "_" << n++ << ext; } demoName = buf.str(); diff --git a/rts/System/Log/DefaultFilter.cpp b/rts/System/Log/DefaultFilter.cpp index cf0c3939d29..3ec33410cf1 100644 --- a/rts/System/Log/DefaultFilter.cpp +++ b/rts/System/Log/DefaultFilter.cpp @@ -157,13 +157,15 @@ void log_filter_section_setMinLevel(int level, const char* section) // (same string but will not become garbage) section = *registeredSection; - if (level == log_filter_section_getDefaultMinLevel(section)) { - using P = decltype(log_filter::sectionMinLevels)::value_type; - - const auto sectionComparer = [](const P& a, const P& b) { return (log_filter_section_compare()(a.first, b.first)); }; - const auto sectionMinLevel = std::lower_bound(secLvls.begin(), secLvls.begin() + log_filter::numLevels, P{section, 0}, sectionComparer); + // locate any existing override for this section (the array is kept sorted) + using P = decltype(log_filter::sectionMinLevels)::value_type; + const auto sectionComparer = [](const P& a, const P& b) { return (log_filter_section_compare()(a.first, b.first)); }; + const auto sectionMinLevel = std::lower_bound(secLvls.begin(), secLvls.begin() + log_filter::numLevels, P{section, 0}, sectionComparer); + const bool exists = (sectionMinLevel != (secLvls.begin() + log_filter::numLevels) && strcmp(sectionMinLevel->first, section) == 0); - if (sectionMinLevel == (secLvls.begin() + log_filter::numLevels) || strcmp(sectionMinLevel->first, section) != 0) + if (level == log_filter_section_getDefaultMinLevel(section)) { + // back to default: drop any existing override (nothing to do otherwise) + if (!exists) return; // erase @@ -175,6 +177,13 @@ void log_filter_section_setMinLevel(int level, const char* section) return; } + // non-default: update any existing override in-place + if (exists) { + sectionMinLevel->second = level; + return; + } + + // add a net new override secLvls[log_filter::numLevels++] = {section, level}; // swap into position diff --git a/rts/System/Matrix44f.cpp b/rts/System/Matrix44f.cpp index 6368561aca2..4c1328880dd 100644 --- a/rts/System/Matrix44f.cpp +++ b/rts/System/Matrix44f.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "System/simd_compat.h" @@ -18,6 +19,13 @@ CR_BIND(CMatrix44f, ) CR_REG_METADATA(CMatrix44f, CR_MEMBER(m)) static_assert(alignof(CMatrix44f) == 64); + +std::string CMatrix44f::str() const +{ + return std::format( + "m44(\n{:.3f} {:.3f} {:.3f} {:.3f}\n{:.3f} {:.3f} {:.3f} {:.3f}\n{:.3f} {:.3f} {:.3f} {:.3f}\n{:.3f} {:.3f} {:.3f} {:.3f})", + m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]); +} CMatrix44f::CMatrix44f(const CMatrix44f& mat) { memcpy(&m[0], &mat.m[0], sizeof(CMatrix44f)); diff --git a/rts/System/Matrix44f.h b/rts/System/Matrix44f.h index 3dd6767dd33..f8061e659d7 100644 --- a/rts/System/Matrix44f.h +++ b/rts/System/Matrix44f.h @@ -2,6 +2,7 @@ #pragma once +#include #include #include #include @@ -175,11 +176,7 @@ class CMatrix44f float4 col[4]; }; - std::string str() const { - return std::format( - "m44(\n{:.3f} {:.3f} {:.3f} {:.3f}\n{:.3f} {:.3f} {:.3f} {:.3f}\n{:.3f} {:.3f} {:.3f} {:.3f}\n{:.3f} {:.3f} {:.3f} {:.3f})", - m[0], m[4], m[8], m[12], m[1], m[5], m[9], m[13], m[2], m[6], m[10], m[14], m[3], m[7], m[11], m[15]); - } + std::string str() const; }; diff --git a/rts/System/MemPoolTypes.h b/rts/System/MemPoolTypes.h index dcd5730e040..af15588dc2e 100644 --- a/rts/System/MemPoolTypes.h +++ b/rts/System/MemPoolTypes.h @@ -7,13 +7,14 @@ #include #include // memset #include +#include #include #include #include #include #include -#include "smmalloc/smmalloc.h" +#include "System/recoil-smmalloc.h" #include "System/UnorderedMap.hpp" #include "System/ContainerUtil.h" @@ -62,14 +63,16 @@ template struct PassThroughPool { }; // Helper to infer the memory alignment and size from a set of types. -template -#if 0 // doesn't compile on MSVC 19.37 -struct TypesMem { - alignas(alignof(T)...) uint8_t data[std::max({sizeof(T)...})]; -}; -#else -using TypesMem = std::aligned_storage_t< std::max({ sizeof(T)... }), std::max({ alignof(T)... }) >; -#endif +template +inline constexpr size_t TypesMemAlignment = std::max({alignof(T)...}); + +template +inline constexpr size_t TypesMemSize = [] { + constexpr size_t alignment = TypesMemAlignment; + constexpr size_t size = std::max({sizeof(T)...}); + + return ((size + alignment - 1) / alignment) * alignment; +}(); template struct DynMemPool { public: @@ -163,8 +166,8 @@ template struct DynMemPool { }; // Helper to infer the DynMemPool pool parameters from a types. -template -using DynMemPoolT = DynMemPool), alignof(TypesMem)>; +template +using DynMemPoolT = DynMemPool, TypesMemAlignment>; // fixed-size dynamic version // page size per chunk, number of chunks, number of pages per chunk @@ -286,8 +289,8 @@ template struct FixedDynMemPool }; // Helper to infer the FixedDynMemPool pool parameters from a types. -template -using FixedDynMemPoolT = FixedDynMemPool), N, K, alignof(TypesMem)>; +template +using FixedDynMemPoolT = FixedDynMemPool, N, K, TypesMemAlignment>; // fixed-size version. template struct StaticMemPool { @@ -372,8 +375,8 @@ template struct StaticMemPool { }; // Helper to infer the StaticMemPool pool parameters from a types. -template -using StaticMemPoolT = StaticMemPool), alignof(TypesMem)>; +template +using StaticMemPoolT = StaticMemPool, TypesMemAlignment>; // dynamic memory allocator operating with stable index positions @@ -431,7 +434,7 @@ inline size_t StablePosAllocator::Allocate(size_t numElems) if (positionToSize.empty()) { size_t returnPos = data.size(); data.resize(data.size() + numElems); - myLog("StablePosAllocator::Allocate(%u) = %u [thread_id = %u]", uint32_t(numElems), uint32_t(returnPos), static_cast(Threading::GetCurrentThreadId())); + myLog("StablePosAllocator::Allocate(%u) = %u [thread_id = %u]", uint32_t(numElems), uint32_t(returnPos), Threading::GetCurrentThreadIdAsU32()); return returnPos; } diff --git a/rts/System/MemoryOverride.cpp b/rts/System/MemoryOverride.cpp index deaebdae367..7782592cc3e 100644 --- a/rts/System/MemoryOverride.cpp +++ b/rts/System/MemoryOverride.cpp @@ -70,7 +70,9 @@ void* aligned_alloc(size_t alignment, size_t size) #else // std::aligned_alloc requires size to be a multiple of alignment (C11/C++17); // unlike posix_memalign, passing a non-multiple is undefined behaviour - assert(size % alignment == 0); + size = (size + alignment - 1) & ~(alignment - 1); // bitwise round-up + assert(std::has_single_bit(alignment)); // assumption + assert(size % alignment == 0); // intention return std::aligned_alloc(alignment, size); #endif #endif diff --git a/rts/System/Platform/Linux/Futex.cpp b/rts/System/Platform/Linux/Futex.cpp index 1730b6ebc0a..037edf10c35 100644 --- a/rts/System/Platform/Linux/Futex.cpp +++ b/rts/System/Platform/Linux/Futex.cpp @@ -41,7 +41,7 @@ void spring_futex::lock() do { if ((c == 2) || __sync_val_compare_and_swap(&mtx, 1, 2) != 0) - do_futex(&mtx, FUTEX_WAIT, 2, NULL); + do_futex(&mtx, FUTEX_WAIT_PRIVATE, 2, NULL); } while((c = __sync_val_compare_and_swap(&mtx, 0, 2)) != 0); } @@ -56,7 +56,7 @@ void spring_futex::unlock() { if (__sync_fetch_and_sub(&mtx, 1) != 1) { mtx = 0; - do_futex(&mtx, FUTEX_WAKE, 4, NULL); + do_futex(&mtx, FUTEX_WAKE_PRIVATE, 4, NULL); } } @@ -137,7 +137,7 @@ void linux_signal::wait() const int g = gen.load(); // our gen sleepers++; while ((g - (m = mtx)) >= 0) { - do_futex(&mtx, FUTEX_WAIT, m, NULL); + do_futex(&mtx, FUTEX_WAIT_PRIVATE, m, NULL); } sleepers--; } @@ -156,7 +156,7 @@ void linux_signal::wait_for(spring_time t) const spring_time endTimer = spring_now() + t; while (((g - (m = mtx)) >= 0) && (spring_now() < endTimer)) { - do_futex(&mtx, FUTEX_WAIT, m, &linux_t); + do_futex(&mtx, FUTEX_WAIT_PRIVATE, m, &linux_t); } sleepers--; } @@ -168,6 +168,6 @@ void linux_signal::notify_all(const int min_sleepers) return; mtx = gen++; - do_futex(&mtx, FUTEX_WAKE, INT_MAX, NULL); + do_futex(&mtx, FUTEX_WAKE_PRIVATE, INT_MAX, NULL); } diff --git a/rts/System/Platform/Mac/CrashHandler.cpp b/rts/System/Platform/Mac/CrashHandler.cpp index 5716add1092..0705b63fca4 100644 --- a/rts/System/Platform/Mac/CrashHandler.cpp +++ b/rts/System/Platform/Mac/CrashHandler.cpp @@ -73,7 +73,7 @@ static void TranslateStackTrace(StackTrace& stacktrace, const int logLevel) stackFrame.path = ""; } - LOG_L(L_DEBUG, "\tsymbol = \"%s\", path = \"%s\", addr = 0x%lx", stackFrame.symbol.c_str(), path, stackFrame.ip); + LOG_L(L_DEBUG, "\tsymbol = \"%s\", path = \"%s\", addr = %p", stackFrame.symbol.c_str(), path, stackFrame.ip); } LOG_L(L_DEBUG, "[%s][2]", __func__); @@ -116,7 +116,14 @@ static void TranslateStackTrace(StackTrace& stacktrace, const int logLevel) execCommandString.clear(); stackFrameIndices.clear(); - execCommandBuffer << ADDR2LINE << " -o " << modulePath << " -arch x86_64 -l " << std::hex << addrPathPair.first; + #if defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) || defined(_M_AMD64) + constexpr const char* atosArch = "x86_64"; + #elif defined(__aarch64__) || defined(_M_ARM64) + constexpr const char* atosArch = "arm64"; + #else + #error "Unsupported architecture" + #endif + execCommandBuffer << ADDR2LINE << " -o " << modulePath << " -arch " << atosArch << " -l " << std::hex << addrPathPair.first; // insert requested addresses that should be translated by atos int i = 0; diff --git a/rts/System/Platform/Mac/SDLMain.h b/rts/System/Platform/Mac/SDLMain.h deleted file mode 100644 index 995d036c3f0..00000000000 --- a/rts/System/Platform/Mac/SDLMain.h +++ /dev/null @@ -1,18 +0,0 @@ -/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ - -/* - * SDLMain.m - main entry point for our Cocoa-ized SDL app - * Initial Version: Darrell Walisser - * Non-NIB-Code & other changes: Max Horn - * Feel free to customize this file to suit your needs. - */ - -#ifdef __APPLE__ - -#import - -@interface SDLMain : NSObject -@end - -#endif - diff --git a/rts/System/Platform/Mac/SDLMain.m b/rts/System/Platform/Mac/SDLMain.m deleted file mode 100644 index 2fea69074ad..00000000000 --- a/rts/System/Platform/Mac/SDLMain.m +++ /dev/null @@ -1,299 +0,0 @@ -/* SDLMain.m - main entry point for our Cocoa-ized SDL app - Initial Version: Darrell Walisser - Non-NIB-Code & other changes: Max Horn - - Feel free to customize this file to suit your needs -*/ - -#import "SDL.h" -#import "SDLMain.h" -#import /* for MAXPATHLEN */ -#import -#import - -/* Use this flag to determine whether we use SDLMain.nib or not */ -#define SDL_USE_NIB_FILE 0 - - -static int gArgc; -static char **gArgv; -static BOOL gFinderLaunch; - -//extern NSAutoreleasePool *pool; -//void PreInitMac(); - -void MacMessageBox(const char *msg, const char *caption, unsigned int flags){ - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert addButtonWithTitle:@"OK"]; - [alert setMessageText:[NSString stringWithCString:caption]]; - [alert setInformativeText:[NSString stringWithCString:msg]]; - [alert setAlertStyle:NSWarningAlertStyle]; - [alert runModal]; -} - -#if SDL_USE_NIB_FILE -/* A helper category for NSString */ -@interface NSString (ReplaceSubString) -- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; -@end -#else -/* An internal Apple class used to setup Apple menus */ -@interface NSAppleMenuController:NSObject {} -- (void)controlMenu:(NSMenu *)aMenu; -@end -#endif - -@interface SDLApplication : NSApplication -@end - -@implementation SDLApplication -/* Invoked from the Quit menu item */ -- (void)terminate:(id)sender -{ - /* Post a SDL_QUIT event */ - SDL_Event event; - event.type = SDL_QUIT; - SDL_PushEvent(&event); -} -@end - - -/* The main class of the application, the application's delegate */ -@implementation SDLMain - -/* Set the working directory to the .app's parent directory */ -- (void) setupWorkingDirectory:(BOOL)shouldChdir -{ - char parentdir[MAXPATHLEN]; - char *c; - - strncpy ( parentdir, gArgv[0], sizeof(parentdir) ); - c = (char*) parentdir; - - while (*c != '\0') /* go to end */ - c++; - - while (*c != '/') /* back up to parent */ - c--; - - *c++ = '\0'; /* cut off last part (binary name) */ - - if (shouldChdir) - { - assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */ - assert ( chdir ("../../../") == 0 ); /* chdir to the .app's parent */ - } -} - -#if SDL_USE_NIB_FILE - -/* Fix menu to contain the real app name instead of "SDL App" */ -- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName -{ - NSRange aRange; - NSEnumerator *enumerator; - NSMenuItem *menuItem; - - aRange = [[aMenu title] rangeOfString:@"SDL App"]; - if (aRange.length != 0) - [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; - - enumerator = [[aMenu itemArray] objectEnumerator]; - while ((menuItem = [enumerator nextObject])) - { - aRange = [[menuItem title] rangeOfString:@"SDL App"]; - if (aRange.length != 0) - [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; - if ([menuItem hasSubmenu]) - [self fixMenu:[menuItem submenu] withAppName:appName]; - } - [ aMenu sizeToFit ]; -} - -#else - -void setupAppleMenu(void) -{ - /* warning: this code is very odd */ - NSAppleMenuController *appleMenuController; - NSMenu *appleMenu; - NSMenuItem *appleMenuItem; - - appleMenuController = [[NSAppleMenuController alloc] init]; - appleMenu = [[NSMenu alloc] initWithTitle:@""]; - appleMenuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; - - [appleMenuItem setSubmenu:appleMenu]; - - /* yes, we do need to add it and then remove it -- - if you don't add it, it doesn't get displayed - if you don't remove it, you have an extra, titleless item in the menubar - when you remove it, it appears to stick around - very, very odd */ - [[NSApp mainMenu] addItem:appleMenuItem]; - [appleMenuController controlMenu:appleMenu]; - [[NSApp mainMenu] removeItem:appleMenuItem]; - [appleMenu release]; - [appleMenuItem release]; -} - -/* Create a window menu */ -void setupWindowMenu(void) -{ - NSMenu *windowMenu; - NSMenuItem *windowMenuItem; - NSMenuItem *menuItem; - - - windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; - - /* "Minimize" item */ - menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; - [windowMenu addItem:menuItem]; - [menuItem release]; - - /* Put menu into the menubar */ - windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; - [windowMenuItem setSubmenu:windowMenu]; - [[NSApp mainMenu] addItem:windowMenuItem]; - - /* Tell the application object that this is now the window menu */ - [NSApp setWindowsMenu:windowMenu]; - - /* Finally give up our references to the objects */ - [windowMenu release]; - [windowMenuItem release]; -} - -/* Replacement for NSApplicationMain */ -void CustomApplicationMain () -{ - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - SDLMain *sdlMain; - //PreInitMac(); - - /* Ensure the application object is initialised */ - [SDLApplication sharedApplication]; - - /* Set up the menubar */ - [NSApp setMainMenu:[[NSMenu alloc] init]]; - setupAppleMenu(); - setupWindowMenu(); - - /* Create SDLMain and make it the app delegate */ - sdlMain = [[SDLMain alloc] init]; - [NSApp setDelegate:sdlMain]; - - /* Bring the app to foreground */ - ProcessSerialNumber psn; - GetCurrentProcess(&psn); - TransformProcessType(&psn,kProcessTransformToForegroundApplication); - - /* Start the main event loop */ - [NSApp run]; - - [sdlMain release]; - [pool release]; -} - -#endif - -/* Called when the internal event loop has just started running */ -- (void) applicationDidFinishLaunching: (NSNotification *) note -{ - int status; - - /* Set the working directory to the .app's parent directory */ - [self setupWorkingDirectory:gFinderLaunch]; - -#if SDL_USE_NIB_FILE - /* Set the main menu to contain the real app name instead of "SDL App" */ - [self fixMenu:[NSApp mainMenu] withAppName:[[NSProcessInfo processInfo] processName]]; -#endif - - /* Hand off to main application code */ - status = SDL_main (gArgc, gArgv); - - /* We're done, thank you for playing */ - exit(status); -} -@end - - -@implementation NSString (ReplaceSubString) - -- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString -{ - unsigned int bufferSize; - unsigned int selfLen = [self length]; - unsigned int aStringLen = [aString length]; - unichar *buffer; - NSRange localRange; - NSString *result; - - bufferSize = selfLen + aStringLen - aRange.length; - buffer = NSAllocateMemoryPages(bufferSize*sizeof(unichar)); - - /* Get first part into buffer */ - localRange.location = 0; - localRange.length = aRange.location; - [self getCharacters:buffer range:localRange]; - - /* Get middle part into buffer */ - localRange.location = 0; - localRange.length = aStringLen; - [aString getCharacters:(buffer+aRange.location) range:localRange]; - - /* Get last part into buffer */ - localRange.location = aRange.location + aRange.length; - localRange.length = selfLen - localRange.location; - [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; - - /* Build output string */ - result = [NSString stringWithCharacters:buffer length:bufferSize]; - - NSDeallocateMemoryPages(buffer, bufferSize); - - return result; -} - -@end - - - -#ifdef main -# undef main -#endif - - -/* Main entry point to executable - should *not* be SDL_main! */ -int main (int argc, char **argv) -{ - - /* Copy the arguments into a global variable */ - int i; - - /* This is passed if we are launched by double-clicking */ - if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { - gArgc = 1; - gFinderLaunch = YES; - } else { - gArgc = argc; - gFinderLaunch = NO; - } - gArgv = (char**) malloc (sizeof(*gArgv) * (gArgc+1)); - assert (gArgv != NULL); - for (i = 0; i < gArgc; i++) - gArgv[i] = argv[i]; - gArgv[i] = NULL; - -#if SDL_USE_NIB_FILE - [SDLApplication poseAsClass:[NSApplication class]]; - NSApplicationMain (); -#else - CustomApplicationMain (); -#endif - return 0; -} - - diff --git a/rts/System/Platform/Threading.cpp b/rts/System/Platform/Threading.cpp index 738d3179bb2..45e0d8d8a93 100644 --- a/rts/System/Platform/Threading.cpp +++ b/rts/System/Platform/Threading.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) #elif defined(_WIN32) #include diff --git a/rts/System/Platform/Threading.h b/rts/System/Platform/Threading.h index e2d94292b59..277f49f7643 100644 --- a/rts/System/Platform/Threading.h +++ b/rts/System/Platform/Threading.h @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -55,6 +56,19 @@ namespace Threading { NativeThreadHandle GetCurrentThread(); NativeThreadId GetCurrentThreadId(); + // Convert a NativeThreadId to a 32-bit value for logging. NativeThreadId is a + // pointer (pthread_t) on macOS/BSD and an integer elsewhere; the template lets + // `if constexpr` select a cast that is valid for the actual type on each target + // (it would not be discarded in a non-template function). + template + inline std::uint32_t ThreadIdAsU32(TId id) { + if constexpr (std::is_pointer_v) + return static_cast(reinterpret_cast(id)); + else + return static_cast(id); + } + inline std::uint32_t GetCurrentThreadIdAsU32() { return ThreadIdAsU32(GetCurrentThreadId()); } + #ifndef _WIN32 extern thread_local std::shared_ptr localThreadControls; #endif diff --git a/rts/System/Platform/Win/WinVersion.cpp b/rts/System/Platform/Win/WinVersion.cpp index d6ba5cf40dd..c90dd16ae54 100644 --- a/rts/System/Platform/Win/WinVersion.cpp +++ b/rts/System/Platform/Win/WinVersion.cpp @@ -1,5 +1,6 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ +#include #include #include #include @@ -286,15 +287,13 @@ std::string windows::GetDisplayString(bool getName, bool getVersion, bool getExt oss << " (build " << osvi.dwBuildNumber; if (osvi.szCSDVersion[0] != 0) { - static_assert(sizeof(wchar_t) >= sizeof(osvi.szCSDVersion[0]), ""); + const int utf8Len = WideCharToMultiByte(CP_UTF8, 0, osvi.szCSDVersion, -1, nullptr, 0, nullptr, nullptr); - // Windows uses UTF16 - std::wstring_convert, wchar_t> ws2s; - - const std::wstring wstr(osvi.szCSDVersion); - const std::string nstr(ws2s.to_bytes(wstr)); - - oss << ", " << nstr; + if (utf8Len > 1) { + std::string nstr(utf8Len - 1, '\0'); + WideCharToMultiByte(CP_UTF8, 0, osvi.szCSDVersion, -1, nstr.data(), utf8Len, nullptr, nullptr); + oss << ", " << nstr; + } } oss << ")"; @@ -309,14 +308,25 @@ std::string windows::GetHardwareString() { std::ostringstream oss; - unsigned char regbuf[200]; - DWORD regLength = sizeof(regbuf); + std::array regbuf{}; + DWORD regLength = static_cast(regbuf.size() * sizeof(wchar_t)); // RegQueryValueExW wants bytes DWORD regType = REG_SZ; HKEY regkey; - if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Hardware\\Description\\System\\CentralProcessor\\0", 0, KEY_READ, ®key) == ERROR_SUCCESS) { - if (RegQueryValueEx(regkey, L"ProcessorNameString", 0, ®Type, regbuf, ®Length) == ERROR_SUCCESS) { - oss << regbuf << "; "; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Hardware\\Description\\System\\CentralProcessor\\0", 0, KEY_READ, ®key) == ERROR_SUCCESS) { + if (RegQueryValueExW(regkey, L"ProcessorNameString", 0, ®Type, reinterpret_cast(regbuf.data()), ®Length) == ERROR_SUCCESS) { + // REG_SZ values aren't guaranteed to be null-terminated; force it + regbuf.back() = L'\0'; + + const int utf8Len = WideCharToMultiByte(CP_UTF8, 0, regbuf.data(), -1, nullptr, 0, nullptr, nullptr); + + if (utf8Len > 1) { + std::string utf8str(utf8Len - 1, '\0'); + WideCharToMultiByte(CP_UTF8, 0, regbuf.data(), -1, utf8str.data(), utf8Len, nullptr, nullptr); + oss << utf8str << "; "; + } else { + oss << "cannot convert processor name to UTF-8; "; + } } else { oss << "cannot read processor data; "; } diff --git a/rts/System/Platform/Win/win32.h b/rts/System/Platform/Win/win32.h index 81bf95fdb31..57c61feb791 100644 --- a/rts/System/Platform/Win/win32.h +++ b/rts/System/Platform/Win/win32.h @@ -31,6 +31,7 @@ #undef DeleteFile #undef SendMessage #undef GetCharWidth + #undef MemoryBarrier #undef far #undef near #undef FAR diff --git a/rts/System/RangesCompat.h b/rts/System/RangesCompat.h new file mode 100644 index 00000000000..88eaa9a4ca4 --- /dev/null +++ b/rts/System/RangesCompat.h @@ -0,0 +1,48 @@ +/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ + +#ifndef RANGES_COMPAT_H +#define RANGES_COMPAT_H + +#include +#include +#include +#include + +namespace spring::views { + +#ifdef __cpp_lib_ranges_enumerate + +using std::views::enumerate; + +#else + +template auto enumerate(Rng& rng) +{ + struct Iterator { + decltype(std::begin(rng)) it; + std::ptrdiff_t idx; + + // the element half stays a reference, copying it would turn a + // mutating loop into a no-op + auto operator * () const { return std::pair(idx, *it); } + Iterator& operator ++ () { ++it; ++idx; return *this; } + bool operator != (const Iterator& o) const { return it != o.it; } + }; + + struct View { + Rng& rng; + Iterator begin() const { return {std::begin(rng), 0}; } + Iterator end () const { return {std::end (rng), 0}; } + }; + + return View{rng}; +} + +// the view holds a reference, so a temporary would dangle +template void enumerate(Rng&&) = delete; + +#endif + +} + +#endif // RANGES_COMPAT_H diff --git a/rts/System/SafeUtil.h b/rts/System/SafeUtil.h index ce19b3f9738..9fbf946cc82 100644 --- a/rts/System/SafeUtil.h +++ b/rts/System/SafeUtil.h @@ -5,6 +5,8 @@ #include #include +#include +#include namespace spring { template inline void SafeDestruct(T*& p) @@ -112,8 +114,8 @@ namespace spring { static_assert(sizeof(TIn) == sizeof(TOut), "Types must match sizes"); static_assert(std::is_trivially_copyable::value , "Requires TriviallyCopyable input"); static_assert(std::is_trivially_copyable::value, "Requires TriviallyCopyable output"); - static_assert(std::is_trivially_constructible_v, - "This implementation additionally requires destination type to be trivially constructible"); + static_assert(std::is_trivially_default_constructible::value, + "This implementation additionally requires destination type to be trivially default-constructible"); TOut t2; std::memcpy(std::addressof(t2), std::addressof(t1), sizeof(TIn)); diff --git a/rts/System/SpringApp.cpp b/rts/System/SpringApp.cpp index e5a011b19c3..fd0a928910b 100644 --- a/rts/System/SpringApp.cpp +++ b/rts/System/SpringApp.cpp @@ -13,7 +13,9 @@ #undef KeyRelease #else #include // isatty +#ifndef __APPLE__ #include // XInitThreads +#endif #undef KeyPress #undef KeyRelease @@ -41,6 +43,7 @@ #include "Game/UI/InfoConsole.h" #include "Game/UI/MouseHandler.h" #include "Lua/LuaOpenGL.h" +#include "Lua/LuaDebugExtra.h" #include "Lua/LuaVFSDownload.h" #include "Menu/LuaMenuController.h" #include "Menu/SelectMenu.h" @@ -97,6 +100,7 @@ #include "System/Sound/ISound.h" #include "System/Sync/FPUCheck.h" #include "System/Threading/ThreadPool.h" +#include "System/LoadSave/DemoFileExtension.h" #include "Game/UnsyncedGameCommands.h" #include "Game/SyncedGameCommands.h" @@ -683,6 +687,14 @@ void SpringApp::LoadSpringMenu() } } +static bool IsReplay(const std::string& path) +{ + if (IsDemoExtension(FileSystem::GetExtensionLowerCase(path))) + return true; + + return ContentsLookLikeAReplay(path); +} + /** * Initializes instance of GameSetup */ @@ -729,7 +741,7 @@ void SpringApp::Startup() pregame = new CPreGame(clientSetup); return; } - if (extension == "sdfz") { + if (IsReplay(inputFile)) { LoadDemoFile(inputFile); return; } @@ -1176,6 +1188,10 @@ bool SpringApp::MainEventHandler(const SDL_Event& event) //FIXME check if still happens with SDL2 (2013) SDL_SetModState((SDL_Keymod)(SDL_GetModState() & (KMOD_NUM | KMOD_CAPS | KMOD_MODE))); + // drop emulated input first: it fires its own releases directly, + // since the pushed SDL releases below get eaten by the emulation gate + LuaDebugExtra::ClearEmulatedInput(); + // release all keyboard keys KeyInput::ReleaseAllKeys(); @@ -1242,7 +1258,12 @@ bool SpringApp::MainEventHandler(const SDL_Event& event) if (activeController != nullptr) { int keyCode = CKeyCodes::GetNormalizedSymbol(event.key.keysym.sym); int scanCode = CScanCodes::GetNormalizedSymbol(event.key.keysym.scancode); - activeController->KeyPressed(keyCode, scanCode, event.key.repeat); + + // if the key is already held via input emulation the effective + // state is already down, so the real press is not a new edge + // (the emulated store is keyed by raw SDL2 keycode, like keyVec) + if (!KeyInput::IsKeyEmulated(event.key.keysym.sym)) + activeController->KeyPressed(keyCode, scanCode, event.key.repeat); } } break; @@ -1253,7 +1274,10 @@ bool SpringApp::MainEventHandler(const SDL_Event& event) gameTextInput.ignoreNextChar = false; int keyCode = CKeyCodes::GetNormalizedSymbol(event.key.keysym.sym); int scanCode = CScanCodes::GetNormalizedSymbol(event.key.keysym.scancode); - activeController->KeyReleased(keyCode, scanCode); + + // emulation still holds it down, so the real release is not an edge + if (!KeyInput::IsKeyEmulated(event.key.keysym.sym)) + activeController->KeyReleased(keyCode, scanCode); } } break; case SDL_KEYMAPCHANGED: { diff --git a/rts/System/SpringHashMap.hpp b/rts/System/SpringHashMap.hpp index 1bf487a6111..f6a359cb14d 100644 --- a/rts/System/SpringHashMap.hpp +++ b/rts/System/SpringHashMap.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include #include #include diff --git a/rts/System/SpringHashSet.hpp b/rts/System/SpringHashSet.hpp index 3375e12520c..366a5d35211 100644 --- a/rts/System/SpringHashSet.hpp +++ b/rts/System/SpringHashSet.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include #include // malloc #include diff --git a/rts/System/SpringMath.h b/rts/System/SpringMath.h index 2d3af6ffe9b..f49871c778c 100644 --- a/rts/System/SpringMath.h +++ b/rts/System/SpringMath.h @@ -175,6 +175,26 @@ template constexpr T mixRotation(T v1, T v2, T2 a) { template constexpr T Blend(const T v1, const T v2, const float a) { return mix(v1, v2, a); } +// Catmull-Rom cubic interpolation through p1..p2, with p0/p3 the outer neighbours, t in [0,1]. +// C1-continuous (matching gradients across segment borders), unlike linear mix. +constexpr float CatmullRom(float p0, float p1, float p2, float p3, float t) +{ + return p1 + 0.5f * t * ((p2 - p0) + t * ((2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) + t * (3.0f * (p1 - p2) + p3 - p0))); +} + +// Bicubic Catmull-Rom over a 4x4 patch of samples p[row][col]; dx/dy are the +// fractional offsets in [0,1] from the inner sample p[1][1] toward p[2][2]. +inline float InterpolateBicubic(const float p[4][4], float dx, float dy) +{ + const float cols[4] = { + CatmullRom(p[0][0], p[0][1], p[0][2], p[0][3], dx), + CatmullRom(p[1][0], p[1][1], p[1][2], p[1][3], dx), + CatmullRom(p[2][0], p[2][1], p[2][2], p[2][3], dx), + CatmullRom(p[3][0], p[3][1], p[3][2], p[3][3], dx), + }; + return CatmullRom(cols[0], cols[1], cols[2], cols[3], dy); +} + int Round(const float f) _const _warn_unused_result; template constexpr T Square(const T x) { return x*x; } diff --git a/rts/System/StringHash.h b/rts/System/StringHash.h index 2a7fede1a22..98218fefd1a 100644 --- a/rts/System/StringHash.h +++ b/rts/System/StringHash.h @@ -36,4 +36,9 @@ struct compileTimeHasher { } }; -#define COMPILE_TIME_HASH(str) compileTimeHasher::hash(str) +template +[[nodiscard]] constexpr uint32_t CompileTimeHash(const char (&literal)[N]) noexcept +{ + static_assert(N > 1); + return compileTimeHasher::hash(literal); +} \ No newline at end of file diff --git a/rts/System/StringUtil.h b/rts/System/StringUtil.h index ea7ca4b62c6..73d2aaeef26 100644 --- a/rts/System/StringUtil.h +++ b/rts/System/StringUtil.h @@ -30,26 +30,6 @@ struct _UTIL_CONCAT(doOnce, __LINE__) { _UTIL_CONCAT(doOnce, __LINE__)() { code; } }; static _UTIL_CONCAT(doOnce, __LINE__) _UTIL_CONCAT(doOnceVar, __LINE__); -static char lcstr[32768]; -static char lcsub[32768]; -static inline const char* StrCaseStr(const char* str, const char* sub) { - const char* pos = nullptr; - - if (str == nullptr) - return nullptr; - if (sub == nullptr) - return nullptr; - - std::strncpy(lcstr, str, sizeof(lcstr) - 1); - std::strncpy(lcsub, sub, sizeof(lcsub) - 1); - std::transform(lcstr, lcstr + sizeof(lcstr), lcstr, (int (*)(int)) tolower); - std::transform(lcsub, lcsub + sizeof(lcsub), lcsub, (int (*)(int)) tolower); - - if ((pos = std::strstr(lcstr, lcsub)) == nullptr) - return nullptr; - - return (str + (pos - lcstr)); -} static inline void StringToLower(const char* in, char* out, size_t len) { diff --git a/rts/System/Sync/DumpHistory.cpp b/rts/System/Sync/DumpHistory.cpp index 8280888df87..724352a6855 100644 --- a/rts/System/Sync/DumpHistory.cpp +++ b/rts/System/Sync/DumpHistory.cpp @@ -1,5 +1,7 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ +#include + #include "DumpHistory.h" #include "SyncChecker.h" diff --git a/rts/System/Sync/DumpState.cpp b/rts/System/Sync/DumpState.cpp index 86ad1ea2788..bb7806973d2 100644 --- a/rts/System/Sync/DumpState.cpp +++ b/rts/System/Sync/DumpState.cpp @@ -1,5 +1,6 @@ /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ +#include "System/RangesCompat.h" #include #include #include @@ -687,14 +688,16 @@ void DumpState(int newMinFrameNum, int newMaxFrameNum, int newFramePeriod, std:: const CTeam* t = teamHandler.Team(a); file << "\t\tteamID: " << t->teamNum << " (controller: " << t->GetControllerName() << ")\n"; - file << "\t\t\tmetal: " << TapFloats(t->res.metal); - file << "\t\t\tenergy: " << TapFloats(t->res.energy); - file << "\t\t\tmetalPull: " << TapFloats(t->resPull.metal); - file << "\t\t\tenergyPull: " << TapFloats(t->resPull.energy); - file << "\t\t\tmetalIncome: " << TapFloats(t->resIncome.metal); - file << "\t\t\tenergyIncome: " << TapFloats(t->resIncome.energy); - file << "\t\t\tmetalExpense: " << TapFloats(t->resExpense.metal); - file << "\t\t\tenergyExpense: " << TapFloats(t->resExpense.energy); + for (const auto &[resourceID, value] : spring::views::enumerate(t->res)) + file << "\t\t\tstored[" << resourceID << "]: " << TapFloats(value); + for (const auto &[resourceID, value] : spring::views::enumerate(t->resStorage)) + file << "\t\t\tmaxStorage[" << resourceID << "]: " << TapFloats(value); + for (const auto &[resourceID, value] : spring::views::enumerate(t->resPull)) + file << "\t\t\tpull[" << resourceID << "]: " << TapFloats(value); + for (const auto &[resourceID, value] : spring::views::enumerate(t->resIncome)) + file << "\t\t\tincome[" << resourceID << "]: " << TapFloats(value); + for (const auto &[resourceID, value] : spring::views::enumerate(t->resExpense)) + file << "\t\t\texpense[" << resourceID << "]: " << TapFloats(value); } #endif diff --git a/rts/System/Sync/SyncChecker.cpp b/rts/System/Sync/SyncChecker.cpp index 4063faff100..edb0cb02e4b 100644 --- a/rts/System/Sync/SyncChecker.cpp +++ b/rts/System/Sync/SyncChecker.cpp @@ -11,6 +11,7 @@ unsigned CSyncChecker::g_checksum; +unsigned CSyncChecker::g_prevChecksum; int CSyncChecker::inSyncedCode; void CSyncChecker::NewFrame() diff --git a/rts/System/Sync/SyncChecker.h b/rts/System/Sync/SyncChecker.h index 35aa1be6d7a..3fb996c262a 100644 --- a/rts/System/Sync/SyncChecker.h +++ b/rts/System/Sync/SyncChecker.h @@ -31,6 +31,8 @@ class CSyncChecker { * Keeps a running checksum over all assignments to synced variables. */ static unsigned GetChecksum() { return g_checksum; } + static unsigned GetPrevChecksum() { return g_prevChecksum; } + static void SetPrevChecksum(unsigned v) { g_prevChecksum = v; } static void NewFrame(); static void debugSyncCheckThreading(); static void Sync(uint32_t val); @@ -48,6 +50,11 @@ class CSyncChecker { */ static unsigned g_checksum; + /** + * Final checksum of the previous simulation frame. + */ + static unsigned g_prevChecksum; + /** * @brief in synced code * diff --git a/rts/System/Threading/ThreadPool.h b/rts/System/Threading/ThreadPool.h index ee5251511f0..48fdc10acce 100644 --- a/rts/System/Threading/ThreadPool.h +++ b/rts/System/Threading/ThreadPool.h @@ -224,7 +224,10 @@ class ITaskGroup } uint32_t GetId() const { return id; } - uint64_t GetDeltaTime(const spring_time t) const { return (std::max(ts.load(), uint64_t(t.toNanoSecsi())) - ts); } + uint64_t GetDeltaTime(const spring_time t0) const { + const uint64_t t1 = ts.load(); + return (std::max(t1, uint64_t(t0.toNanoSecsi())) - t1); + } void UpdateId() { id = lastId.fetch_add(1); } void SetTimeStamp(const spring_time t) { ts = t.toNanoSecsi(); } diff --git a/rts/System/creg/SerializeLuaState.cpp b/rts/System/creg/SerializeLuaState.cpp index f8a767be377..65b7d7c3e98 100644 --- a/rts/System/creg/SerializeLuaState.cpp +++ b/rts/System/creg/SerializeLuaState.cpp @@ -1042,7 +1042,7 @@ void creg_lua_State::PostLoad() } size_t savedpc_offset = * (size_t *) &savedpc; - savedpc = GetProtoFromCallInfo(ci)->code + savedpc_offset; + savedpc = GetProtoFromCallInfo(ci - 1)->code + savedpc_offset; } diff --git a/rts/System/creg/Serializer.cpp b/rts/System/creg/Serializer.cpp index 288ceacf663..2822194249b 100644 --- a/rts/System/creg/Serializer.cpp +++ b/rts/System/creg/Serializer.cpp @@ -13,6 +13,7 @@ #include "System/Log/ILog.h" #include "System/Platform/byteorder.h" #include "System/Exceptions.h" +#include "System/StringUtil.h" #include #include @@ -415,11 +416,9 @@ CInputStreamSerializer::CInputStreamSerializer() CInputStreamSerializer::~CInputStreamSerializer() { - for (StoredObject& o: objects) { - if (o.obj) { - classRefs[o.classRef]->DeleteInstance(o.obj); - } - } + // the entries are live or half-initialized objects, deleting either crashes + if (!objects.empty()) + LOG_L(L_WARNING, "[creg::%s] load failed, leaking %d partially loaded objects", __func__, int(objects.size())); } bool CInputStreamSerializer::IsWriting() @@ -476,6 +475,8 @@ void CInputStreamSerializer::SerializeObjectPtr(void** ptr, creg::Class* cls) { unsigned int id; ReadVarSizeUInt(stream, &id); + if (id >= objects.size()) + throw content_error("Save corrupted: object reference " + IntToString(id) + " out of range (" + IntToString(objects.size()) + " objects)"); if (id) { StoredObject& o = objects [id]; if (o.obj) *ptr = o.obj; @@ -501,7 +502,13 @@ void CInputStreamSerializer::SerializeObjectInstance(void* inst, creg::Class* cl if (id == 0) return; // this is old save game and it has not this object - skip it + if (id >= objects.size()) + throw content_error("Save corrupted: instance reference " + IntToString(id) + " out of range (" + IntToString(objects.size()) + " objects)"); + StoredObject& o = objects[id]; + creg::Class* fileCls = classRefs[o.classRef]; + if (fileCls != cls) + throw content_error(std::string("Save incompatible: file contains ") + fileCls->name + " where " + cls->name + " was expected (the save was made with different settings)"); assert(!o.obj); assert(o.isEmbedded); @@ -637,6 +644,8 @@ void CInputStreamSerializer::LoadPackage(std::istream* s, void*& root, creg::Cla creg::Class* cls = classRefs[object.classRef]; SerializeObject(cls, object.obj); + if (stream->fail()) + throw content_error(std::string("Save corrupted: stream exhausted while reading ") + cls->name); LOG_SL(LOG_SECTION_CREG_SERIALIZER, L_DEBUG, "Deserialized %s size:%i", cls->name, cls->size); } diff --git a/rts/System/float3.cpp b/rts/System/float3.cpp index 370f53e32a8..6189708d086 100644 --- a/rts/System/float3.cpp +++ b/rts/System/float3.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "System/creg/creg_cond.h" #include "System/SpringMath.h" @@ -15,6 +16,11 @@ CR_REG_METADATA(float3, (CR_MEMBER(x), CR_MEMBER(y), CR_MEMBER(z))) float float3::maxxpos = -1.0f; float float3::maxzpos = -1.0f; +std::string float3::str() const +{ + return std::format("float3({:.3f}, {:.3f}, {:.3f})", x, y, z); +} + float3 float3::PickNonParallel() const { // https://math.stackexchange.com/questions/3122010/how-to-deterministically-pick-a-vector-that-is-guaranteed-to-be-non-parallel-to diff --git a/rts/System/float3.h b/rts/System/float3.h index f096c112c0d..e827f2c0b73 100644 --- a/rts/System/float3.h +++ b/rts/System/float3.h @@ -6,10 +6,9 @@ #include #include #include -#include +#include #include "System/BranchPrediction.h" -#include "lib/streflop/streflop_cond.h" #include "System/creg/creg_cond.h" #include "System/FastMath.h" #include "System/type2.h" @@ -839,9 +838,7 @@ class float3 static constexpr float cmp_eps() { return 1e-04f; } static constexpr float nrm_eps() { return 1e-12f; } - std::string str() const { - return std::format("float3({:.3f}, {:.3f}, {:.3f})", x, y, z); - } + std::string str() const; /** * @brief max x pos diff --git a/rts/System/float4.cpp b/rts/System/float4.cpp index 8fcdfb597b6..d6bf6971115 100644 --- a/rts/System/float4.cpp +++ b/rts/System/float4.cpp @@ -4,9 +4,16 @@ #include "System/creg/creg_cond.h" #include "System/SpringMath.h" +#include + CR_BIND(float4, ) CR_REG_METADATA(float4, (CR_MEMBER(x), CR_MEMBER(y), CR_MEMBER(z), CR_MEMBER(w))) +std::string float4::str() const +{ + return std::format("float4({:.3f}, {:.3f}, {:.3f}, {:.3f})", x, y, z, w); +} + // ensure that we use a continuous block of memory // (required for passing to gl functions) static_assert(sizeof(float4) == 4 * sizeof(float), ""); diff --git a/rts/System/float4.h b/rts/System/float4.h index 347f0d2e822..7381701184b 100644 --- a/rts/System/float4.h +++ b/rts/System/float4.h @@ -108,9 +108,7 @@ struct float4 : public float3 return (x * f.x) + (y * f.y) + (z * f.z) + (w * f.w); } - std::string str() const { - return std::format("float4({:.3f}, {:.3f}, {:.3f}, {:.3f})", x, y, z, w); - } + std::string str() const; /// Allows implicit conversion to float* (for passing to gl functions) diff --git a/rts/System/recoil-smmalloc.h b/rts/System/recoil-smmalloc.h new file mode 100644 index 00000000000..4ee7b9cb246 --- /dev/null +++ b/rts/System/recoil-smmalloc.h @@ -0,0 +1,15 @@ +/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */ + +#pragma once + +// smmalloc.h leaks `#define INLINE inline` -- a very generic token that +// collides with unrelated code (e.g. simdjson's layout_mode::INLINE +// enumerator) whenever both end up in the same translation unit. Include +// smmalloc only through this wrapper so the macro is dropped immediately and +// never escapes into engine code. + +#include "smmalloc/smmalloc.h" + +#ifdef INLINE +#undef INLINE +#endif diff --git a/rts/System/simd_compat.h b/rts/System/simd_compat.h index a405e02f587..c68b59e3484 100644 --- a/rts/System/simd_compat.h +++ b/rts/System/simd_compat.h @@ -3,6 +3,19 @@ #ifdef SSE2NEON #include "lib/sse2neon/sse2neon.h" + // sse2neon leaks 's FE_XXX macros, which collide with the ones streflop + // redefines and trigger a #warning. Undef them here so streflop gets a clean slate. + #undef FE_INVALID + #undef FE_DENORMAL + #undef FE_DIVBYZERO + #undef FE_OVERFLOW + #undef FE_UNDERFLOW + #undef FE_INEXACT + #undef FE_ALL_EXCEPT + #undef FE_TONEAREST + #undef FE_DOWNWARD + #undef FE_UPWARD + #undef FE_TOWARDZERO #else #ifdef _MSC_VER #include // MSVC umbrella diff --git a/rts/build/cmake/FindSevenZip.cmake b/rts/build/cmake/FindSevenZip.cmake index 1077ec7b17e..0e3e4002a67 100644 --- a/rts/build/cmake/FindSevenZip.cmake +++ b/rts/build/cmake/FindSevenZip.cmake @@ -23,7 +23,7 @@ ENDIF (SEVENZIP_BIN) set(progfilesx86 "ProgramFiles(x86)") find_program(SEVENZIP_BIN - NAMES 7z 7za + NAMES 7z 7za 7zz HINTS "${MINGWDIR}" "${MINGWLIBS}/bin" "$ENV{${progfilesx86}}/7-zip" "$ENV{ProgramFiles}/7-zip" "$ENV{ProgramW6432}/7-zip" PATH_SUFFIXES bin DOC "7zip executable" diff --git a/rts/build/cmake/UtilJava.cmake b/rts/build/cmake/UtilJava.cmake deleted file mode 100644 index 71874cc4966..00000000000 --- a/rts/build/cmake/UtilJava.cmake +++ /dev/null @@ -1,101 +0,0 @@ -# This file is part of the Spring engine (GPL v2 or later), see LICENSE.html - -# -# Spring Java related CMake utilities -# ----------------------------------- -# -# Variables set in this file: -# * JAVA_GLOBAL_LIBS_DIRS -# -# Functions and macros defined in this file: -# * find_java_lib -# * get_first_sub_dir_name -# * create_classpath -# * concat_classpaths -# * find_manifest_file -# - - -if (CMAKE_HOST_WIN32) - set(JAVA_GLOBAL_LIBS_DIRS "${MINGWLIBS}") -else (CMAKE_HOST_WIN32) - set(JAVA_GLOBAL_LIBS_DIRS "/usr/share/java" "/usr/local/share/java") -endif (CMAKE_HOST_WIN32) -make_global(JAVA_GLOBAL_LIBS_DIRS) - - -# Looks for a Java library (Jar file) in system wide search paths and in -# additional dirs supplied as argument. -macro (find_java_lib path_var libName additionalSearchDirs) - find_file(${path_var} "${libName}.jar" - PATHS ${additionalSearchDirs} ${JAVA_GLOBAL_LIBS_DIRS} - DOC "Path to the Java library ${libName}.jar" - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH - ) - if (NOT ${path_var}) - message(SEND_ERROR "${libName}.jar not found!") - else (NOT ${path_var}) - ai_message(STATUS "Found ${libName}.jar: ${${path_var}}") - endif (NOT ${path_var}) -endmacro (find_java_lib path_var libName additionalSearchDirs) - - -# Returns the name of the first sub-dir (in alphabetical descending order) -# under dir. -macro (get_first_sub_dir_name name_var dir) - file(GLOB dirContent RELATIVE "${dir}" "${dir}/*") - foreach (dirPart ${dirContent}) - if (IS_DIRECTORY "${dir}/${dirPart}") - set(${name_var} ${dirPart}) - break() - endif (IS_DIRECTORY "${dir}/${dirPart}") - endforeach (dirPart) -endmacro (get_first_sub_dir_name name_var dir) - - -# Recursively lists all JAR files in a given directory -# and concatenates them in a Java Classpath compatible way into a single string. -macro (create_classpath classPath_var dir) - file(GLOB_RECURSE ${classPath_var} FOLLOW_SYMLINKS "${dir}/*.jar") - # Make sure we use the correct path delimiter for the compiling system - string(REPLACE ";" "${PATH_DELIM_H}" ${classPath_var} "${${classPath_var}}") -endmacro (create_classpath classPath_var dir) - - -# Concatenates an arbitrary number of Java ClassPaths (may be empty). -function (concat_classpaths resultingCP_var) - set(${resultingCP_var} "") - foreach (cpPart ${ARGN}) - set(${resultingCP_var} "${${resultingCP_var}}${cpPart}${PATH_DELIM_H}") - endforeach (cpPart) - string(REGEX REPLACE "${PATH_DELIM_H}\$" "" ${resultingCP_var} "${${resultingCP_var}}") - set(${resultingCP_var} "${${resultingCP_var}}" PARENT_SCOPE) -endfunction (concat_classpaths) - -# Look for a manifest.mf file in a few specific sub-dirs. -# This could be done with a simple find_file call, -# but that strangely does not find the file under win32, -# so we use this workaround -function (find_manifest_file srcDir result_var) - set(manifestSubdirs - "/src/main/resources/META-INF/" - "/src/" - "/") - set(${result_var} "${result_var}-NOTFOUND") - if (CMAKE_HOST_WIN32) - foreach(subDir_var ${manifestSubdirs}) - if (EXISTS "${srcDir}${subDir_var}manifest.mf") - set(${result_var} "${srcDir}${subDir_var}manifest.mf") - break() - endif (EXISTS "${srcDir}${subDir_var}manifest.mf") - endforeach(subDir_var) - else (CMAKE_HOST_WIN32) - find_file(${result_var} - NAMES "manifest.mf" "MANIFEST.MF" - PATHS "${srcDir}" - PATH_SUFFIXES ${manifestSubdirs} - NO_DEFAULT_PATH) - endif (CMAKE_HOST_WIN32) - set(${result_var} ${${result_var}} PARENT_SCOPE) -endfunction (find_manifest_file) diff --git a/rts/builds/dedicated/CMakeLists.txt b/rts/builds/dedicated/CMakeLists.txt index f425d68bd4f..51548bac7ca 100644 --- a/rts/builds/dedicated/CMakeLists.txt +++ b/rts/builds/dedicated/CMakeLists.txt @@ -102,6 +102,7 @@ set(system_files ${ENGINE_SRC_ROOT_DIR}/System/Config/ConfigSource.cpp ${ENGINE_SRC_ROOT_DIR}/System/Config/ConfigVariable.cpp ${ENGINE_SRC_ROOT_DIR}/System/LoadSave/Demo.cpp + ${ENGINE_SRC_ROOT_DIR}/System/LoadSave/DemoFileExtension.cpp ${ENGINE_SRC_ROOT_DIR}/System/LoadSave/DemoReader.cpp ${ENGINE_SRC_ROOT_DIR}/System/LoadSave/DemoRecorder.cpp ${ENGINE_SRC_ROOT_DIR}/System/Log/Backend.cpp diff --git a/rts/builds/legacy/CMakeLists.txt b/rts/builds/legacy/CMakeLists.txt index cbcc906f83d..f1d19ede7e3 100644 --- a/rts/builds/legacy/CMakeLists.txt +++ b/rts/builds/legacy/CMakeLists.txt @@ -49,7 +49,7 @@ find_freetype_hack() # hack to find different named freetype.dll find_package_static(Freetype 2.8.1 REQUIRED) list(APPEND engineLibraries Freetype::Freetype) -if (UNIX) +if (UNIX AND NOT APPLE) find_package(X11 REQUIRED) target_link_libraries(Game PRIVATE X11::Xcursor) list(APPEND engineLibraries ${X11_Xcursor_LIB} ${X11_X11_LIB}) diff --git a/rts/lib/assimp/include/assimp/matrix3x3.inl b/rts/lib/assimp/include/assimp/matrix3x3.inl index d90c2ad2eb6..c894ce07058 100644 --- a/rts/lib/assimp/include/assimp/matrix3x3.inl +++ b/rts/lib/assimp/include/assimp/matrix3x3.inl @@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_MATRIX3X3_INL_INC #ifdef __cplusplus +#include #include "matrix3x3.h" #include "matrix4x4.h" diff --git a/rts/lib/assimp/include/assimp/matrix4x4.inl b/rts/lib/assimp/include/assimp/matrix4x4.inl index 42851f48bce..d56691b86d1 100644 --- a/rts/lib/assimp/include/assimp/matrix4x4.inl +++ b/rts/lib/assimp/include/assimp/matrix4x4.inl @@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifdef __cplusplus +#include #include "matrix4x4.h" #include "matrix3x3.h" #include "quaternion.h" diff --git a/rts/lib/assimp/include/assimp/quaternion.inl b/rts/lib/assimp/include/assimp/quaternion.inl index b2bacacb35a..017e9599131 100644 --- a/rts/lib/assimp/include/assimp/quaternion.inl +++ b/rts/lib/assimp/include/assimp/quaternion.inl @@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_QUATERNION_INL_INC #ifdef __cplusplus +#include #include "quaternion.h" #include "lib/streflop/streflop_cond.h" diff --git a/rts/lib/assimp/include/assimp/vector2.inl b/rts/lib/assimp/include/assimp/vector2.inl index e2750e42784..06f2b8d2d2c 100644 --- a/rts/lib/assimp/include/assimp/vector2.inl +++ b/rts/lib/assimp/include/assimp/vector2.inl @@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_VECTOR2D_INL_INC #ifdef __cplusplus +#include #include "vector2.h" #include "lib/streflop/streflop_cond.h" diff --git a/rts/lib/assimp/include/assimp/vector3.inl b/rts/lib/assimp/include/assimp/vector3.inl index b33b60c5b08..4790cf95c23 100644 --- a/rts/lib/assimp/include/assimp/vector3.inl +++ b/rts/lib/assimp/include/assimp/vector3.inl @@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_VECTOR3D_INL_INC #ifdef __cplusplus +#include #include "vector3.h" #include "lib/streflop/streflop_cond.h" diff --git a/rts/lib/glad/CMakeLists.txt b/rts/lib/glad/CMakeLists.txt index a67e710dd36..7f7391dd7d3 100644 --- a/rts/lib/glad/CMakeLists.txt +++ b/rts/lib/glad/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.5) project(Glad) -if (UNIX AND NOT MINGW) +if (UNIX AND NOT MINGW AND NOT APPLE) add_library(glad glad.c glad_glx.c) -else (UNIX AND NOT MINGW) +else (UNIX AND NOT MINGW AND NOT APPLE) add_library(glad glad.c) -endif (UNIX AND NOT MINGW) +endif (UNIX AND NOT MINGW AND NOT APPLE) target_include_directories(glad PUBLIC /) \ No newline at end of file diff --git a/rts/lib/headlessStubs/gladstub.cpp b/rts/lib/headlessStubs/gladstub.cpp index f34a055a716..045f6a4522c 100644 --- a/rts/lib/headlessStubs/gladstub.cpp +++ b/rts/lib/headlessStubs/gladstub.cpp @@ -357,6 +357,7 @@ decltype(glad_glTexCoord2fv) glad_glTexCoord2fv = nullptr; decltype(glad_glTexCoord2i) glad_glTexCoord2i = nullptr; decltype(glad_glTexCoord3f) glad_glTexCoord3f = nullptr; decltype(glad_glTexCoord4f) glad_glTexCoord4f = nullptr; +decltype(glad_glTexCoord4fv) glad_glTexCoord4fv = nullptr; decltype(glad_glTexCoordPointer) glad_glTexCoordPointer = nullptr; decltype(glad_glTexEnvf) glad_glTexEnvf = nullptr; decltype(glad_glTexEnvfv) glad_glTexEnvfv = nullptr; @@ -796,6 +797,7 @@ int gladLoadGL(void) { glad_glTexCoord2i = MakeStubImpl(glad_glTexCoord2i); glad_glTexCoord3f = MakeStubImpl(glad_glTexCoord3f); glad_glTexCoord4f = MakeStubImpl(glad_glTexCoord4f); + glad_glTexCoord4fv = MakeStubImpl(glad_glTexCoord4fv); glad_glTexCoordPointer = MakeStubImpl(glad_glTexCoordPointer); glad_glTexEnvf = MakeStubImpl(glad_glTexEnvf); glad_glTexEnvfv = MakeStubImpl(glad_glTexEnvfv); diff --git a/test/AGENTS.md b/test/AGENTS.md new file mode 100644 index 00000000000..17fe17159f0 --- /dev/null +++ b/test/AGENTS.md @@ -0,0 +1,128 @@ +# AGENTS.md + +This file provides guidance to coding agents when working with code in this repository's test folder. + +## Build & Run Tests + +```bash +# From build/: build all test executables +cmake --build . --target tests + +# From build/: run tests. ctest/check recipes here assume a non-docker +# build. For a docker build, run `docker-build-v2/build.sh --compile linux -t check` +# (runs ctest inside the container) or invoke binaries in +# build-amd64-linux/test/ directly (see below). +ctest # run already-built tests; does not rebuild +cmake --build . --target check # rebuild engine-headless + all tests first, then ctest -V + +# From build/: run ctest with filters +ctest --output-on-failure # concise; only shows failing output +ctest -R Float3 --output-on-failure # filter by name regex +ctest -R Float3 -V # same, verbose +ctest -N # list all registered tests without running + +# From repo root: run a single test binary directly (fastest iteration). +# Use build-amd64-linux/ instead of build/ if you built via docker. +./build/test/test_Float3 +./build/test/test_Float3 -s # Catch2: show passing assertions too +./build/test/test_Float3 "Float3" # filter by TEST_CASE name (supports wildcards) +``` + +`cmake --build . --target check` is the full-fat target: it depends on `engine-headless` and +every `test_*` executable, so it relinks anything stale before running ctest with +`--output-on-failure -V`. Use bare `ctest` when you want to skip the rebuild. + +## Framework + +**Catch2** (amalgamated single-header version) in `lib/catch2/`. Custom main in `lib/catch2/catch_main.cpp` with leak detection enabled via `CATCH_AMALGAMATED_CUSTOM_MAIN`. + +## Test Organization + +``` +engine/System/ # Core system tests (math, threading, I/O, serialization) +engine/Sim/Misc/ # Simulation tests (QuadField, Ellipsoid) +lib/luasocket/ # Lua socket restriction tests +other/ # Mutex benchmarks, memory pool tests +unitsync/ # UnitSync API tests +validation/ # Integration tests (shell scripts that run full game simulation) +tools/CompileFailTest/ # Negative test framework (tests that must NOT compile) +headercheck/ # Header isolation tests (cmake -DHEADERCHECK=ON) +``` + +## Adding a New Test + +1. Create test source in the appropriate subdirectory under `engine/`, `other/`, etc. +2. In `test/CMakeLists.txt`, add a block using the `add_spring_test` macro: +```cmake +set(test_name MyTest) +set(test_src + "${CMAKE_CURRENT_SOURCE_DIR}/engine/System/testMyTest.cpp" + ${test_Common_sources} +) +set(test_libs "") +set(test_flags "-DNOT_USING_CREG -DNOT_USING_STREFLOP -DBUILDING_AI") +add_spring_test(${test_name} "${test_src}" "${test_libs}" "${test_flags}") +``` +3. The macro creates executable `test_` and registers it with ctest as `test`. + +## Common Compile Flags + +| Flag | Purpose | +|------|---------| +| `-DUNIT_TEST` | Always set for all tests (global) | +| `-DSYNCCHECK` | Always set for all tests (global) | +| `-DNOT_USING_CREG` | Stubs out `CR_*` macros. Default unless the test exercises save/load serialization. | +| `-DNOT_USING_STREFLOP` | Falls back to ``. Default unless the test verifies synced floating-point determinism. | +| `-DBUILDING_AI` | Makes engine headers skip engine-only paths. Pair with `NOT_USING_CREG` and `NOT_USING_STREFLOP`. | +| `-DTHREADPOOL` | Selects the real thread pool over the stub. Set only when the test needs real parallelism. | +| `-DUNITSYNC` | Marks the file as part of unitsync. Only needed for tests that link the unitsync library. | + +## Patterns + +### Basic test file +```cpp +#include +#include "System/Log/ILog.h" + +TEST_CASE("MyFeature") { + CHECK(1 + 1 == 2); + SECTION("sub-case") { + CHECK(true); + } +} +``` + +### Tests that need timing +```cpp +#include "System/Misc/SpringTime.h" +TEST_CASE("TimingTest") { + InitSpringTime ist; // RAII - must be instantiated before using spring_time + // ... +} +``` + +### Thread-safe assertions +Catch2 is NOT thread-safe. Multi-threaded tests must guard assertions: +```cpp +static spring::mutex m; +#define SAFE_CHECK(expr) { std::lock_guard lk(m); CHECK(expr); } +``` + +### Compile-fail tests +Tests that verify code correctly fails to compile. Source uses `#ifdef FAIL` guards: +```cpp +#ifdef FAIL +#ifdef TEST1 + int x = someStronglyTypedEnum; // must not compile +#endif +#endif +``` +Registered in CMakeLists.txt via: +```cmake +spring_test_compile_fail(testName_fail1 ${test_src} "-DTEST1") +``` + +## Test Helpers (mock/stub files) + +- `engine/System/NullGlobalConfig.cpp` — provides default `globalConfig` without full engine init +- `engine/System/Nullerrorhandler.cpp` — stubs `ErrorMessageBox()` to prevent GUI popups diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 099dc4dbe71..61ed08519a1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,15 +3,18 @@ add_definitions(-DCATCH_AMALGAMATED_CUSTOM_MAIN) # defines spring_test_compile_fail macro include(${CMAKE_CURRENT_SOURCE_DIR}/tools/CompileFailTest/CompileFailTest.cmake) -if (UNIX AND (NOT APPLE) AND (NOT MINGW)) +if (UNIX AND NOT APPLE AND NOT MINGW AND + NOT (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")) find_library(REALTIME_LIBRARY rt) if (PREFER_STATIC_LIBS AND NOT EXISTS "${REALTIME_LIBRARY}") message(FATAL_ERROR "librt.[so|a] not found! Needed by std::chrono when statically linked!") endif (PREFER_STATIC_LIBS AND NOT EXISTS "${REALTIME_LIBRARY}") -else (UNIX AND (NOT APPLE) AND (NOT MINGW)) +else (UNIX AND NOT APPLE AND NOT MINGW AND + NOT (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")) set(REALTIME_LIBRARY "") -endif (UNIX AND (NOT APPLE) AND (NOT MINGW)) +endif (UNIX AND NOT APPLE AND NOT MINGW AND + NOT (CMAKE_SYSTEM_NAME MATCHES "OpenBSD")) ADD_SUBDIRECTORY(lib/catch2) # Engine-wide -fsingle-precision-constant (gcc) makes `0.` a float, which @@ -36,7 +39,7 @@ if (WIN32) RESULT_VARIABLE IPV6_RET ERROR_QUIET) else(WIN32) - execute_process(COMMAND ping6 ::1 -c 1 + execute_process(COMMAND ping6 -c 1 ::1 RESULT_VARIABLE IPV6_RET ERROR_QUIET) endif (WIN32) @@ -73,7 +76,9 @@ set(test_common_libraries fmt::fmt ) -add_custom_target(tests) +if(NOT TARGET tests) + add_custom_target(tests) +endif() add_custom_target(check ${CMAKE_CTEST_COMMAND} --output-on-failure -V DEPENDS engine-headless) add_custom_target(install-tests) @@ -338,6 +343,25 @@ endif() add_spring_test(${test_name} "${test_src}" "${test_libs}" "-DTEST") +################################################################################ +### DemoFileExtension + set(test_name DemoFileExtension) + set(test_src + "${CMAKE_CURRENT_SOURCE_DIR}/engine/System/LoadSave/testDemoFileExtension.cpp" + "${ENGINE_SOURCE_DIR}/System/LoadSave/DemoFileExtension.cpp" + "${ENGINE_SOURCE_DIR}/System/Config/ConfigVariable.cpp" + "${ENGINE_SOURCE_DIR}/System/StringUtil.cpp" + ${test_Common_sources} + ) + + find_package_static(ZLIB 1.2.7 REQUIRED) + + set(test_libs + ZLIB::ZLIB + ) + + add_spring_test(${test_name} "${test_src}" "${test_libs}" "") + if (NOT NO_CREG) ################################################################################ ### CREG @@ -407,7 +431,9 @@ endif() set(test_libs ${WINMM_LIBRARY} ) - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND + NOT (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") AND + NOT (CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")) list(APPEND test_libs atomic) endif() add_spring_test(${test_name} "${test_src}" "${test_libs}" "-DTHREADPOOL -DUNITSYNC") diff --git a/test/engine/System/LoadSave/testDemoFileExtension.cpp b/test/engine/System/LoadSave/testDemoFileExtension.cpp new file mode 100644 index 00000000000..0788b113663 --- /dev/null +++ b/test/engine/System/LoadSave/testDemoFileExtension.cpp @@ -0,0 +1,205 @@ +/* This file is part of the Recoil engine (GPL v2 or later), see LICENSE.html */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include "System/Config/ConfigHandler.h" +#include "System/LoadSave/DemoFileExtension.h" +#include "System/LoadSave/demofile.h" + +// Minimal ConfigHandler stub — just enough for GetDemoFileExtensions() +class StubConfigHandler : public ConfigHandler { +public: + std::map values; + + void FinalizeLoad() override {} + void SetString(const std::string&, const std::string&, bool, bool) override {} + std::string GetString(const std::string& key) const override + { + auto it = values.find(key); + return (it != values.end()) ? it->second : std::string{}; + } + bool IsSet(const std::string&) const override { return true; } + bool IsReadOnly(const std::string&) const override { return false; } + bool IsDeprecated(const std::string&) const override { return false; } + void Delete(const std::string&) override {} + std::string GetConfigFile() const override { return {}; } + const std::map GetData() const override { return {}; } + std::map GetDataWithoutDefaults() const override { return {}; } + void Update() override {} + void EnableWriting(bool) override {} +protected: + void AddObserver(ConfigNotifyCallback, void*, const std::vector&) override {} + void RemoveObserver(void*) override {} +}; + +ConfigHandler* configHandler = nullptr; + +struct TempGzFile { + std::string path; + + explicit TempGzFile(const void* data, int len) + { + path = std::tmpnam(nullptr); + gzFile f = gzopen(path.c_str(), "wb"); + REQUIRE(f != nullptr); + if (len > 0) + gzwrite(f, data, len); + gzclose(f); + } + + ~TempGzFile() { std::remove(path.c_str()); } + + TempGzFile(const TempGzFile&) = delete; + TempGzFile& operator=(const TempGzFile&) = delete; +}; + +struct DemoExtFixture { + StubConfigHandler stub; + + DemoExtFixture() { configHandler = &stub; } + ~DemoExtFixture() { configHandler = nullptr; } +}; + +// ============================= GetDemoFileExtensions ======================== + +TEST_CASE_METHOD(DemoExtFixture, "GetDemoFileExtensions: single default extension", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = "sdfz"; + auto exts = GetDemoFileExtensions(); + REQUIRE(exts.size() == 1); + CHECK(exts[0] == "sdfz"); +} + +TEST_CASE_METHOD(DemoExtFixture, "GetDemoFileExtensions: multiple extensions", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = "barreplay,sdfz"; + auto exts = GetDemoFileExtensions(); + REQUIRE(exts.size() == 2); + CHECK(exts[0] == "barreplay"); + CHECK(exts[1] == "sdfz"); +} + +TEST_CASE_METHOD(DemoExtFixture, "GetDemoFileExtensions: trims whitespace", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = " barreplay , sdfz "; + auto exts = GetDemoFileExtensions(); + REQUIRE(exts.size() == 2); + CHECK(exts[0] == "barreplay"); + CHECK(exts[1] == "sdfz"); +} + +TEST_CASE_METHOD(DemoExtFixture, "GetDemoFileExtensions: empty string falls back to sdfz", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = ""; + auto exts = GetDemoFileExtensions(); + REQUIRE(exts.size() == 1); + CHECK(exts[0] == "sdfz"); +} + +TEST_CASE_METHOD(DemoExtFixture, "GetDemoFileExtensions: skips empty segments", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = "foo,,bar"; + auto exts = GetDemoFileExtensions(); + REQUIRE(exts.size() == 2); + CHECK(exts[0] == "foo"); + CHECK(exts[1] == "bar"); +} + +TEST_CASE_METHOD(DemoExtFixture, "GetDemoFileExtensions: filters extensions with slash", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = "bad/ext,ok"; + auto exts = GetDemoFileExtensions(); + REQUIRE(exts.size() == 1); + CHECK(exts[0] == "ok"); +} + +TEST_CASE_METHOD(DemoExtFixture, "GetDemoFileExtensions: filters extensions with dot", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = "bad.ext,ok"; + auto exts = GetDemoFileExtensions(); + REQUIRE(exts.size() == 1); + CHECK(exts[0] == "ok"); +} + +TEST_CASE_METHOD(DemoExtFixture, "GetDemoFileExtensions: filters extensions with backslash", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = "bad\\ext,ok"; + auto exts = GetDemoFileExtensions(); + REQUIRE(exts.size() == 1); + CHECK(exts[0] == "ok"); +} + +TEST_CASE_METHOD(DemoExtFixture, "GetDemoFileExtensions: all invalid falls back to sdfz", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = "a/b,c.d,e\\f"; + auto exts = GetDemoFileExtensions(); + REQUIRE(exts.size() == 1); + CHECK(exts[0] == "sdfz"); +} + +// ============================= IsDemoExtension ============================= + +TEST_CASE_METHOD(DemoExtFixture, "IsDemoExtension: known extensions accepted", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = "barreplay,sdfz"; + CHECK(IsDemoExtension("sdfz")); + CHECK(IsDemoExtension("barreplay")); +} + +TEST_CASE_METHOD(DemoExtFixture, "IsDemoExtension: unknown extension rejected", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = "barreplay,sdfz"; + CHECK_FALSE(IsDemoExtension("mp4")); + CHECK_FALSE(IsDemoExtension("")); +} + +TEST_CASE_METHOD(DemoExtFixture, "IsDemoExtension: case insensitive", "[DemoFileExtension]") +{ + stub.values["DemoFileExtension"] = "sdfz"; + CHECK(IsDemoExtension("SDFZ")); + CHECK(IsDemoExtension("Sdfz")); + CHECK(IsDemoExtension("sdfz")); +} + +// ========================= ContentsLookLikeAReplay ========================= + +TEST_CASE("ContentsLookLikeAReplay: valid magic detected", "[DemoFileExtension]") +{ + char magic[16] = {}; + std::memcpy(magic, DEMOFILE_MAGIC, sizeof(magic)); + TempGzFile tmp(magic, sizeof(magic)); + CHECK(ContentsLookLikeAReplay(tmp.path)); +} + +TEST_CASE("ContentsLookLikeAReplay: wrong magic rejected", "[DemoFileExtension]") +{ + char magic[16] = "not a demofile!"; + TempGzFile tmp(magic, sizeof(magic)); + CHECK_FALSE(ContentsLookLikeAReplay(tmp.path)); +} + +TEST_CASE("ContentsLookLikeAReplay: too short content rejected", "[DemoFileExtension]") +{ + char data[5] = "spri"; + TempGzFile tmp(data, sizeof(data)); + CHECK_FALSE(ContentsLookLikeAReplay(tmp.path)); +} + +TEST_CASE("ContentsLookLikeAReplay: empty file rejected", "[DemoFileExtension]") +{ + TempGzFile tmp(nullptr, 0); + CHECK_FALSE(ContentsLookLikeAReplay(tmp.path)); +} + +TEST_CASE("ContentsLookLikeAReplay: nonexistent file rejected", "[DemoFileExtension]") +{ + CHECK_FALSE(ContentsLookLikeAReplay("/nonexistent/path/replay.sdfz")); +} diff --git a/test/engine/System/Log/TestILog.cpp b/test/engine/System/Log/TestILog.cpp index cb8d4b3e4a1..7b7d1345f0c 100644 --- a/test/engine/System/Log/TestILog.cpp +++ b/test/engine/System/Log/TestILog.cpp @@ -3,6 +3,7 @@ #include "System/Log/FileSink.h" #include "System/Log/StreamSink.h" #include "System/Log/LogUtil.h" +#include "System/Log/DefaultFilter.h" #include @@ -231,3 +232,33 @@ TEST_CASE("IsEnabled") TLOG_SL( "other-one-time-section", L_DEBUG, "Testing LOG_IS_ENABLED_S"); } + +// Regression for the duplicate-entry leak in log_filter_section_setMinLevel. +// Setting a section to a non-default level used to *append* a new row every call +// instead of updating the existing one, so repeated changes to one section filled +// the fixed-size sectionMinLevels table and then made *all* section-level changes +// silently fail ("too many section-levels"). +TEST_CASE("SectionMinLevelNoDuplicateLeak") +{ + // non-default levels for these (non-default) sections; restored at the end + const int savedDefined = log_filter_section_getMinLevel(LOG_SECTION_DEFINED); + const int savedOneTime = log_filter_section_getMinLevel(LOG_SECTION_ONE_TIME_0); + + // hammer one section far more than the table could ever hold + for (int i = 0; i < 300; ++i) + log_filter_section_setMinLevel((i & 1) ? LOG_LEVEL_WARNING : LOG_LEVEL_ERROR, LOG_SECTION_DEFINED); + + // the most recent value wins (a single, updated-in-place entry) + log_filter_section_setMinLevel(LOG_LEVEL_ERROR, LOG_SECTION_DEFINED); + CHECK(log_filter_section_getMinLevel(LOG_SECTION_DEFINED) == LOG_LEVEL_ERROR); + + // and a *different* section must still be settable: with the old append bug + // the table is saturated by now and this set would be dropped + log_filter_section_setMinLevel(LOG_LEVEL_WARNING, LOG_SECTION_ONE_TIME_0); + CHECK(log_filter_section_getMinLevel(LOG_SECTION_ONE_TIME_0) == LOG_LEVEL_WARNING); + + // restore original levels (setting back to default takes the erase path) + log_filter_section_setMinLevel(savedDefined, LOG_SECTION_DEFINED); + log_filter_section_setMinLevel(savedOneTime, LOG_SECTION_ONE_TIME_0); +} + diff --git a/test/engine/System/testClampRad.cpp b/test/engine/System/testClampRad.cpp index db38cf44ba0..5e35d35718a 100644 --- a/test/engine/System/testClampRad.cpp +++ b/test/engine/System/testClampRad.cpp @@ -8,6 +8,19 @@ #include +// COB scripts encode angles as TA units where a full turn is COBSCALE (65536), +// so any angle past a half turn exceeds the range of a signed short and is meant +// to wrap around (it is a circular 16-bit angle). Truncating the scaled value to +// int first is well defined for the bounded angles the sim feeds in, and the +// following int->short narrowing performs that modular wrap deterministically. +// Converting straight from float to short would be undefined behaviour once the +// value leaves short's range, and produced different results on arm64 vs x86, +// desyncing multiplayer. +static inline short RadAngleToCobShort(float radAngle) +{ + return static_cast(static_cast(radAngle * RAD2TAANG)); +} + InitSpringTime ist; TEST_CASE("ClampRad") @@ -43,10 +56,10 @@ TEST_CASE("ClampRad") CHECK_FALSE(std::signbit(ClampRad(0.0f))); // Test TAANG2RAD conversion to short for [0, 2pi) - CHECK(static_cast(ClampRad(0.0f) * RAD2TAANG) == short(0)); - CHECK(static_cast(ClampRad(+std::nextafterf(math::TWOPI, -std::numeric_limits::infinity())) * RAD2TAANG) == short(-1)); - CHECK(static_cast(ClampRad(+std::nextafterf( 0.0f, +std::numeric_limits::infinity())) * RAD2TAANG) == short( 0)); - CHECK(static_cast(ClampRad(+std::nextafterf(TAANG2RAD , +std::numeric_limits::infinity())) * RAD2TAANG) == short(+1)); + CHECK(RadAngleToCobShort(ClampRad(0.0f)) == short(0)); + CHECK(RadAngleToCobShort(ClampRad(+std::nextafterf(math::TWOPI, -std::numeric_limits::infinity()))) == short(-1)); + CHECK(RadAngleToCobShort(ClampRad(+std::nextafterf( 0.0f, +std::numeric_limits::infinity()))) == short( 0)); + CHECK(RadAngleToCobShort(ClampRad(+std::nextafterf(TAANG2RAD , +std::numeric_limits::infinity()))) == short(+1)); } TEST_CASE("ClampRadPi") @@ -76,8 +89,8 @@ TEST_CASE("ClampRadPi") CHECK_FALSE(std::signbit(ClampRadPi(0.0f))); // Test TAANG2RAD conversion to short for [-pi, pi) - CHECK(static_cast(ClampRadPi(-(math::PI)) * RAD2TAANG) == short(-32768)); - CHECK(static_cast(ClampRadPi(+std::nextafterf(math::PI, -std::numeric_limits::infinity())) * RAD2TAANG) == short(32767)); - CHECK(static_cast(ClampRadPi((math::PI)) * RAD2TAANG) == short(-32768)); + CHECK(RadAngleToCobShort(ClampRadPi(-(math::PI))) == short(-32768)); + CHECK(RadAngleToCobShort(ClampRadPi(+std::nextafterf(math::PI, -std::numeric_limits::infinity()))) == short(32767)); + CHECK(RadAngleToCobShort(ClampRadPi((math::PI))) == short(-32768)); } diff --git a/test/other/testMutex.cpp b/test/other/testMutex.cpp index 0a84f129e45..8e1a43ff0e1 100644 --- a/test/other/testMutex.cpp +++ b/test/other/testMutex.cpp @@ -11,11 +11,19 @@ #include #include -#ifndef _WIN32 +#ifdef __linux__ #include #include #endif +#ifdef __OpenBSD__ + #include +#endif + +#ifndef _WIN32 + #include +#endif + #ifdef _WIN32 #include #endif @@ -25,34 +33,43 @@ InitSpringTime ist; #ifndef _WIN32 - typedef uint32_t futex; + typedef uint32_t lock; + + static inline long do_futex (uint32_t *mtx, int op, uint32_t value, const struct timespec *timeout) + { +#ifndef __OpenBSD__ + return syscall(SYS_futex, mtx, op, value, timeout, NULL, 0); +#else + return futex(mtx, op, value, timeout, NULL); +#endif + } - static void futex_init(futex* m) + static void futex_init(lock* m) { *m = 0; } - static void futex_destroy(futex* m) + static void futex_destroy(lock* m) { *m = 0; } - static void futex_lock(futex* m) + static void futex_lock(lock* m) { - futex c; + lock c; if ((c = __sync_val_compare_and_swap(m, 0, 1)) != 0) { do { if ((c == 2) || __sync_val_compare_and_swap(m, 1, 2) != 0) - syscall(SYS_futex, m, FUTEX_WAIT_PRIVATE, 2, NULL, NULL, 0); + do_futex(m, FUTEX_WAIT_PRIVATE, 2, NULL); } while((c = __sync_val_compare_and_swap(m, 0, 2)) != 0); } } - static void futex_unlock(futex* m) + static void futex_unlock(lock* m) { if (__sync_fetch_and_sub(m, 1) != 1) { *m = 0; - syscall(SYS_futex, m, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0); + do_futex(m, FUTEX_WAKE_PRIVATE, 1, NULL); } } #endif @@ -97,7 +114,7 @@ TEST_CASE("Mutex") #endif #ifndef _WIN32 - futex ftx; + lock ftx; futex_init(&ftx); spring_time tCrit = Test("futex", [&]{ futex_lock(&ftx); }, [&]{ futex_unlock(&ftx); }); futex_init(&ftx); diff --git a/test/synctest/README.md b/test/synctest/README.md new file mode 100644 index 00000000000..3bc32d08350 --- /dev/null +++ b/test/synctest/README.md @@ -0,0 +1,48 @@ +# Synctest + +Engine CI sync test driven by BAR's synctest gadget (see beyond-all-reason/Beyond-All-Reason#7440 and #7445). The `.github/workflows/synctest.yml` workflow runs a single seeded scenario on each supported platform (amd64-linux, arm64-linux, amd64-windows) on top of a pinned BAR game + map and asserts that a deterministic sync-hash JSON file lands in the engine's write-dir. The `cross-platform-check` job then verifies all platforms produced identical digests, catching architecture-specific desyncs. Background: beyond-all-reason/RecoilEngine#2910, beyond-all-reason/RecoilEngine#2906. + +The gadget itself rotates through unit categories (bots, tanks, fighters, bombers, hover, subs, ships, spiders) internally, so one scenario is enough — no per-category scripts needed. + +## Running locally + +Build the engine, make sure `spring-headless` and `pr-downloader` are on your PATH or referenced by absolute path, then — using the same `GAME`/`MAP` values as the `env:` block in `.github/workflows/synctest.yml`: + +``` +GAME="Beyond All Reason test-30212-63c6ded" +MAP="Jade Empress 1.41" + +# The PRD_* vars point at BAR's CDN; without them pr-downloader resolves a +# different rapid master than CI does. +PRD_RAPID_USE_STREAMER=false \ +PRD_RAPID_REPO_MASTER=https://repos-cdn.beyondallreason.dev/repos.gz \ +PRD_HTTP_SEARCH_URL=https://files-cdn.beyondallreason.dev/find \ +pr-downloader --filesystem-writepath ./bar-data \ + --download-game "$GAME" \ + --download-map "$MAP" + +# synctest-startscript.txt is a template — render the pins into it first. +sed -e "s/@VERSION@/${GAME##* }/g" -e "s/@MAPNAME@/$MAP/g" \ + test/synctest/synctest-startscript.txt > startscript.txt + +spring-headless --isolation --write-dir ./bar-data startscript.txt +``` + +A successful run exits 0 at game frame ~2200 (the startscript's trailing `quitforce`) and leaves `bar-data/synctest_synchash.json` — a JSON file whose `digest` field is what CI compares. + +## Bumping the pinned BAR version + +BAR's rapid `test--` tags are content-addressed — once published they never change — which is why the CI doesn't need an external version manifest. The only place to bump is the `GAME:` env in `.github/workflows/synctest.yml` (and `MAP:` for the map): the workflow feeds them to `pr-downloader`, substitutes them into the startscript template's `gametype=Beyond All Reason @VERSION@;` and `mapname=@MAPNAME@;` lines, and uses both in the cache key. + + +## Replacing the startscript + +The startscript was generated by BAR from running a fightertest scenario in the actual BAR lobby, then pulled from the bar data dir; modifying it to instead run synctest on a different map. If it ever needs changing, that's the easiest way to regenerate it. + +A trailing `quitforce` in the startscript's `debugcommands` is required so the engine actually quits when the test finishes. + +## Why `luaui disable` + +`debugcommands` disables LuaUI at frame 4, before any units are spawned. This is required for the test to be reproducible. + +BAR's LuaUI widgets are unsynced but issue orders into the simulation, so for example `unit_bombers_default_hold_fire.lua` calls `Spring.GiveOrderToUnit` for every bomber created. Orders issued from unsynced code become network commands, so the frame on which they take effect depends on wall-clock scheduling. Two runs with identical inputs then diverge: a bomber flips to Hold Fire on frame 53 in one run and frame 54 in the next, and everything downstream follows. diff --git a/test/synctest/synctest-startscript.txt b/test/synctest/synctest-startscript.txt new file mode 100644 index 00000000000..95cd3c3356f --- /dev/null +++ b/test/synctest/synctest-startscript.txt @@ -0,0 +1,57 @@ +[game] +{ + [allyteam1] + { + startrectleft=0.82999998; + startrectright=1; + startrectbottom=1; + numallies=0; + startrecttop=0; + } + [team0] + { + startposx=800; + allyteam=0; + teamleader=0; + side=Armada; + startposz=100; + } + [allyteam0] + { + startrectleft=0; + startrectright=0.17; + startrectbottom=1; + numallies=0; + startrecttop=0; + } + [modoptions] + { + debugcommands=2:cheat|3:globallos|4:luaui disable|15:luarules synctest 2000 0.33 0.33 0.1 0.5 100|20:setspeed 20|21:speedcontrol 0|2200:quitforce; + } + [player0] + { + team=0; + name=Player; + } + [ai0] + { + team=1; + host=0; + shortname=NullAI; + } + [team1] + { + allyteam=1; + teamleader=0; + side=Cortex; + startposx=100; + startposz=800; + } + ishost=1; + startpostype=3; + gametype=Beyond All Reason @VERSION@; + mapname=@MAPNAME@; + myplayername=Player; + fixedrngseed=123123; + numplayers=1; +} diff --git a/test/unitsync/testUnitSync.cpp b/test/unitsync/testUnitSync.cpp index 338e31801b0..0aec702e3fe 100644 --- a/test/unitsync/testUnitSync.cpp +++ b/test/unitsync/testUnitSync.cpp @@ -350,4 +350,12 @@ TEST_CASE("UnitSync") if ((errmsg = us::GetNextError()) == nullptr) { FAIL_CHECK("No error on GetWritableDataDirectory before init"); // there's an error cause we called GetWritableDataDirectory() after UnInit()! } + + // Exercise repeated initialization and shutdown. + for (int cycle = 0; cycle < 2; ++cycle) { + CHECK(us::Init(false, 0) != 0); + CHECK_ERROR_MESSAGE(errmsg); + us::UnInit(); + CHECK_ERROR_MESSAGE(errmsg); + } } diff --git a/test/validation/prepare.sh b/test/validation/prepare.sh index e62c45dd675..1fa1e4adf45 100755 --- a/test/validation/prepare.sh +++ b/test/validation/prepare.sh @@ -33,7 +33,6 @@ cat < #include #include #include @@ -417,7 +418,7 @@ void TrafficDump(CDemoReader& reader, bool trafficStats) //uchar myPlayerNum; int frameNum; uint checksum; std::cout << "NETMSG_SYNCRESPONSE: Playernum: "<< (unsigned)buffer[1]; std::cout << " Framenum: " << *(int*)(buffer+2); - std::cout << " Checksum: " << (unsigned)buffer[6]; + std::cout << " Checksum: " << *(uint32_t*)(buffer+6); std::cout << std::endl; break; case NETMSG_DIRECT_CONTROL: diff --git a/tools/benchmark/script_benchmark_zwzsg.txt b/tools/benchmark/script_benchmark_zwzsg.txt index 4ac003c248f..dbc94cbc435 100644 --- a/tools/benchmark/script_benchmark_zwzsg.txt +++ b/tools/benchmark/script_benchmark_zwzsg.txt @@ -35,7 +35,6 @@ datetime=Friday 30 July 2010 at 00:44:15; deathmode=killall; fixedallies=0; fullscript=script.sav; -gamemode=0; ghostedbuildings=1; launchername=Write GameState widget; launcherversion=1.52; diff --git a/tools/pr-downloader b/tools/pr-downloader index 1b95b70bdb6..185cfba7fda 160000 --- a/tools/pr-downloader +++ b/tools/pr-downloader @@ -1 +1 @@ -Subproject commit 1b95b70bdb63923f21f2b6b08240dd769cc733b9 +Subproject commit 185cfba7fda4dc683acec8b2ecbd15838d3022f5 diff --git a/tools/scripts/runner.sh b/tools/scripts/runner.sh index ca9b0f648af..2fe1a16059b 100755 --- a/tools/scripts/runner.sh +++ b/tools/scripts/runner.sh @@ -133,9 +133,6 @@ _write_startscript() StartEnergy=1000; MaxUnits=500; // per team StartPosType=1; // 0 fixed, 1 random, 2 select in map - GameMode=0; // 0 cmdr dead->game continues, 1 cmdr dead->game ends - LimitDgun=1; // limit dgun to fixed radius around startpos? - DiminishingMMs=0; // diminish metal maker's metal production for every new one of them? DisableMapDamage=0; // disable map craters? GhostedBuildings=1; // ghost enemy buildings after losing los on them diff --git a/tools/unitsync/unitsync.cpp b/tools/unitsync/unitsync.cpp index 7da1def5408..5140328b99c 100644 --- a/tools/unitsync/unitsync.cpp +++ b/tools/unitsync/unitsync.cpp @@ -12,6 +12,7 @@ // shared with spring: #include "lib/lua/include/LuaInclude.h" #include "Game/GameVersion.h" +#include "Lua/LuaMemPool.h" #include "Lua/LuaParser.h" #include "Map/MapParser.h" #include "Map/ReadMap.h" @@ -421,6 +422,7 @@ EXPORT(void) UnInit() try { _Cleanup(); FileSystemInitializer::Cleanup(); + LuaMemPool::KillStatic(); ConfigHandler::Deallocate(); DataDirLocater::FreeInstance(); } @@ -2371,4 +2373,3 @@ EXPORT(const char*) GetMacAddrHash() { memcpy(macAddrBuf.data(), macAddrHash.data(), std::min(macAddrHash.size(), macAddrBuf.size())); return (macAddrBuf.data()); } -