mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-14 08:23:52 +00:00
Compare commits
No commits in common. "master" and "v0.4.8-preview" have entirely different histories.
master
...
v0.4.8-pre
641 changed files with 63011 additions and 186468 deletions
47
.clang-tidy
47
.clang-tidy
|
|
@ -1,47 +0,0 @@
|
||||||
# clang-tidy configuration for WoWee
|
|
||||||
# Targets C++20. Checks are tuned for a Vulkan/game-engine codebase:
|
|
||||||
# - reinterpret_cast, pointer arithmetic, and magic numbers are frequent
|
|
||||||
# in low-level graphics/network code, so the most aggressive
|
|
||||||
# cppcoreguidelines and readability-magic-numbers checks are disabled.
|
|
||||||
---
|
|
||||||
Checks: >
|
|
||||||
bugprone-*,
|
|
||||||
clang-analyzer-*,
|
|
||||||
performance-*,
|
|
||||||
modernize-use-nullptr,
|
|
||||||
modernize-use-override,
|
|
||||||
modernize-use-default-member-init,
|
|
||||||
modernize-use-emplace,
|
|
||||||
modernize-loop-convert,
|
|
||||||
modernize-deprecated-headers,
|
|
||||||
modernize-make-unique,
|
|
||||||
modernize-make-shared,
|
|
||||||
modernize-use-nodiscard,
|
|
||||||
modernize-use-designated-initializers,
|
|
||||||
readability-braces-around-statements,
|
|
||||||
readability-container-size-empty,
|
|
||||||
readability-delete-null-pointer,
|
|
||||||
readability-else-after-return,
|
|
||||||
readability-misplaced-array-index,
|
|
||||||
readability-non-const-parameter,
|
|
||||||
readability-redundant-control-flow,
|
|
||||||
readability-redundant-declaration,
|
|
||||||
readability-simplify-boolean-expr,
|
|
||||||
readability-string-compare,
|
|
||||||
-bugprone-easily-swappable-parameters,
|
|
||||||
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
|
|
||||||
-performance-avoid-endl
|
|
||||||
|
|
||||||
WarningsAsErrors: ''
|
|
||||||
|
|
||||||
# Suppress the noise from GCC-only LTO flags in compile_commands.json.
|
|
||||||
# clang doesn't support -fno-fat-lto-objects; this silences the harmless warning.
|
|
||||||
ExtraArgs:
|
|
||||||
- -std=c++20
|
|
||||||
- -Wno-ignored-optimization-argument
|
|
||||||
|
|
||||||
HeaderFilterRegex: '^.*/include/.*\.hpp$'
|
|
||||||
|
|
||||||
CheckOptions:
|
|
||||||
- key: modernize-use-default-member-init.UseAssignment
|
|
||||||
value: true
|
|
||||||
1
.claude/scheduled_tasks.lock
Normal file
1
.claude/scheduled_tasks.lock
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"sessionId":"55a28c7e-8043-44c2-9829-702f303c84ba","pid":3880168,"acquiredAt":1773085726967}
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
# .dockerignore — Exclude files from the Docker build context.
|
|
||||||
# Keeps the context small and prevents leaking build artifacts or secrets.
|
|
||||||
|
|
||||||
# Build outputs
|
|
||||||
build/
|
|
||||||
cache/
|
|
||||||
|
|
||||||
# Git history
|
|
||||||
.git/
|
|
||||||
.gitignore
|
|
||||||
.github/
|
|
||||||
|
|
||||||
# Large external directories (fetched at build time inside the container)
|
|
||||||
extern/FidelityFX-FSR2/
|
|
||||||
extern/FidelityFX-SDK/
|
|
||||||
|
|
||||||
# IDE / editor files
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
*~
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Documentation (not needed for build)
|
|
||||||
docs/
|
|
||||||
*.md
|
|
||||||
!container/*.md
|
|
||||||
|
|
||||||
# Test / tool outputs
|
|
||||||
logs/
|
|
||||||
|
|
||||||
# Host build scripts that run outside the container (not needed inside)
|
|
||||||
build.sh
|
|
||||||
build.bat
|
|
||||||
build.ps1
|
|
||||||
rebuild.sh
|
|
||||||
rebuild.bat
|
|
||||||
rebuild.ps1
|
|
||||||
clean.sh
|
|
||||||
debug_texture.*
|
|
||||||
extract_assets.*
|
|
||||||
extract_warden_rsa.py
|
|
||||||
restart-worldserver.sh
|
|
||||||
test.sh
|
|
||||||
|
|
||||||
# macOS SDK tarballs that may be temporarily placed here
|
|
||||||
*.tar.xz
|
|
||||||
*.tar.gz
|
|
||||||
*.tar.bz2
|
|
||||||
789
.github/workflows/build.yml
vendored
789
.github/workflows/build.yml
vendored
|
|
@ -2,12 +2,11 @@ name: Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [master]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master ]
|
branches: [master]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
WOWEE_AMD_FSR2_REPO: https://github.com/GPUOpen-Effects/FidelityFX-FSR2.git
|
WOWEE_AMD_FSR2_REPO: https://github.com/GPUOpen-Effects/FidelityFX-FSR2.git
|
||||||
WOWEE_AMD_FSR2_REF: master
|
WOWEE_AMD_FSR2_REF: master
|
||||||
WOWEE_FFX_SDK_REPO: https://github.com/Kelsidavis/FidelityFX-SDK.git
|
WOWEE_FFX_SDK_REPO: https://github.com/Kelsidavis/FidelityFX-SDK.git
|
||||||
|
|
@ -21,110 +20,105 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- arch: x86-64
|
- arch: x86-64
|
||||||
runner: ubuntu-24.04
|
runner: ubuntu-24.04
|
||||||
deb_arch: amd64
|
deb_arch: amd64
|
||||||
build_jobs: $(nproc)
|
- arch: arm64
|
||||||
- arch: arm64
|
runner: ubuntu-24.04-arm
|
||||||
runner: ubuntu-24.04-arm
|
deb_arch: arm64
|
||||||
deb_arch: arm64
|
|
||||||
build_jobs: 2
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Cache apt packages
|
- name: Cache apt packages
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: /var/cache/apt/archives/*.deb
|
path: /var/cache/apt/archives/*.deb
|
||||||
key: apt-${{ matrix.arch }}-${{ hashFiles('.github/workflows/build.yml') }}
|
key: apt-${{ matrix.arch }}-${{ hashFiles('.github/workflows/build.yml') }}
|
||||||
restore-keys: apt-${{ matrix.arch }}-
|
restore-keys: apt-${{ matrix.arch }}-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y \
|
sudo apt-get install -y \
|
||||||
cmake \
|
cmake \
|
||||||
build-essential \
|
build-essential \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
libsdl2-dev \
|
libsdl2-dev \
|
||||||
libglew-dev \
|
libglew-dev \
|
||||||
libglm-dev \
|
libglm-dev \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
zlib1g-dev \
|
zlib1g-dev \
|
||||||
libvulkan-dev \
|
libvulkan-dev \
|
||||||
vulkan-tools \
|
vulkan-tools \
|
||||||
glslc \
|
glslc \
|
||||||
libavformat-dev \
|
libavformat-dev \
|
||||||
libavcodec-dev \
|
libavcodec-dev \
|
||||||
libswscale-dev \
|
libswscale-dev \
|
||||||
libavutil-dev \
|
libavutil-dev \
|
||||||
libunicorn-dev \
|
libunicorn-dev \
|
||||||
libx11-dev
|
libx11-dev
|
||||||
sudo apt-get install -y libstorm-dev || true
|
sudo apt-get install -y libstorm-dev || true
|
||||||
|
|
||||||
- name: Fetch AMD FSR2 SDK
|
- name: Fetch AMD FSR2 SDK
|
||||||
run: |
|
run: |
|
||||||
rm -rf extern/FidelityFX-FSR2
|
rm -rf extern/FidelityFX-FSR2
|
||||||
git clone --depth 1 --branch "${WOWEE_AMD_FSR2_REF}" "${WOWEE_AMD_FSR2_REPO}" extern/FidelityFX-FSR2
|
git clone --depth 1 --branch "${WOWEE_AMD_FSR2_REF}" "${WOWEE_AMD_FSR2_REPO}" extern/FidelityFX-FSR2
|
||||||
rm -rf extern/FidelityFX-SDK
|
rm -rf extern/FidelityFX-SDK
|
||||||
git clone --depth 1 --branch "${WOWEE_FFX_SDK_REF}" "${WOWEE_FFX_SDK_REPO}" extern/FidelityFX-SDK
|
git clone --depth 1 --branch "${WOWEE_FFX_SDK_REF}" "${WOWEE_FFX_SDK_REPO}" extern/FidelityFX-SDK
|
||||||
|
|
||||||
- name: Check AMD FSR2 Vulkan permutation headers
|
- name: Check AMD FSR2 Vulkan permutation headers
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
SDK_DIR="$PWD/extern/FidelityFX-FSR2"
|
SDK_DIR="$PWD/extern/FidelityFX-FSR2"
|
||||||
OUT_DIR="$SDK_DIR/src/ffx-fsr2-api/vk/shaders"
|
OUT_DIR="$SDK_DIR/src/ffx-fsr2-api/vk/shaders"
|
||||||
if [ -f "$OUT_DIR/ffx_fsr2_accumulate_pass_permutations.h" ]; then
|
if [ -f "$OUT_DIR/ffx_fsr2_accumulate_pass_permutations.h" ]; then
|
||||||
echo "AMD FSR2 Vulkan permutation headers detected."
|
echo "AMD FSR2 Vulkan permutation headers detected."
|
||||||
else
|
else
|
||||||
echo "AMD FSR2 Vulkan permutation headers not found in SDK checkout."
|
echo "AMD FSR2 Vulkan permutation headers not found in SDK checkout."
|
||||||
echo "WoWee CMake will bootstrap vendored headers."
|
echo "WoWee CMake will bootstrap vendored headers."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Check FidelityFX-SDK Kits framegen headers
|
- name: Check FidelityFX-SDK Kits framegen headers
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
KITS_DIR="$PWD/extern/FidelityFX-SDK/Kits/FidelityFX"
|
KITS_DIR="$PWD/extern/FidelityFX-SDK/Kits/FidelityFX"
|
||||||
test -f "$KITS_DIR/upscalers/fsr3/include/ffx_fsr3upscaler.h"
|
test -f "$KITS_DIR/upscalers/fsr3/include/ffx_fsr3upscaler.h"
|
||||||
test -f "$KITS_DIR/framegeneration/fsr3/include/ffx_frameinterpolation.h"
|
test -f "$KITS_DIR/framegeneration/fsr3/include/ffx_frameinterpolation.h"
|
||||||
test -f "$KITS_DIR/framegeneration/fsr3/include/ffx_opticalflow.h"
|
test -f "$KITS_DIR/framegeneration/fsr3/include/ffx_opticalflow.h"
|
||||||
test -f "$KITS_DIR/backend/vk/ffx_vk.h"
|
test -f "$KITS_DIR/backend/vk/ffx_vk.h"
|
||||||
echo "FidelityFX-SDK Kits framegen headers detected."
|
echo "FidelityFX-SDK Kits framegen headers detected."
|
||||||
|
|
||||||
- name: Configure (AMD ON)
|
- name: Configure (AMD ON)
|
||||||
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DWOWEE_ENABLE_AMD_FSR2=ON -DWOWEE_BUILD_TESTS=ON
|
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DWOWEE_ENABLE_AMD_FSR2=ON
|
||||||
|
|
||||||
- name: Assert AMD FSR2 target
|
- name: Assert AMD FSR2 target
|
||||||
run: cmake --build build --target wowee_fsr2_amd_vk --parallel ${{ matrix.build_jobs }}
|
run: cmake --build build --target wowee_fsr2_amd_vk --parallel $(nproc)
|
||||||
|
|
||||||
- name: Assert AMD FSR3 framegen probe target (if present)
|
- name: Assert AMD FSR3 framegen probe target (if present)
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
if cmake --build build --target help | grep -q 'wowee_fsr3_framegen_amd_vk_probe'; then
|
if cmake --build build --target help | grep -q 'wowee_fsr3_framegen_amd_vk_probe'; then
|
||||||
cmake --build build --target wowee_fsr3_framegen_amd_vk_probe --parallel ${{ matrix.build_jobs }}
|
cmake --build build --target wowee_fsr3_framegen_amd_vk_probe --parallel $(nproc)
|
||||||
else
|
else
|
||||||
echo "FSR3 framegen probe target not generated for this SDK layout; continuing."
|
echo "FSR3 framegen probe target not generated for this SDK layout; continuing."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --parallel ${{ matrix.build_jobs }}
|
run: cmake --build build --parallel $(nproc)
|
||||||
|
|
||||||
- name: Run tests
|
- name: Package (DEB)
|
||||||
run: cd build && ctest --output-on-failure
|
run: cd build && cpack -G DEB
|
||||||
|
|
||||||
- name: Package (DEB)
|
- name: Upload DEB
|
||||||
run: cd build && cpack -G DEB
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
- name: Upload DEB
|
name: wowee-linux-${{ matrix.arch }}-deb
|
||||||
uses: actions/upload-artifact@v4
|
path: build/wowee-*.deb
|
||||||
with:
|
if-no-files-found: error
|
||||||
name: wowee-linux-${{ matrix.arch }}-deb
|
|
||||||
path: build/wowee-*.deb
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
name: Build (macOS ${{ matrix.arch }})
|
name: Build (macOS ${{ matrix.arch }})
|
||||||
|
|
@ -133,396 +127,311 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- arch: arm64
|
- arch: arm64
|
||||||
runner: macos-15
|
runner: macos-15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
brew install cmake pkg-config sdl2 glew glm openssl@3 zlib ffmpeg unicorn \
|
brew install cmake pkg-config sdl2 glew glm openssl@3 zlib ffmpeg unicorn \
|
||||||
stormlib vulkan-loader vulkan-headers shaderc dylibbundler || true
|
stormlib vulkan-loader vulkan-headers shaderc dylibbundler || true
|
||||||
# dylibbundler may not be in all brew mirrors; install separately to not block others
|
# dylibbundler may not be in all brew mirrors; install separately to not block others
|
||||||
brew install dylibbundler 2>/dev/null || true
|
brew install dylibbundler 2>/dev/null || true
|
||||||
|
|
||||||
- name: Fetch AMD FSR2 SDK
|
- name: Fetch AMD FSR2 SDK
|
||||||
run: |
|
run: |
|
||||||
rm -rf extern/FidelityFX-FSR2
|
rm -rf extern/FidelityFX-FSR2
|
||||||
git clone --depth 1 --branch "${WOWEE_AMD_FSR2_REF}" "${WOWEE_AMD_FSR2_REPO}" extern/FidelityFX-FSR2
|
git clone --depth 1 --branch "${WOWEE_AMD_FSR2_REF}" "${WOWEE_AMD_FSR2_REPO}" extern/FidelityFX-FSR2
|
||||||
rm -rf extern/FidelityFX-SDK
|
rm -rf extern/FidelityFX-SDK
|
||||||
git clone --depth 1 --branch "${WOWEE_FFX_SDK_REF}" "${WOWEE_FFX_SDK_REPO}" extern/FidelityFX-SDK
|
git clone --depth 1 --branch "${WOWEE_FFX_SDK_REF}" "${WOWEE_FFX_SDK_REPO}" extern/FidelityFX-SDK
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
BREW=$(brew --prefix)
|
BREW=$(brew --prefix)
|
||||||
export PKG_CONFIG_PATH="$BREW/lib/pkgconfig:$(brew --prefix ffmpeg)/lib/pkgconfig:$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix vulkan-loader)/lib/pkgconfig:$(brew --prefix shaderc)/lib/pkgconfig"
|
export PKG_CONFIG_PATH="$BREW/lib/pkgconfig:$(brew --prefix ffmpeg)/lib/pkgconfig:$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix vulkan-loader)/lib/pkgconfig:$(brew --prefix shaderc)/lib/pkgconfig"
|
||||||
cmake -S . -B build \
|
cmake -S . -B build \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_PREFIX_PATH="$BREW" \
|
-DCMAKE_PREFIX_PATH="$BREW" \
|
||||||
-DOPENSSL_ROOT_DIR="$(brew --prefix openssl@3)" \
|
-DOPENSSL_ROOT_DIR="$(brew --prefix openssl@3)" \
|
||||||
-DWOWEE_ENABLE_AMD_FSR2=ON \
|
-DWOWEE_ENABLE_AMD_FSR2=ON
|
||||||
-DWOWEE_BUILD_TESTS=ON
|
|
||||||
|
|
||||||
- name: Assert AMD FSR2 target
|
- name: Assert AMD FSR2 target
|
||||||
run: cmake --build build --target wowee_fsr2_amd_vk --parallel $(sysctl -n hw.logicalcpu)
|
run: cmake --build build --target wowee_fsr2_amd_vk --parallel $(sysctl -n hw.logicalcpu)
|
||||||
|
|
||||||
- name: Assert AMD FSR3 framegen probe target (if present)
|
- name: Assert AMD FSR3 framegen probe target (if present)
|
||||||
run: |
|
run: |
|
||||||
if cmake --build build --target help | grep -q 'wowee_fsr3_framegen_amd_vk_probe'; then
|
if cmake --build build --target help | grep -q 'wowee_fsr3_framegen_amd_vk_probe'; then
|
||||||
cmake --build build --target wowee_fsr3_framegen_amd_vk_probe --parallel $(sysctl -n hw.logicalcpu)
|
cmake --build build --target wowee_fsr3_framegen_amd_vk_probe --parallel $(sysctl -n hw.logicalcpu)
|
||||||
else
|
else
|
||||||
echo "FSR3 framegen probe target not generated for this SDK layout; continuing."
|
echo "FSR3 framegen probe target not generated for this SDK layout; continuing."
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --parallel $(sysctl -n hw.logicalcpu)
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
run: cd build && ctest --output-on-failure
|
|
||||||
|
|
||||||
- name: Create .app bundle
|
|
||||||
run: |
|
|
||||||
mkdir -p Wowee.app/Contents/{MacOS,Frameworks,Resources}
|
|
||||||
|
|
||||||
# Wrapper launch script — cd to MacOS/ so ./assets/ resolves correctly
|
|
||||||
printf '#!/bin/bash\ncd "$(dirname "$0")"\nexec ./wowee_bin "$@"\n' \
|
|
||||||
> Wowee.app/Contents/MacOS/wowee
|
|
||||||
chmod +x Wowee.app/Contents/MacOS/wowee
|
|
||||||
|
|
||||||
# Actual binary
|
|
||||||
cp build/bin/wowee Wowee.app/Contents/MacOS/wowee_bin
|
|
||||||
|
|
||||||
# Assets (exclude proprietary music)
|
|
||||||
rsync -a --exclude='Original Music' build/bin/assets/ \
|
|
||||||
Wowee.app/Contents/MacOS/assets/
|
|
||||||
|
|
||||||
# Bundle dylibs (if dylibbundler available)
|
|
||||||
if command -v dylibbundler &>/dev/null; then
|
|
||||||
dylibbundler -od -b \
|
|
||||||
-x Wowee.app/Contents/MacOS/wowee_bin \
|
|
||||||
-d Wowee.app/Contents/Frameworks/ \
|
|
||||||
-p @executable_path/../Frameworks/
|
|
||||||
fi
|
|
||||||
|
|
||||||
# dylibbundler may miss Homebrew's Vulkan loader on some runner images.
|
|
||||||
# Copy all vulkan-loader dylib names so wowee_bin can resolve whichever
|
|
||||||
# install_name it was linked against (e.g. libvulkan.1.4.341.dylib).
|
|
||||||
VULKAN_LIB_DIR="$(brew --prefix vulkan-loader)/lib"
|
|
||||||
for lib in "${VULKAN_LIB_DIR}"/libvulkan*.dylib; do
|
|
||||||
[ -e "${lib}" ] || continue
|
|
||||||
cp -f "${lib}" Wowee.app/Contents/Frameworks/
|
|
||||||
done
|
|
||||||
|
|
||||||
if ! ls Wowee.app/Contents/Frameworks/libvulkan*.dylib >/dev/null 2>&1; then
|
|
||||||
echo "Missing Vulkan loader dylib(s) in app bundle Frameworks/" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# dylibbundler may miss Homebrew's OpenSSL on some runner images.
|
|
||||||
# Copy libssl and libcrypto so wowee_bin can resolve them at runtime.
|
|
||||||
OPENSSL_LIB_DIR="$(brew --prefix openssl@3)/lib"
|
|
||||||
for lib in "${OPENSSL_LIB_DIR}"/libssl*.dylib "${OPENSSL_LIB_DIR}"/libcrypto*.dylib; do
|
|
||||||
[ -e "${lib}" ] || continue
|
|
||||||
cp -f "${lib}" Wowee.app/Contents/Frameworks/
|
|
||||||
done
|
|
||||||
|
|
||||||
if ! ls Wowee.app/Contents/Frameworks/libssl*.dylib >/dev/null 2>&1; then
|
|
||||||
echo "Missing OpenSSL libssl dylib(s) in app bundle Frameworks/" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# dylibbundler may miss Homebrew's FFmpeg libraries.
|
|
||||||
# Copy libavformat, libavcodec, libavutil, and libswscale.
|
|
||||||
FFMPEG_LIB_DIR="$(brew --prefix ffmpeg)/lib"
|
|
||||||
for lib in "${FFMPEG_LIB_DIR}"/libavformat*.dylib \
|
|
||||||
"${FFMPEG_LIB_DIR}"/libavcodec*.dylib \
|
|
||||||
"${FFMPEG_LIB_DIR}"/libavutil*.dylib \
|
|
||||||
"${FFMPEG_LIB_DIR}"/libswscale*.dylib \
|
|
||||||
"${FFMPEG_LIB_DIR}"/libswresample*.dylib; do
|
|
||||||
[ -e "${lib}" ] || continue
|
|
||||||
cp -f "${lib}" Wowee.app/Contents/Frameworks/
|
|
||||||
done
|
|
||||||
|
|
||||||
if ! ls Wowee.app/Contents/Frameworks/libavformat*.dylib >/dev/null 2>&1; then
|
|
||||||
echo "Missing FFmpeg libavformat dylib(s) in app bundle Frameworks/" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Info.plist
|
|
||||||
cat > Wowee.app/Contents/Info.plist << 'EOF'
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
||||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0"><dict>
|
|
||||||
<key>CFBundleExecutable</key><string>wowee</string>
|
|
||||||
<key>CFBundleIdentifier</key><string>com.wowee.app</string>
|
|
||||||
<key>CFBundleName</key><string>Wowee</string>
|
|
||||||
<key>CFBundleVersion</key><string>1.0.0</string>
|
|
||||||
<key>CFBundleShortVersionString</key><string>1.0.0</string>
|
|
||||||
<key>CFBundlePackageType</key><string>APPL</string>
|
|
||||||
</dict></plist>
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Ad-hoc codesign (allows running on the local machine)
|
|
||||||
codesign --force --deep --sign - Wowee.app
|
|
||||||
|
|
||||||
- name: Verify bundled dylibs
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
echo "=== dylib references for wowee_bin ==="
|
|
||||||
otool -L Wowee.app/Contents/MacOS/wowee_bin
|
|
||||||
|
|
||||||
# Every non-system dylib referenced by the binary must resolve inside
|
|
||||||
# the bundle. System paths (/usr/lib, /System) are always present on
|
|
||||||
# macOS and don't need bundling.
|
|
||||||
missing=0
|
|
||||||
while IFS= read -r dep; do
|
|
||||||
# Strip leading whitespace and version info: " /path/to/lib.dylib (compat ...)"
|
|
||||||
path=$(echo "$dep" | sed 's/^[[:space:]]*//;s/ (compat.*//')
|
|
||||||
case "$path" in
|
|
||||||
/usr/lib/*|/System/*|@executable_path/*|@rpath/*) continue ;;
|
|
||||||
esac
|
|
||||||
# Resolve @loader_path relative to the binary
|
|
||||||
resolved="${path/#@loader_path/Wowee.app/Contents/MacOS}"
|
|
||||||
if [ ! -f "$resolved" ]; then
|
|
||||||
basename=$(basename "$path")
|
|
||||||
if [ ! -f "Wowee.app/Contents/Frameworks/$basename" ]; then
|
|
||||||
echo "ERROR: unbundled dylib: $path" >&2
|
|
||||||
missing=$((missing + 1))
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
done < <(otool -L Wowee.app/Contents/MacOS/wowee_bin | tail -n +2)
|
|
||||||
|
|
||||||
if [ "$missing" -gt 0 ]; then
|
- name: Build
|
||||||
echo ""
|
run: cmake --build build --parallel $(sysctl -n hw.logicalcpu)
|
||||||
echo "=== Frameworks directory ==="
|
|
||||||
ls -la Wowee.app/Contents/Frameworks/
|
|
||||||
echo ""
|
|
||||||
echo "FAIL: $missing dylib(s) missing from app bundle" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "All non-system dylibs are bundled."
|
|
||||||
|
|
||||||
- name: Create DMG
|
- name: Create .app bundle
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
mkdir -p Wowee.app/Contents/{MacOS,Frameworks,Resources}
|
||||||
rm -f Wowee.dmg
|
|
||||||
# CI runners can occasionally leave a mounted volume around; detach if present.
|
|
||||||
if [ -d "/Volumes/Wowee" ]; then
|
|
||||||
hdiutil detach "/Volumes/Wowee" -force || true
|
|
||||||
sleep 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
ok=0
|
# Wrapper launch script — cd to MacOS/ so ./assets/ resolves correctly
|
||||||
for attempt in 1 2 3 4 5; do
|
printf '#!/bin/bash\ncd "$(dirname "$0")"\nexec ./wowee_bin "$@"\n' \
|
||||||
if hdiutil create -volname Wowee -srcfolder Wowee.app -ov -format UDZO Wowee.dmg; then
|
> Wowee.app/Contents/MacOS/wowee
|
||||||
ok=1
|
chmod +x Wowee.app/Contents/MacOS/wowee
|
||||||
break
|
|
||||||
|
# Actual binary
|
||||||
|
cp build/bin/wowee Wowee.app/Contents/MacOS/wowee_bin
|
||||||
|
|
||||||
|
# Assets (exclude proprietary music)
|
||||||
|
rsync -a --exclude='Original Music' build/bin/assets/ \
|
||||||
|
Wowee.app/Contents/MacOS/assets/
|
||||||
|
|
||||||
|
# Bundle dylibs (if dylibbundler available)
|
||||||
|
if command -v dylibbundler &>/dev/null; then
|
||||||
|
dylibbundler -od -b \
|
||||||
|
-x Wowee.app/Contents/MacOS/wowee_bin \
|
||||||
|
-d Wowee.app/Contents/Frameworks/ \
|
||||||
|
-p @executable_path/../Frameworks/
|
||||||
fi
|
fi
|
||||||
echo "hdiutil create failed on attempt ${attempt}; retrying..."
|
|
||||||
|
# Info.plist
|
||||||
|
cat > Wowee.app/Contents/Info.plist << 'EOF'
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0"><dict>
|
||||||
|
<key>CFBundleExecutable</key><string>wowee</string>
|
||||||
|
<key>CFBundleIdentifier</key><string>com.wowee.app</string>
|
||||||
|
<key>CFBundleName</key><string>Wowee</string>
|
||||||
|
<key>CFBundleVersion</key><string>1.0.0</string>
|
||||||
|
<key>CFBundleShortVersionString</key><string>1.0.0</string>
|
||||||
|
<key>CFBundlePackageType</key><string>APPL</string>
|
||||||
|
</dict></plist>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Ad-hoc codesign (allows running on the local machine)
|
||||||
|
codesign --force --deep --sign - Wowee.app
|
||||||
|
|
||||||
|
- name: Create DMG
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
rm -f Wowee.dmg
|
||||||
|
# CI runners can occasionally leave a mounted volume around; detach if present.
|
||||||
if [ -d "/Volumes/Wowee" ]; then
|
if [ -d "/Volumes/Wowee" ]; then
|
||||||
hdiutil detach "/Volumes/Wowee" -force || true
|
hdiutil detach "/Volumes/Wowee" -force || true
|
||||||
|
sleep 2
|
||||||
fi
|
fi
|
||||||
sleep 3
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$ok" -ne 1 ] || [ ! -f Wowee.dmg ]; then
|
ok=0
|
||||||
echo "Failed to create Wowee.dmg after retries."
|
for attempt in 1 2 3 4 5; do
|
||||||
exit 1
|
if hdiutil create -volname Wowee -srcfolder Wowee.app -ov -format UDZO Wowee.dmg; then
|
||||||
fi
|
ok=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "hdiutil create failed on attempt ${attempt}; retrying..."
|
||||||
|
if [ -d "/Volumes/Wowee" ]; then
|
||||||
|
hdiutil detach "/Volumes/Wowee" -force || true
|
||||||
|
fi
|
||||||
|
sleep 3
|
||||||
|
done
|
||||||
|
|
||||||
- name: Upload DMG
|
if [ "$ok" -ne 1 ] || [ ! -f Wowee.dmg ]; then
|
||||||
uses: actions/upload-artifact@v4
|
echo "Failed to create Wowee.dmg after retries."
|
||||||
with:
|
exit 1
|
||||||
name: wowee-macos-${{ matrix.arch }}-dmg
|
fi
|
||||||
path: Wowee.dmg
|
|
||||||
if-no-files-found: error
|
- name: Upload DMG
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: wowee-macos-${{ matrix.arch }}-dmg
|
||||||
|
path: Wowee.dmg
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
build-windows-arm:
|
build-windows-arm:
|
||||||
name: Build (windows-arm64)
|
name: Build (windows-arm64)
|
||||||
runs-on: windows-11-arm
|
runs-on: windows-11-arm
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Set up MSYS2
|
- name: Set up MSYS2
|
||||||
uses: msys2/setup-msys2@v2
|
uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
msystem: CLANGARM64
|
msystem: CLANGARM64
|
||||||
update: true
|
update: true
|
||||||
install: >-
|
install: >-
|
||||||
mingw-w64-clang-aarch64-cmake
|
mingw-w64-clang-aarch64-cmake
|
||||||
mingw-w64-clang-aarch64-clang
|
mingw-w64-clang-aarch64-clang
|
||||||
mingw-w64-clang-aarch64-ninja
|
mingw-w64-clang-aarch64-ninja
|
||||||
mingw-w64-clang-aarch64-pkgconf
|
mingw-w64-clang-aarch64-pkgconf
|
||||||
mingw-w64-clang-aarch64-SDL2
|
mingw-w64-clang-aarch64-SDL2
|
||||||
mingw-w64-clang-aarch64-glew
|
mingw-w64-clang-aarch64-glew
|
||||||
mingw-w64-clang-aarch64-glm
|
mingw-w64-clang-aarch64-glm
|
||||||
mingw-w64-clang-aarch64-openssl
|
mingw-w64-clang-aarch64-openssl
|
||||||
mingw-w64-clang-aarch64-zlib
|
mingw-w64-clang-aarch64-zlib
|
||||||
mingw-w64-clang-aarch64-ffmpeg
|
mingw-w64-clang-aarch64-ffmpeg
|
||||||
mingw-w64-clang-aarch64-unicorn
|
mingw-w64-clang-aarch64-unicorn
|
||||||
mingw-w64-clang-aarch64-vulkan-loader
|
mingw-w64-clang-aarch64-vulkan-loader
|
||||||
mingw-w64-clang-aarch64-vulkan-headers
|
mingw-w64-clang-aarch64-vulkan-headers
|
||||||
mingw-w64-clang-aarch64-shaderc
|
mingw-w64-clang-aarch64-shaderc
|
||||||
git
|
git
|
||||||
|
|
||||||
- name: Build StormLib from source
|
- name: Build StormLib from source
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 https://github.com/ladislav-zezula/StormLib.git /tmp/StormLib
|
git clone --depth 1 https://github.com/ladislav-zezula/StormLib.git /tmp/StormLib
|
||||||
# Disable x86 inline asm in bundled libtomcrypt (bswapl/movl) —
|
# Disable x86 inline asm in bundled libtomcrypt (bswapl/movl) —
|
||||||
# __MINGW32__ is defined on CLANGARM64 which incorrectly enables x86 asm
|
# __MINGW32__ is defined on CLANGARM64 which incorrectly enables x86 asm
|
||||||
cmake -S /tmp/StormLib -B /tmp/StormLib/build -G Ninja \
|
cmake -S /tmp/StormLib -B /tmp/StormLib/build -G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" \
|
-DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" \
|
||||||
-DBUILD_SHARED_LIBS=OFF \
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
-DCMAKE_C_FLAGS="-DLTC_NO_BSWAP" \
|
-DCMAKE_C_FLAGS="-DLTC_NO_BSWAP" \
|
||||||
-DCMAKE_CXX_FLAGS="-DLTC_NO_BSWAP"
|
-DCMAKE_CXX_FLAGS="-DLTC_NO_BSWAP"
|
||||||
cmake --build /tmp/StormLib/build --parallel $(nproc)
|
cmake --build /tmp/StormLib/build --parallel $(nproc)
|
||||||
cmake --install /tmp/StormLib/build
|
cmake --install /tmp/StormLib/build
|
||||||
|
|
||||||
- name: Fetch AMD FSR2 SDK
|
- name: Fetch AMD FSR2 SDK
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
rm -rf extern/FidelityFX-FSR2
|
rm -rf extern/FidelityFX-FSR2
|
||||||
git clone --depth 1 --branch "${WOWEE_AMD_FSR2_REF}" "${WOWEE_AMD_FSR2_REPO}" extern/FidelityFX-FSR2
|
git clone --depth 1 --branch "${WOWEE_AMD_FSR2_REF}" "${WOWEE_AMD_FSR2_REPO}" extern/FidelityFX-FSR2
|
||||||
rm -rf extern/FidelityFX-SDK
|
rm -rf extern/FidelityFX-SDK
|
||||||
git clone --depth 1 --branch "${WOWEE_FFX_SDK_REF}" "${WOWEE_FFX_SDK_REPO}" extern/FidelityFX-SDK
|
git clone --depth 1 --branch "${WOWEE_FFX_SDK_REF}" "${WOWEE_FFX_SDK_REPO}" extern/FidelityFX-SDK
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DWOWEE_ENABLE_AMD_FSR2=ON
|
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DWOWEE_ENABLE_AMD_FSR2=ON
|
||||||
|
|
||||||
- name: Assert AMD FSR2 target
|
- name: Assert AMD FSR2 target
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: cmake --build build --target wowee_fsr2_amd_vk --parallel $(nproc)
|
run: cmake --build build --target wowee_fsr2_amd_vk --parallel $(nproc)
|
||||||
|
|
||||||
- name: Assert AMD FSR3 framegen probe target (if present)
|
- name: Assert AMD FSR3 framegen probe target (if present)
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
if cmake --build build --target help | grep -q 'wowee_fsr3_framegen_amd_vk_probe'; then
|
if cmake --build build --target help | grep -q 'wowee_fsr3_framegen_amd_vk_probe'; then
|
||||||
cmake --build build --target wowee_fsr3_framegen_amd_vk_probe --parallel $(nproc)
|
cmake --build build --target wowee_fsr3_framegen_amd_vk_probe --parallel $(nproc)
|
||||||
else
|
else
|
||||||
echo "FSR3 framegen probe target not generated for this SDK layout; continuing."
|
echo "FSR3 framegen probe target not generated for this SDK layout; continuing."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: cmake --build build --parallel $(nproc)
|
run: cmake --build build --parallel $(nproc)
|
||||||
|
|
||||||
- name: Bundle DLLs
|
- name: Bundle DLLs
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
ldd build/bin/wowee.exe \
|
ldd build/bin/wowee.exe \
|
||||||
| awk '/=> \// { print $3 }' \
|
| awk '/=> \// { print $3 }' \
|
||||||
| grep -iv '^/c/Windows' \
|
| grep -iv '^/c/Windows' \
|
||||||
| xargs -I{} sh -c 'cp -n "{}" build/bin/ 2>/dev/null || true'
|
| xargs -I{} sh -c 'cp -n "{}" build/bin/ 2>/dev/null || true'
|
||||||
|
|
||||||
- name: Package (ZIP)
|
- name: Package (ZIP)
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: cd build && cpack -G ZIP
|
run: cd build && cpack -G ZIP
|
||||||
|
|
||||||
- name: Upload ZIP
|
- name: Upload ZIP
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: wowee-windows-arm64-zip
|
name: wowee-windows-arm64-zip
|
||||||
path: build/wowee-*.zip
|
path: build/wowee-*.zip
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
name: Build (windows-x86-64)
|
name: Build (windows-x86-64)
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Set up MSYS2
|
- name: Set up MSYS2
|
||||||
uses: msys2/setup-msys2@v2
|
uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
msystem: MINGW64
|
msystem: MINGW64
|
||||||
update: false
|
update: false
|
||||||
install: >-
|
install: >-
|
||||||
mingw-w64-x86_64-cmake
|
mingw-w64-x86_64-cmake
|
||||||
mingw-w64-x86_64-gcc
|
mingw-w64-x86_64-gcc
|
||||||
mingw-w64-x86_64-ninja
|
mingw-w64-x86_64-ninja
|
||||||
mingw-w64-x86_64-pkgconf
|
mingw-w64-x86_64-pkgconf
|
||||||
mingw-w64-x86_64-SDL2
|
mingw-w64-x86_64-SDL2
|
||||||
mingw-w64-x86_64-glew
|
mingw-w64-x86_64-glew
|
||||||
mingw-w64-x86_64-glm
|
mingw-w64-x86_64-glm
|
||||||
mingw-w64-x86_64-openssl
|
mingw-w64-x86_64-openssl
|
||||||
mingw-w64-x86_64-zlib
|
mingw-w64-x86_64-zlib
|
||||||
mingw-w64-x86_64-ffmpeg
|
mingw-w64-x86_64-ffmpeg
|
||||||
mingw-w64-x86_64-unicorn
|
mingw-w64-x86_64-unicorn
|
||||||
mingw-w64-x86_64-vulkan-loader
|
mingw-w64-x86_64-vulkan-loader
|
||||||
mingw-w64-x86_64-vulkan-headers
|
mingw-w64-x86_64-vulkan-headers
|
||||||
mingw-w64-x86_64-shaderc
|
mingw-w64-x86_64-shaderc
|
||||||
mingw-w64-x86_64-nsis
|
mingw-w64-x86_64-nsis
|
||||||
git
|
git
|
||||||
|
|
||||||
- name: Build StormLib from source
|
- name: Build StormLib from source
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 https://github.com/ladislav-zezula/StormLib.git /tmp/StormLib
|
git clone --depth 1 https://github.com/ladislav-zezula/StormLib.git /tmp/StormLib
|
||||||
cmake -S /tmp/StormLib -B /tmp/StormLib/build -G Ninja \
|
cmake -S /tmp/StormLib -B /tmp/StormLib/build -G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" \
|
-DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" \
|
||||||
-DBUILD_SHARED_LIBS=OFF
|
-DBUILD_SHARED_LIBS=OFF
|
||||||
cmake --build /tmp/StormLib/build --parallel $(nproc)
|
cmake --build /tmp/StormLib/build --parallel $(nproc)
|
||||||
cmake --install /tmp/StormLib/build
|
cmake --install /tmp/StormLib/build
|
||||||
|
|
||||||
- name: Fetch AMD FSR2 SDK
|
- name: Fetch AMD FSR2 SDK
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
rm -rf extern/FidelityFX-FSR2
|
rm -rf extern/FidelityFX-FSR2
|
||||||
git clone --depth 1 --branch "${WOWEE_AMD_FSR2_REF}" "${WOWEE_AMD_FSR2_REPO}" extern/FidelityFX-FSR2
|
git clone --depth 1 --branch "${WOWEE_AMD_FSR2_REF}" "${WOWEE_AMD_FSR2_REPO}" extern/FidelityFX-FSR2
|
||||||
rm -rf extern/FidelityFX-SDK
|
rm -rf extern/FidelityFX-SDK
|
||||||
git clone --depth 1 --branch "${WOWEE_FFX_SDK_REF}" "${WOWEE_FFX_SDK_REPO}" extern/FidelityFX-SDK
|
git clone --depth 1 --branch "${WOWEE_FFX_SDK_REF}" "${WOWEE_FFX_SDK_REPO}" extern/FidelityFX-SDK
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DWOWEE_ENABLE_AMD_FSR2=ON
|
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DWOWEE_ENABLE_AMD_FSR2=ON
|
||||||
|
|
||||||
- name: Assert AMD FSR2 target
|
- name: Assert AMD FSR2 target
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: cmake --build build --target wowee_fsr2_amd_vk --parallel $(nproc)
|
run: cmake --build build --target wowee_fsr2_amd_vk --parallel $(nproc)
|
||||||
|
|
||||||
- name: Assert AMD FSR3 framegen probe target (if present)
|
- name: Assert AMD FSR3 framegen probe target (if present)
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
if cmake --build build --target help | grep -q 'wowee_fsr3_framegen_amd_vk_probe'; then
|
if cmake --build build --target help | grep -q 'wowee_fsr3_framegen_amd_vk_probe'; then
|
||||||
cmake --build build --target wowee_fsr3_framegen_amd_vk_probe --parallel $(nproc)
|
cmake --build build --target wowee_fsr3_framegen_amd_vk_probe --parallel $(nproc)
|
||||||
else
|
else
|
||||||
echo "FSR3 framegen probe target not generated for this SDK layout; continuing."
|
echo "FSR3 framegen probe target not generated for this SDK layout; continuing."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: cmake --build build --parallel $(nproc)
|
run: cmake --build build --parallel $(nproc)
|
||||||
|
|
||||||
- name: Bundle DLLs
|
- name: Bundle DLLs
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
ldd build/bin/wowee.exe \
|
ldd build/bin/wowee.exe \
|
||||||
| awk '/=> \// { print $3 }' \
|
| awk '/=> \// { print $3 }' \
|
||||||
| grep -iv '^/c/Windows' \
|
| grep -iv '^/c/Windows' \
|
||||||
| xargs -I{} sh -c 'cp -n "{}" build/bin/ 2>/dev/null || true'
|
| xargs -I{} sh -c 'cp -n "{}" build/bin/ 2>/dev/null || true'
|
||||||
|
|
||||||
- name: Package (NSIS)
|
- name: Package (NSIS)
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: cd build && cpack -G NSIS
|
run: cd build && cpack -G NSIS
|
||||||
|
|
||||||
- name: Upload installer
|
- name: Upload installer
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: wowee-windows-x86-64-installer
|
name: wowee-windows-x86-64-installer
|
||||||
path: build/wowee-*.exe
|
path: build/wowee-*.exe
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
|
||||||
632
.github/workflows/release.yml
vendored
632
.github/workflows/release.yml
vendored
|
|
@ -2,10 +2,7 @@ name: Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: [ 'v*' ]
|
tags: ['v*']
|
||||||
|
|
||||||
env:
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
@ -18,274 +15,201 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- arch: x86-64
|
- arch: x86-64
|
||||||
runner: ubuntu-24.04
|
runner: ubuntu-24.04
|
||||||
- arch: arm64
|
- arch: arm64
|
||||||
runner: ubuntu-24.04-arm
|
runner: ubuntu-24.04-arm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Cache apt packages
|
- name: Cache apt packages
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: /var/cache/apt/archives/*.deb
|
path: /var/cache/apt/archives/*.deb
|
||||||
key: apt-${{ matrix.arch }}-${{ hashFiles('.github/workflows/release.yml') }}
|
key: apt-${{ matrix.arch }}-${{ hashFiles('.github/workflows/release.yml') }}
|
||||||
restore-keys: apt-${{ matrix.arch }}-
|
restore-keys: apt-${{ matrix.arch }}-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y \
|
sudo apt-get install -y \
|
||||||
cmake \
|
cmake \
|
||||||
build-essential \
|
build-essential \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
libsdl2-dev \
|
libsdl2-dev \
|
||||||
libglew-dev \
|
libglew-dev \
|
||||||
libglm-dev \
|
libglm-dev \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
zlib1g-dev \
|
zlib1g-dev \
|
||||||
libvulkan-dev \
|
libvulkan-dev \
|
||||||
vulkan-tools \
|
vulkan-tools \
|
||||||
glslc \
|
glslc \
|
||||||
libavformat-dev \
|
libavformat-dev \
|
||||||
libavcodec-dev \
|
libavcodec-dev \
|
||||||
libswscale-dev \
|
libswscale-dev \
|
||||||
libavutil-dev \
|
libavutil-dev \
|
||||||
libunicorn-dev \
|
libunicorn-dev \
|
||||||
libx11-dev
|
libx11-dev
|
||||||
sudo apt-get install -y libstorm-dev || true
|
sudo apt-get install -y libstorm-dev || true
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
- name: Verify Release Config
|
- name: Verify Release Config
|
||||||
run: |
|
run: |
|
||||||
cmake -LA -N build | grep -E '^CMAKE_BUILD_TYPE:STRING=Release$'
|
cmake -LA -N build | grep -E '^CMAKE_BUILD_TYPE:STRING=Release$'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --parallel $(nproc)
|
run: cmake --build build --parallel $(nproc)
|
||||||
|
|
||||||
- name: Package
|
- name: Package
|
||||||
run: |
|
run: |
|
||||||
TAG="${GITHUB_REF_NAME}"
|
TAG="${GITHUB_REF_NAME}"
|
||||||
STAGING="wowee-${TAG}-linux-${{ matrix.arch }}"
|
STAGING="wowee-${TAG}-linux-${{ matrix.arch }}"
|
||||||
mkdir -p "${STAGING}"
|
mkdir -p "${STAGING}"
|
||||||
|
|
||||||
# Binary
|
# Binary
|
||||||
cp build/bin/wowee "${STAGING}/"
|
cp build/bin/wowee "${STAGING}/"
|
||||||
|
|
||||||
# Asset extraction tool (if built — requires StormLib)
|
# Asset extraction tool (if built — requires StormLib)
|
||||||
if [ -f build/bin/asset_extract ]; then
|
if [ -f build/bin/asset_extract ]; then
|
||||||
cp build/bin/asset_extract "${STAGING}/"
|
cp build/bin/asset_extract "${STAGING}/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extraction scripts and GUI
|
# Extraction scripts and GUI
|
||||||
cp extract_assets.sh "${STAGING}/"
|
cp extract_assets.sh "${STAGING}/"
|
||||||
cp tools/asset_pipeline_gui.py "${STAGING}/"
|
cp tools/asset_pipeline_gui.py "${STAGING}/"
|
||||||
|
|
||||||
# Assets (exclude proprietary music)
|
# Assets (exclude proprietary music)
|
||||||
rsync -a --exclude='Original Music' build/bin/assets/ "${STAGING}/assets/"
|
rsync -a --exclude='Original Music' build/bin/assets/ "${STAGING}/assets/"
|
||||||
|
|
||||||
# Data directory (git-tracked files only)
|
# Data directory (git-tracked files only)
|
||||||
git ls-files Data/ | while read -r f; do
|
git ls-files Data/ | while read -r f; do
|
||||||
mkdir -p "${STAGING}/$(dirname "$f")"
|
mkdir -p "${STAGING}/$(dirname "$f")"
|
||||||
cp "$f" "${STAGING}/$f"
|
cp "$f" "${STAGING}/$f"
|
||||||
done
|
done
|
||||||
|
|
||||||
tar czf "${STAGING}.tar.gz" "${STAGING}"
|
tar czf "${STAGING}.tar.gz" "${STAGING}"
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: wowee-linux-${{ matrix.arch }}
|
name: wowee-linux-${{ matrix.arch }}
|
||||||
path: wowee-*.tar.gz
|
path: wowee-*.tar.gz
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
name: Build (macOS arm64)
|
name: Build (macOS arm64)
|
||||||
runs-on: macos-15
|
runs-on: macos-15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
brew install cmake pkg-config sdl2 glew glm openssl@3 zlib ffmpeg unicorn \
|
brew install cmake pkg-config sdl2 glew glm openssl@3 zlib ffmpeg unicorn \
|
||||||
stormlib vulkan-loader vulkan-headers shaderc dylibbundler || true
|
stormlib vulkan-loader vulkan-headers shaderc dylibbundler || true
|
||||||
brew install dylibbundler 2>/dev/null || true
|
brew install dylibbundler 2>/dev/null || true
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
BREW=$(brew --prefix)
|
BREW=$(brew --prefix)
|
||||||
export PKG_CONFIG_PATH="$BREW/lib/pkgconfig:$(brew --prefix ffmpeg)/lib/pkgconfig:$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix vulkan-loader)/lib/pkgconfig:$(brew --prefix shaderc)/lib/pkgconfig"
|
export PKG_CONFIG_PATH="$BREW/lib/pkgconfig:$(brew --prefix ffmpeg)/lib/pkgconfig:$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix vulkan-loader)/lib/pkgconfig:$(brew --prefix shaderc)/lib/pkgconfig"
|
||||||
cmake -S . -B build \
|
cmake -S . -B build \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_PREFIX_PATH="$BREW" \
|
-DCMAKE_PREFIX_PATH="$BREW" \
|
||||||
-DOPENSSL_ROOT_DIR="$(brew --prefix openssl@3)"
|
-DOPENSSL_ROOT_DIR="$(brew --prefix openssl@3)"
|
||||||
|
|
||||||
- name: Verify Release Config
|
- name: Verify Release Config
|
||||||
run: |
|
run: |
|
||||||
cmake -LA -N build | grep -E '^CMAKE_BUILD_TYPE:STRING=Release$'
|
cmake -LA -N build | grep -E '^CMAKE_BUILD_TYPE:STRING=Release$'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build build --parallel $(sysctl -n hw.logicalcpu)
|
run: cmake --build build --parallel $(sysctl -n hw.logicalcpu)
|
||||||
|
|
||||||
- name: Create .app bundle
|
- name: Create .app bundle
|
||||||
run: |
|
run: |
|
||||||
TAG="${GITHUB_REF_NAME}"
|
TAG="${GITHUB_REF_NAME}"
|
||||||
mkdir -p Wowee.app/Contents/{MacOS,Frameworks,Resources}
|
mkdir -p Wowee.app/Contents/{MacOS,Frameworks,Resources}
|
||||||
|
|
||||||
# Wrapper launch script
|
# Wrapper launch script
|
||||||
printf '#!/bin/bash\ncd "$(dirname "$0")"\nexec ./wowee_bin "$@"\n' \
|
printf '#!/bin/bash\ncd "$(dirname "$0")"\nexec ./wowee_bin "$@"\n' \
|
||||||
> Wowee.app/Contents/MacOS/wowee
|
> Wowee.app/Contents/MacOS/wowee
|
||||||
chmod +x Wowee.app/Contents/MacOS/wowee
|
chmod +x Wowee.app/Contents/MacOS/wowee
|
||||||
|
|
||||||
# Actual binary
|
# Actual binary
|
||||||
cp build/bin/wowee Wowee.app/Contents/MacOS/wowee_bin
|
cp build/bin/wowee Wowee.app/Contents/MacOS/wowee_bin
|
||||||
|
|
||||||
# Asset extraction tool (if built — requires StormLib)
|
# Asset extraction tool (if built — requires StormLib)
|
||||||
if [ -f build/bin/asset_extract ]; then
|
if [ -f build/bin/asset_extract ]; then
|
||||||
cp build/bin/asset_extract Wowee.app/Contents/MacOS/
|
cp build/bin/asset_extract Wowee.app/Contents/MacOS/
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extraction scripts and GUI
|
# Extraction scripts and GUI
|
||||||
cp extract_assets.sh Wowee.app/Contents/MacOS/
|
cp extract_assets.sh Wowee.app/Contents/MacOS/
|
||||||
cp tools/asset_pipeline_gui.py Wowee.app/Contents/MacOS/
|
cp tools/asset_pipeline_gui.py Wowee.app/Contents/MacOS/
|
||||||
|
|
||||||
# Assets (exclude proprietary music)
|
# Assets (exclude proprietary music)
|
||||||
rsync -a --exclude='Original Music' build/bin/assets/ \
|
rsync -a --exclude='Original Music' build/bin/assets/ \
|
||||||
Wowee.app/Contents/MacOS/assets/
|
Wowee.app/Contents/MacOS/assets/
|
||||||
|
|
||||||
# Data directory (git-tracked files only)
|
# Data directory (git-tracked files only)
|
||||||
git ls-files Data/ | while read -r f; do
|
git ls-files Data/ | while read -r f; do
|
||||||
mkdir -p "Wowee.app/Contents/MacOS/$(dirname "$f")"
|
mkdir -p "Wowee.app/Contents/MacOS/$(dirname "$f")"
|
||||||
cp "$f" "Wowee.app/Contents/MacOS/$f"
|
cp "$f" "Wowee.app/Contents/MacOS/$f"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Bundle dylibs
|
# Bundle dylibs
|
||||||
if command -v dylibbundler &>/dev/null; then
|
if command -v dylibbundler &>/dev/null; then
|
||||||
dylibbundler -od -b \
|
|
||||||
-x Wowee.app/Contents/MacOS/wowee_bin \
|
|
||||||
-d Wowee.app/Contents/Frameworks/ \
|
|
||||||
-p @executable_path/../Frameworks/
|
|
||||||
if [ -f Wowee.app/Contents/MacOS/asset_extract ]; then
|
|
||||||
dylibbundler -od -b \
|
dylibbundler -od -b \
|
||||||
-x Wowee.app/Contents/MacOS/asset_extract \
|
-x Wowee.app/Contents/MacOS/wowee_bin \
|
||||||
-d Wowee.app/Contents/Frameworks/ \
|
-d Wowee.app/Contents/Frameworks/ \
|
||||||
-p @executable_path/../Frameworks/
|
-p @executable_path/../Frameworks/
|
||||||
fi
|
if [ -f Wowee.app/Contents/MacOS/asset_extract ]; then
|
||||||
fi
|
dylibbundler -od -b \
|
||||||
|
-x Wowee.app/Contents/MacOS/asset_extract \
|
||||||
# dylibbundler may miss Homebrew's Vulkan loader on some runner images.
|
-d Wowee.app/Contents/Frameworks/ \
|
||||||
# Copy all vulkan-loader dylib names so wowee_bin can resolve whichever
|
-p @executable_path/../Frameworks/
|
||||||
# install_name it was linked against (e.g. libvulkan.1.4.341.dylib).
|
|
||||||
VULKAN_LIB_DIR="$(brew --prefix vulkan-loader)/lib"
|
|
||||||
for lib in "${VULKAN_LIB_DIR}"/libvulkan*.dylib; do
|
|
||||||
[ -e "${lib}" ] || continue
|
|
||||||
cp -f "${lib}" Wowee.app/Contents/Frameworks/
|
|
||||||
done
|
|
||||||
|
|
||||||
if ! ls Wowee.app/Contents/Frameworks/libvulkan*.dylib >/dev/null 2>&1; then
|
|
||||||
echo "Missing Vulkan loader dylib(s) in app bundle Frameworks/" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# dylibbundler may miss Homebrew's OpenSSL on some runner images.
|
|
||||||
# Copy libssl and libcrypto so wowee_bin can resolve them at runtime.
|
|
||||||
OPENSSL_LIB_DIR="$(brew --prefix openssl@3)/lib"
|
|
||||||
for lib in "${OPENSSL_LIB_DIR}"/libssl*.dylib "${OPENSSL_LIB_DIR}"/libcrypto*.dylib; do
|
|
||||||
[ -e "${lib}" ] || continue
|
|
||||||
cp -f "${lib}" Wowee.app/Contents/Frameworks/
|
|
||||||
done
|
|
||||||
|
|
||||||
if ! ls Wowee.app/Contents/Frameworks/libssl*.dylib >/dev/null 2>&1; then
|
|
||||||
echo "Missing OpenSSL libssl dylib(s) in app bundle Frameworks/" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# dylibbundler may miss Homebrew's FFmpeg libraries.
|
|
||||||
# Copy libavformat, libavcodec, libavutil, and libswscale.
|
|
||||||
FFMPEG_LIB_DIR="$(brew --prefix ffmpeg)/lib"
|
|
||||||
for lib in "${FFMPEG_LIB_DIR}"/libavformat*.dylib \
|
|
||||||
"${FFMPEG_LIB_DIR}"/libavcodec*.dylib \
|
|
||||||
"${FFMPEG_LIB_DIR}"/libavutil*.dylib \
|
|
||||||
"${FFMPEG_LIB_DIR}"/libswscale*.dylib \
|
|
||||||
"${FFMPEG_LIB_DIR}"/libswresample*.dylib; do
|
|
||||||
[ -e "${lib}" ] || continue
|
|
||||||
cp -f "${lib}" Wowee.app/Contents/Frameworks/
|
|
||||||
done
|
|
||||||
|
|
||||||
if ! ls Wowee.app/Contents/Frameworks/libavformat*.dylib >/dev/null 2>&1; then
|
|
||||||
echo "Missing FFmpeg libavformat dylib(s) in app bundle Frameworks/" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Info.plist
|
|
||||||
cat > Wowee.app/Contents/Info.plist << EOF
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
|
||||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0"><dict>
|
|
||||||
<key>CFBundleExecutable</key><string>wowee</string>
|
|
||||||
<key>CFBundleIdentifier</key><string>com.wowee.app</string>
|
|
||||||
<key>CFBundleName</key><string>Wowee</string>
|
|
||||||
<key>CFBundleVersion</key><string>${TAG}</string>
|
|
||||||
<key>CFBundleShortVersionString</key><string>${TAG}</string>
|
|
||||||
<key>CFBundlePackageType</key><string>APPL</string>
|
|
||||||
</dict></plist>
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Ad-hoc codesign
|
|
||||||
codesign --force --deep --sign - Wowee.app
|
|
||||||
|
|
||||||
- name: Verify bundled dylibs
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
echo "=== dylib references for wowee_bin ==="
|
|
||||||
otool -L Wowee.app/Contents/MacOS/wowee_bin
|
|
||||||
|
|
||||||
missing=0
|
|
||||||
while IFS= read -r dep; do
|
|
||||||
path=$(echo "$dep" | sed 's/^[[:space:]]*//;s/ (compat.*//')
|
|
||||||
case "$path" in
|
|
||||||
/usr/lib/*|/System/*|@executable_path/*|@rpath/*) continue ;;
|
|
||||||
esac
|
|
||||||
resolved="${path/#@loader_path/Wowee.app/Contents/MacOS}"
|
|
||||||
if [ ! -f "$resolved" ]; then
|
|
||||||
basename=$(basename "$path")
|
|
||||||
if [ ! -f "Wowee.app/Contents/Frameworks/$basename" ]; then
|
|
||||||
echo "ERROR: unbundled dylib: $path" >&2
|
|
||||||
missing=$((missing + 1))
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done < <(otool -L Wowee.app/Contents/MacOS/wowee_bin | tail -n +2)
|
|
||||||
|
|
||||||
if [ "$missing" -gt 0 ]; then
|
# Info.plist
|
||||||
ls -la Wowee.app/Contents/Frameworks/
|
cat > Wowee.app/Contents/Info.plist << EOF
|
||||||
echo "FAIL: $missing dylib(s) missing from app bundle" >&2
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
exit 1
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||||
fi
|
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
echo "All non-system dylibs are bundled."
|
<plist version="1.0"><dict>
|
||||||
|
<key>CFBundleExecutable</key><string>wowee</string>
|
||||||
|
<key>CFBundleIdentifier</key><string>com.wowee.app</string>
|
||||||
|
<key>CFBundleName</key><string>Wowee</string>
|
||||||
|
<key>CFBundleVersion</key><string>${TAG}</string>
|
||||||
|
<key>CFBundleShortVersionString</key><string>${TAG}</string>
|
||||||
|
<key>CFBundlePackageType</key><string>APPL</string>
|
||||||
|
</dict></plist>
|
||||||
|
EOF
|
||||||
|
|
||||||
- name: Create DMG
|
# Ad-hoc codesign
|
||||||
run: |
|
codesign --force --deep --sign - Wowee.app
|
||||||
TAG="${GITHUB_REF_NAME}"
|
|
||||||
hdiutil create -volname Wowee -srcfolder Wowee.app -ov -format UDZO \
|
|
||||||
"wowee-${TAG}-macos-arm64.dmg"
|
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Create DMG
|
||||||
uses: actions/upload-artifact@v4
|
run: |
|
||||||
with:
|
TAG="${GITHUB_REF_NAME}"
|
||||||
name: wowee-macos-arm64
|
hdiutil create -volname Wowee -srcfolder Wowee.app -ov -format UDZO \
|
||||||
path: wowee-*.dmg
|
"wowee-${TAG}-macos-arm64.dmg"
|
||||||
if-no-files-found: error
|
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: wowee-macos-arm64
|
||||||
|
path: wowee-*.dmg
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
name: Build (windows-${{ matrix.arch }})
|
name: Build (windows-${{ matrix.arch }})
|
||||||
|
|
@ -294,145 +218,159 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- arch: x86-64
|
- arch: x86-64
|
||||||
runner: windows-latest
|
runner: windows-latest
|
||||||
msystem: MINGW64
|
msystem: MINGW64
|
||||||
prefix: mingw-w64-x86_64
|
prefix: mingw-w64-x86_64
|
||||||
- arch: arm64
|
- arch: arm64
|
||||||
runner: windows-11-arm
|
runner: windows-11-arm
|
||||||
msystem: CLANGARM64
|
msystem: CLANGARM64
|
||||||
prefix: mingw-w64-clang-aarch64
|
prefix: mingw-w64-clang-aarch64
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Set up MSYS2
|
- name: Set up MSYS2
|
||||||
uses: msys2/setup-msys2@v2
|
uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
msystem: ${{ matrix.msystem }}
|
msystem: ${{ matrix.msystem }}
|
||||||
update: ${{ matrix.arch == 'arm64' }}
|
update: ${{ matrix.arch == 'arm64' }}
|
||||||
install: >-
|
install: >-
|
||||||
${{ matrix.prefix }}-cmake ${{ matrix.arch == 'x86-64' && format('{0}-gcc', matrix.prefix) || format('{0}-clang', matrix.prefix) }} ${{ matrix.prefix }}-ninja ${{ matrix.prefix }}-pkgconf ${{ matrix.prefix }}-SDL2 ${{ matrix.prefix }}-glew ${{ matrix.prefix }}-glm ${{ matrix.prefix }}-openssl ${{ matrix.prefix }}-zlib ${{ matrix.prefix }}-ffmpeg ${{ matrix.prefix }}-unicorn ${{ matrix.prefix }}-vulkan-loader ${{ matrix.prefix }}-vulkan-headers ${{ matrix.prefix }}-shaderc git
|
${{ matrix.prefix }}-cmake
|
||||||
|
${{ matrix.arch == 'x86-64' && format('{0}-gcc', matrix.prefix) || format('{0}-clang', matrix.prefix) }}
|
||||||
|
${{ matrix.prefix }}-ninja
|
||||||
|
${{ matrix.prefix }}-pkgconf
|
||||||
|
${{ matrix.prefix }}-SDL2
|
||||||
|
${{ matrix.prefix }}-glew
|
||||||
|
${{ matrix.prefix }}-glm
|
||||||
|
${{ matrix.prefix }}-openssl
|
||||||
|
${{ matrix.prefix }}-zlib
|
||||||
|
${{ matrix.prefix }}-ffmpeg
|
||||||
|
${{ matrix.prefix }}-unicorn
|
||||||
|
${{ matrix.prefix }}-vulkan-loader
|
||||||
|
${{ matrix.prefix }}-vulkan-headers
|
||||||
|
${{ matrix.prefix }}-shaderc
|
||||||
|
git
|
||||||
|
|
||||||
- name: Install optional packages
|
- name: Install optional packages
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
pacman -S --noconfirm --needed zip
|
pacman -S --noconfirm --needed zip
|
||||||
|
|
||||||
- name: Build StormLib from source
|
- name: Build StormLib from source
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
git clone --depth 1 https://github.com/ladislav-zezula/StormLib.git /tmp/StormLib
|
git clone --depth 1 https://github.com/ladislav-zezula/StormLib.git /tmp/StormLib
|
||||||
# Disable x86 inline asm in bundled libtomcrypt (bswapl/movl) —
|
# Disable x86 inline asm in bundled libtomcrypt (bswapl/movl) —
|
||||||
# __MINGW32__ is defined on CLANGARM64 which incorrectly enables x86 asm
|
# __MINGW32__ is defined on CLANGARM64 which incorrectly enables x86 asm
|
||||||
cmake -S /tmp/StormLib -B /tmp/StormLib/build -G Ninja \
|
cmake -S /tmp/StormLib -B /tmp/StormLib/build -G Ninja \
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
-DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" \
|
-DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" \
|
||||||
-DBUILD_SHARED_LIBS=OFF \
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
-DCMAKE_C_FLAGS="-DLTC_NO_BSWAP" \
|
-DCMAKE_C_FLAGS="-DLTC_NO_BSWAP" \
|
||||||
-DCMAKE_CXX_FLAGS="-DLTC_NO_BSWAP"
|
-DCMAKE_CXX_FLAGS="-DLTC_NO_BSWAP"
|
||||||
cmake --build /tmp/StormLib/build --parallel $(nproc)
|
cmake --build /tmp/StormLib/build --parallel $(nproc)
|
||||||
cmake --install /tmp/StormLib/build
|
cmake --install /tmp/StormLib/build
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
|
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
- name: Verify Release Config
|
- name: Verify Release Config
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
cmake -LA -N build | grep -E '^CMAKE_BUILD_TYPE:STRING=Release$'
|
cmake -LA -N build | grep -E '^CMAKE_BUILD_TYPE:STRING=Release$'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: cmake --build build --parallel $(nproc)
|
run: cmake --build build --parallel $(nproc)
|
||||||
|
|
||||||
- name: Bundle DLLs
|
- name: Bundle DLLs
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
for exe in build/bin/wowee.exe build/bin/asset_extract.exe; do
|
for exe in build/bin/wowee.exe build/bin/asset_extract.exe; do
|
||||||
[ -f "$exe" ] || continue
|
[ -f "$exe" ] || continue
|
||||||
ldd "$exe" \
|
ldd "$exe" \
|
||||||
| awk '/=> \// { print $3 }' \
|
| awk '/=> \// { print $3 }' \
|
||||||
| grep -iv '^/c/Windows' \
|
| grep -iv '^/c/Windows' \
|
||||||
| xargs -I{} sh -c 'cp -n "{}" build/bin/ 2>/dev/null || true'
|
| xargs -I{} sh -c 'cp -n "{}" build/bin/ 2>/dev/null || true'
|
||||||
done
|
done
|
||||||
|
|
||||||
- name: Package
|
- name: Package
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
TAG="${GITHUB_REF_NAME}"
|
TAG="${GITHUB_REF_NAME}"
|
||||||
STAGING="wowee-${TAG}-windows-${{ matrix.arch }}"
|
STAGING="wowee-${TAG}-windows-${{ matrix.arch }}"
|
||||||
mkdir -p "${STAGING}"
|
mkdir -p "${STAGING}"
|
||||||
|
|
||||||
# Binary and DLLs
|
# Binary and DLLs
|
||||||
cp build/bin/wowee.exe "${STAGING}/"
|
cp build/bin/wowee.exe "${STAGING}/"
|
||||||
cp build/bin/*.dll "${STAGING}/" 2>/dev/null || true
|
cp build/bin/*.dll "${STAGING}/" 2>/dev/null || true
|
||||||
|
|
||||||
# Asset extraction tool (if built — requires StormLib)
|
# Asset extraction tool (if built — requires StormLib)
|
||||||
if [ -f build/bin/asset_extract.exe ]; then
|
if [ -f build/bin/asset_extract.exe ]; then
|
||||||
cp build/bin/asset_extract.exe "${STAGING}/"
|
cp build/bin/asset_extract.exe "${STAGING}/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Extraction scripts and GUI
|
# Extraction scripts and GUI
|
||||||
cp extract_assets.ps1 "${STAGING}/"
|
cp extract_assets.ps1 "${STAGING}/"
|
||||||
cp extract_assets.bat "${STAGING}/"
|
cp extract_assets.bat "${STAGING}/"
|
||||||
cp tools/asset_pipeline_gui.py "${STAGING}/"
|
cp tools/asset_pipeline_gui.py "${STAGING}/"
|
||||||
|
|
||||||
# Assets (exclude proprietary music)
|
# Assets (exclude proprietary music)
|
||||||
mkdir -p "${STAGING}/assets"
|
mkdir -p "${STAGING}/assets"
|
||||||
for d in build/bin/assets/*/; do
|
for d in build/bin/assets/*/; do
|
||||||
dirname="$(basename "$d")"
|
dirname="$(basename "$d")"
|
||||||
[ "$dirname" = "Original Music" ] && continue
|
[ "$dirname" = "Original Music" ] && continue
|
||||||
cp -r "$d" "${STAGING}/assets/"
|
cp -r "$d" "${STAGING}/assets/"
|
||||||
done
|
done
|
||||||
# Copy top-level asset files
|
# Copy top-level asset files
|
||||||
cp build/bin/assets/* "${STAGING}/assets/" 2>/dev/null || true
|
cp build/bin/assets/* "${STAGING}/assets/" 2>/dev/null || true
|
||||||
|
|
||||||
# Data directory (git-tracked files only)
|
# Data directory (git-tracked files only)
|
||||||
git ls-files Data/ | while read -r f; do
|
git ls-files Data/ | while read -r f; do
|
||||||
mkdir -p "${STAGING}/$(dirname "$f")"
|
mkdir -p "${STAGING}/$(dirname "$f")"
|
||||||
cp "$f" "${STAGING}/$f"
|
cp "$f" "${STAGING}/$f"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Create ZIP
|
# Create ZIP
|
||||||
zip -r "${STAGING}.zip" "${STAGING}"
|
zip -r "${STAGING}.zip" "${STAGING}"
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: wowee-windows-${{ matrix.arch }}
|
name: wowee-windows-${{ matrix.arch }}
|
||||||
path: wowee-*.zip
|
path: wowee-*.zip
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Create Release
|
name: Create Release
|
||||||
needs: [ build-linux, build-macos, build-windows ]
|
needs: [build-linux, build-macos, build-windows]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download all artifacts
|
- name: Download all artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
path: artifacts/
|
path: artifacts/
|
||||||
|
|
||||||
- name: Create GitHub Release
|
- name: Create GitHub Release
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
GH_REPO: ${{ github.repository }}
|
GH_REPO: ${{ github.repository }}
|
||||||
run: |
|
run: |
|
||||||
TAG="${GITHUB_REF_NAME}"
|
TAG="${GITHUB_REF_NAME}"
|
||||||
|
|
||||||
# Collect all release files
|
# Collect all release files
|
||||||
FILES=()
|
FILES=()
|
||||||
for f in artifacts/*/*; do
|
for f in artifacts/*/*; do
|
||||||
FILES+=("$f")
|
FILES+=("$f")
|
||||||
done
|
done
|
||||||
|
|
||||||
gh release create "${TAG}" \
|
gh release create "${TAG}" \
|
||||||
--title "Wowee ${TAG}" \
|
--title "Wowee ${TAG}" \
|
||||||
--generate-notes \
|
--generate-notes \
|
||||||
"${FILES[@]}"
|
"${FILES[@]}"
|
||||||
|
|
|
||||||
3
.github/workflows/security.yml
vendored
3
.github/workflows/security.yml
vendored
|
|
@ -7,9 +7,6 @@ on:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
|
|
|
||||||
56
.gitignore
vendored
56
.gitignore
vendored
|
|
@ -1,7 +1,5 @@
|
||||||
# Build directories
|
# Build directories
|
||||||
build/
|
build/
|
||||||
build_asan/
|
|
||||||
build-debug/
|
|
||||||
build-sanitize/
|
build-sanitize/
|
||||||
bin/
|
bin/
|
||||||
lib/
|
lib/
|
||||||
|
|
@ -19,7 +17,6 @@ Makefile
|
||||||
*.obj
|
*.obj
|
||||||
*.slo
|
*.slo
|
||||||
*.lo
|
*.lo
|
||||||
*.spv
|
|
||||||
|
|
||||||
# Compiled Dynamic libraries
|
# Compiled Dynamic libraries
|
||||||
*.so
|
*.so
|
||||||
|
|
@ -37,9 +34,6 @@ Makefile
|
||||||
*.app
|
*.app
|
||||||
wowee
|
wowee
|
||||||
|
|
||||||
# Claude Code internal state
|
|
||||||
.claude/
|
|
||||||
|
|
||||||
# IDE files
|
# IDE files
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
@ -48,31 +42,12 @@ wowee
|
||||||
*~
|
*~
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# Compilation database (regenerated by cmake)
|
|
||||||
compile_commands.json
|
|
||||||
|
|
||||||
# Language server caches
|
|
||||||
.ccls
|
|
||||||
.ccls-cache/
|
|
||||||
.cache/clangd/
|
|
||||||
|
|
||||||
# Tags
|
|
||||||
tags
|
|
||||||
TAGS
|
|
||||||
.tags
|
|
||||||
cscope.out
|
|
||||||
|
|
||||||
# External dependencies (except submodules and vendored headers)
|
# External dependencies (except submodules and vendored headers)
|
||||||
extern/*
|
extern/*
|
||||||
!extern/.gitkeep
|
!extern/.gitkeep
|
||||||
!extern/catch2
|
|
||||||
!extern/imgui
|
!extern/imgui
|
||||||
!extern/vk-bootstrap
|
!extern/vk-bootstrap
|
||||||
!extern/FidelityFX-SDK
|
|
||||||
!extern/FidelityFX-FSR2
|
|
||||||
!extern/vk_mem_alloc.h
|
!extern/vk_mem_alloc.h
|
||||||
!extern/lua-5.1.5
|
|
||||||
!extern/VERSIONS.md
|
|
||||||
|
|
||||||
# ImGui state
|
# ImGui state
|
||||||
imgui.ini
|
imgui.ini
|
||||||
|
|
@ -92,9 +67,27 @@ saves/
|
||||||
wowee_[0-9][0-9][0-9][0-9]
|
wowee_[0-9][0-9][0-9][0-9]
|
||||||
|
|
||||||
# Extracted assets (run ./extract_assets.sh or .\extract_assets.ps1 to generate)
|
# Extracted assets (run ./extract_assets.sh or .\extract_assets.ps1 to generate)
|
||||||
Data/*
|
Data/db/
|
||||||
!Data/opcodes
|
Data/character/
|
||||||
|
Data/creature/
|
||||||
|
Data/terrain/
|
||||||
|
Data/world/
|
||||||
|
Data/interface/
|
||||||
|
Data/item/
|
||||||
|
Data/sound/
|
||||||
|
Data/spell/
|
||||||
|
Data/environment/
|
||||||
|
Data/misc/
|
||||||
|
Data/enUS/
|
||||||
|
Data/Character/
|
||||||
|
Data/Creature/
|
||||||
|
Data/World/
|
||||||
|
Data/manifest.json
|
||||||
|
Data/expansions/*/manifest.json
|
||||||
|
Data/expansions/*/assets/
|
||||||
|
Data/expansions/*/overlay/
|
||||||
|
Data/expansions/*/db/*.csv
|
||||||
|
Data/hd/
|
||||||
ingest/
|
ingest/
|
||||||
|
|
||||||
# Asset pipeline state and texture packs
|
# Asset pipeline state and texture packs
|
||||||
|
|
@ -107,10 +100,3 @@ node_modules/
|
||||||
# Python cache artifacts
|
# Python cache artifacts
|
||||||
tools/__pycache__/
|
tools/__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
# artifacts
|
|
||||||
.codex-loop/
|
|
||||||
|
|
||||||
# Local agent instructions
|
|
||||||
AGENTS.md
|
|
||||||
codex-loop.sh
|
|
||||||
|
|
|
||||||
10
.gitmodules
vendored
10
.gitmodules
vendored
|
|
@ -6,13 +6,3 @@
|
||||||
path = extern/vk-bootstrap
|
path = extern/vk-bootstrap
|
||||||
url = https://github.com/charles-lunarg/vk-bootstrap.git
|
url = https://github.com/charles-lunarg/vk-bootstrap.git
|
||||||
shallow = true
|
shallow = true
|
||||||
[submodule "extern/FidelityFX-SDK"]
|
|
||||||
path = extern/FidelityFX-SDK
|
|
||||||
url = https://github.com/Kelsidavis/FidelityFX-SDK.git
|
|
||||||
shallow = true
|
|
||||||
update = none
|
|
||||||
[submodule "extern/FidelityFX-FSR2"]
|
|
||||||
path = extern/FidelityFX-FSR2
|
|
||||||
url = https://github.com/Kelsidavis/FidelityFX-FSR2.git
|
|
||||||
shallow = true
|
|
||||||
ignore = dirty
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
# Vendored third-party code (frozen releases, not ours to modify)
|
|
||||||
extern/lua-5.1.5/
|
|
||||||
extern/imgui/
|
|
||||||
extern/stb_image.h
|
|
||||||
extern/stb_image_write.h
|
|
||||||
extern/vk-bootstrap/
|
|
||||||
extern/FidelityFX-FSR2/
|
|
||||||
extern/FidelityFX-SDK/
|
|
||||||
|
|
@ -12,7 +12,7 @@ This document provides platform-specific build instructions for WoWee.
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install -y \
|
sudo apt install -y \
|
||||||
build-essential cmake pkg-config git \
|
build-essential cmake pkg-config git \
|
||||||
libsdl2-dev libglm-dev \
|
libsdl2-dev libglew-dev libglm-dev \
|
||||||
libssl-dev zlib1g-dev \
|
libssl-dev zlib1g-dev \
|
||||||
libvulkan-dev vulkan-tools glslc \
|
libvulkan-dev vulkan-tools glslc \
|
||||||
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \
|
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \
|
||||||
|
|
@ -28,7 +28,7 @@ sudo apt install -y \
|
||||||
```bash
|
```bash
|
||||||
sudo pacman -S --needed \
|
sudo pacman -S --needed \
|
||||||
base-devel cmake pkgconf git \
|
base-devel cmake pkgconf git \
|
||||||
sdl2 glm openssl zlib \
|
sdl2 glew glm openssl zlib \
|
||||||
vulkan-headers vulkan-icd-loader vulkan-tools shaderc \
|
vulkan-headers vulkan-icd-loader vulkan-tools shaderc \
|
||||||
ffmpeg unicorn stormlib
|
ffmpeg unicorn stormlib
|
||||||
```
|
```
|
||||||
|
|
@ -83,7 +83,7 @@ Vulkan on macOS is provided via MoltenVK (a Vulkan-to-Metal translation layer),
|
||||||
which is included in the `vulkan-loader` Homebrew package.
|
which is included in the `vulkan-loader` Homebrew package.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
brew install cmake pkg-config sdl2 glm openssl@3 zlib ffmpeg unicorn \
|
brew install cmake pkg-config sdl2 glew glm openssl@3 zlib ffmpeg unicorn \
|
||||||
stormlib vulkan-loader vulkan-headers shaderc
|
stormlib vulkan-loader vulkan-headers shaderc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -137,6 +137,7 @@ pacman -S --needed \
|
||||||
mingw-w64-x86_64-ninja \
|
mingw-w64-x86_64-ninja \
|
||||||
mingw-w64-x86_64-pkgconf \
|
mingw-w64-x86_64-pkgconf \
|
||||||
mingw-w64-x86_64-SDL2 \
|
mingw-w64-x86_64-SDL2 \
|
||||||
|
mingw-w64-x86_64-glew \
|
||||||
mingw-w64-x86_64-glm \
|
mingw-w64-x86_64-glm \
|
||||||
mingw-w64-x86_64-openssl \
|
mingw-w64-x86_64-openssl \
|
||||||
mingw-w64-x86_64-zlib \
|
mingw-w64-x86_64-zlib \
|
||||||
|
|
@ -173,7 +174,7 @@ For users who prefer Visual Studio over MSYS2.
|
||||||
### vcpkg Dependencies
|
### vcpkg Dependencies
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
vcpkg install sdl2 glm openssl zlib ffmpeg stormlib --triplet x64-windows
|
vcpkg install sdl2 glew glm openssl zlib ffmpeg stormlib --triplet x64-windows
|
||||||
```
|
```
|
||||||
|
|
||||||
### Clone
|
### Clone
|
||||||
|
|
|
||||||
85
CHANGELOG.md
85
CHANGELOG.md
|
|
@ -1,85 +0,0 @@
|
||||||
# Changelog
|
|
||||||
|
|
||||||
## [Unreleased] — changes since v1.8.9-preview
|
|
||||||
|
|
||||||
### Architecture
|
|
||||||
- Break Application::getInstance() singleton from GameHandler via GameServices struct
|
|
||||||
- EntityController refactoring (SOLID decomposition)
|
|
||||||
- Extract 8 domain handler classes from GameHandler
|
|
||||||
- Replace 3,300-line switch with dispatch table
|
|
||||||
- Multi-platform Docker build system (Linux, macOS arm64/x86_64, Windows cross-compilation)
|
|
||||||
|
|
||||||
### Bug Fixes (v1.8.2–v1.8.9)
|
|
||||||
- Fix VkTexture ownsSampler_ flag after move/destroy (prevented double-free)
|
|
||||||
- Fix unsigned underflow in Warden PE section loading (buffer overflow on malformed modules)
|
|
||||||
- Add bounds checks to Warden readLE32/readLE16 (out-of-bounds on untrusted PE data)
|
|
||||||
- Fix undefined behavior: SDL_BUTTON(0) computed 1 << -1 (negative shift)
|
|
||||||
- Fix BigNum::toHex/toDecimal null dereference on OpenSSL allocation failure
|
|
||||||
- Remove duplicate zone weather entry silently overwriting Dustwallow Marsh
|
|
||||||
- Fix LLVM apt repo codename (jammy→noble) in macOS Docker build
|
|
||||||
- Add missing mkdir in Linux Docker build script
|
|
||||||
- Clamp player percentage stats (block/dodge/parry/crit) to prevent NaN from corrupted packets
|
|
||||||
- Guard fsPath underflow in tryLoadPngOverride
|
|
||||||
|
|
||||||
### Code Quality (v1.8.2–v1.8.9)
|
|
||||||
- 30+ named constants replacing magic numbers across game, rendering, and pipeline code
|
|
||||||
- 55+ why-comments documenting WoW protocol quirks, format specifics, and design rationale
|
|
||||||
- 8 DRY extractions (findOnUseSpellId, createFallbackTextures, finalizeSampler,
|
|
||||||
renderClassRestriction/renderRaceRestriction, and more)
|
|
||||||
- Scope macOS -undefined dynamic_lookup linker flag to wowee target only
|
|
||||||
- Replace goto patterns with structured control flow (do/while(false), lambdas)
|
|
||||||
- Zero out GameServices in Application::shutdown to prevent dangling pointers
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## [v1.8.1-preview] — 2026-03-23
|
|
||||||
|
|
||||||
### Performance
|
|
||||||
- Eliminate ~70 unnecessary sqrt ops per frame; constexpr reciprocals and cache optimizations
|
|
||||||
- Skip bone animation for LOD3 models; frustum-cull water surfaces
|
|
||||||
- Eliminate per-frame heap allocations in M2 renderer
|
|
||||||
- Convert entity/skill/DBC/warden maps to unordered_map; fix 3x contacts scan
|
|
||||||
- Eliminate double map lookups and dynamic_cast in render loops
|
|
||||||
- Use second GPU queue for parallel texture/buffer uploads
|
|
||||||
- Time-budget tile finalization to prevent 1+ second main-loop stalls
|
|
||||||
- Add Vulkan pipeline cache persistence for faster startup
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
- Fix spline parsing with expansion context; preload DBC caches at world entry
|
|
||||||
- Fix NPC/player attack animation to use weapon-appropriate anim ID
|
|
||||||
- Fix equipment visibility and follow-target run speed
|
|
||||||
- Fix inspect (packed GUID) and client-side auto-walk for follow
|
|
||||||
- Fix mail money uint64, other-player cape textures, zone toast dedup, TCP_NODELAY
|
|
||||||
- Guard spline point loop against unsigned underflow; guard hexDecode/stoi/stof
|
|
||||||
- Fix infinite recursion in toLowerInPlace and operator precedence bugs
|
|
||||||
- Fix 3D audio coords for PLAY_OBJECT_SOUND; correct melee swing sound paths
|
|
||||||
- Prevent Vulkan sampler exhaustion crash; skip pipeline cache on NVIDIA
|
|
||||||
- Skip FSR3 frame gen on non-AMD GPUs to prevent driver crash
|
|
||||||
- Fix chest GO interaction (send GAMEOBJ_USE+LOOT together)
|
|
||||||
- Restore WMO wall collision threshold; fix off-screen bag positions
|
|
||||||
- Guard texture log dedup sets with mutex for thread safety
|
|
||||||
- Fix lua_pcall return check in ACTIONBAR_PAGE_CHANGED
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- Render equipment on other players (helmets, weapons, belts, wrists, shoulders)
|
|
||||||
- Target frame right-click context menu
|
|
||||||
- Crafting sounds and Create All button
|
|
||||||
- Server-synced bag sort
|
|
||||||
- Log GPU vendor/name at init
|
|
||||||
|
|
||||||
### Security
|
|
||||||
- Add path traversal rejection and packet length validation
|
|
||||||
|
|
||||||
### Code Quality
|
|
||||||
- Packet API: add readPackedGuid, writePackedGuid, writeFloat, getRemainingSize,
|
|
||||||
hasRemaining, hasData, skipAll (replacing 1300+ verbose expressions)
|
|
||||||
- GameHandler helpers: isInWorld, isPreWotlk, guidToUnitId, lookupName,
|
|
||||||
getUnitByGuid, fireAddonEvent, withSoundManager
|
|
||||||
- Dispatch table: registerHandler, registerSkipHandler, registerWorldHandler,
|
|
||||||
registerErrorHandler (replacing 120+ lambda wrappers)
|
|
||||||
- Shared ui_colors.hpp with named constants replacing 200+ inline color literals
|
|
||||||
- Promote 50+ static const arrays to constexpr across audio/core/rendering/UI
|
|
||||||
- Deduplicate class name/color functions, enchantment cache, item-set DBC keys
|
|
||||||
- Extract settings tabs, GameHandler::update() phases, loadWeaponM2 into methods
|
|
||||||
- Remove 12 duplicate dispatch registrations and C-style casts
|
|
||||||
- Extract toHexString, toLowerInPlace, duration formatting, Lua return helpers
|
|
||||||
338
CMakeLists.txt
338
CMakeLists.txt
|
|
@ -1,5 +1,5 @@
|
||||||
cmake_minimum_required(VERSION 3.15)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
project(wowee VERSION 1.0.0 LANGUAGES C CXX)
|
project(wowee VERSION 1.0.0 LANGUAGES CXX)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
@ -25,9 +25,8 @@ endif()
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||||
option(WOWEE_BUILD_TESTS "Build tests" ON)
|
option(WOWEE_BUILD_TESTS "Build tests" OFF)
|
||||||
option(WOWEE_ENABLE_ASAN "Enable AddressSanitizer (Debug builds)" OFF)
|
option(WOWEE_ENABLE_ASAN "Enable AddressSanitizer (Debug builds)" OFF)
|
||||||
option(WOWEE_ENABLE_TRACY "Enable Tracy profiler instrumentation" OFF)
|
|
||||||
option(WOWEE_ENABLE_AMD_FSR2 "Enable AMD FidelityFX FSR2 backend when SDK is present" ON)
|
option(WOWEE_ENABLE_AMD_FSR2 "Enable AMD FidelityFX FSR2 backend when SDK is present" ON)
|
||||||
option(WOWEE_ENABLE_AMD_FSR3_FRAMEGEN "Enable AMD FidelityFX SDK FSR3 frame generation interface probe when SDK is present" ON)
|
option(WOWEE_ENABLE_AMD_FSR3_FRAMEGEN "Enable AMD FidelityFX SDK FSR3 frame generation interface probe when SDK is present" ON)
|
||||||
option(WOWEE_BUILD_AMD_FSR3_RUNTIME "Build native AMD FidelityFX VK runtime (Path A) from extern/FidelityFX-SDK/Kits" ON)
|
option(WOWEE_BUILD_AMD_FSR3_RUNTIME "Build native AMD FidelityFX VK runtime (Path A) from extern/FidelityFX-SDK/Kits" ON)
|
||||||
|
|
@ -249,98 +248,34 @@ endif()
|
||||||
find_package(SDL2 REQUIRED)
|
find_package(SDL2 REQUIRED)
|
||||||
find_package(Vulkan QUIET)
|
find_package(Vulkan QUIET)
|
||||||
if(NOT Vulkan_FOUND)
|
if(NOT Vulkan_FOUND)
|
||||||
# For Windows cross-compilation the host pkg-config finds the Linux libvulkan-dev
|
# Fallback: some distros / CMake versions need pkg-config to locate Vulkan.
|
||||||
# and injects /usr/include as an INTERFACE_INCLUDE_DIRECTORY, which causes
|
find_package(PkgConfig QUIET)
|
||||||
# MinGW clang to pull in glibc headers (bits/libc-header-start.h) instead of
|
if(PkgConfig_FOUND)
|
||||||
# the MinGW sysroot headers. Skip the host pkg-config path entirely and instead
|
pkg_check_modules(VULKAN_PKG vulkan)
|
||||||
# locate Vulkan via vcpkg-installed vulkan-headers or the MinGW toolchain.
|
if(VULKAN_PKG_FOUND)
|
||||||
if(CMAKE_CROSSCOMPILING AND WIN32)
|
add_library(Vulkan::Vulkan INTERFACE IMPORTED)
|
||||||
# The cross-compile build script generates a Vulkan import library
|
set_target_properties(Vulkan::Vulkan PROPERTIES
|
||||||
# (libvulkan-1.a) in ${CMAKE_BINARY_DIR}/vulkan-import from the headers.
|
INTERFACE_INCLUDE_DIRECTORIES "${VULKAN_PKG_INCLUDE_DIRS}"
|
||||||
set(_VULKAN_IMPORT_DIR "${CMAKE_BINARY_DIR}/vulkan-import")
|
INTERFACE_LINK_LIBRARIES "${VULKAN_PKG_LIBRARIES}"
|
||||||
|
)
|
||||||
find_package(VulkanHeaders CONFIG QUIET)
|
if(VULKAN_PKG_LIBRARY_DIRS)
|
||||||
if(VulkanHeaders_FOUND)
|
|
||||||
if(NOT TARGET Vulkan::Vulkan)
|
|
||||||
add_library(Vulkan::Vulkan INTERFACE IMPORTED)
|
|
||||||
endif()
|
|
||||||
# Vulkan::Headers is provided by vcpkg's vulkan-headers port and carries
|
|
||||||
# the correct MinGW include path — no Linux system headers involved.
|
|
||||||
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
|
|
||||||
INTERFACE_LINK_LIBRARIES Vulkan::Headers)
|
|
||||||
# Link against the Vulkan loader import library (vulkan-1.dll).
|
|
||||||
if(EXISTS "${_VULKAN_IMPORT_DIR}/libvulkan-1.a")
|
|
||||||
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
|
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
|
||||||
INTERFACE_LINK_DIRECTORIES "${_VULKAN_IMPORT_DIR}")
|
INTERFACE_LINK_DIRECTORIES "${VULKAN_PKG_LIBRARY_DIRS}")
|
||||||
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
|
|
||||||
INTERFACE_LINK_LIBRARIES vulkan-1)
|
|
||||||
endif()
|
endif()
|
||||||
set(Vulkan_FOUND TRUE)
|
set(Vulkan_FOUND TRUE)
|
||||||
message(STATUS "Found Vulkan headers for Windows cross-compile via vcpkg VulkanHeaders")
|
message(STATUS "Found Vulkan via pkg-config: ${VULKAN_PKG_LIBRARIES}")
|
||||||
else()
|
|
||||||
# Last-resort: check the LLVM-MinGW toolchain sysroot directly.
|
|
||||||
find_path(_VULKAN_MINGW_INCLUDE NAMES vulkan/vulkan.h
|
|
||||||
PATHS /opt/llvm-mingw/x86_64-w64-mingw32/include NO_DEFAULT_PATH)
|
|
||||||
if(_VULKAN_MINGW_INCLUDE)
|
|
||||||
if(NOT TARGET Vulkan::Vulkan)
|
|
||||||
add_library(Vulkan::Vulkan INTERFACE IMPORTED)
|
|
||||||
endif()
|
|
||||||
set_target_properties(Vulkan::Vulkan PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${_VULKAN_MINGW_INCLUDE}")
|
|
||||||
# Link against the Vulkan loader import library (vulkan-1.dll).
|
|
||||||
if(EXISTS "${_VULKAN_IMPORT_DIR}/libvulkan-1.a")
|
|
||||||
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
|
|
||||||
INTERFACE_LINK_DIRECTORIES "${_VULKAN_IMPORT_DIR}")
|
|
||||||
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
|
|
||||||
INTERFACE_LINK_LIBRARIES vulkan-1)
|
|
||||||
endif()
|
|
||||||
set(Vulkan_FOUND TRUE)
|
|
||||||
message(STATUS "Found Vulkan headers in LLVM-MinGW sysroot: ${_VULKAN_MINGW_INCLUDE}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
elseif(CMAKE_CROSSCOMPILING AND CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
|
||||||
# macOS cross-compilation: use vcpkg-installed vulkan-headers.
|
|
||||||
# The host pkg-config would find Linux libvulkan-dev headers which the
|
|
||||||
# macOS cross-compiler cannot use (different sysroot).
|
|
||||||
find_package(VulkanHeaders CONFIG QUIET)
|
|
||||||
if(VulkanHeaders_FOUND)
|
|
||||||
if(NOT TARGET Vulkan::Vulkan)
|
|
||||||
add_library(Vulkan::Vulkan INTERFACE IMPORTED)
|
|
||||||
endif()
|
|
||||||
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
|
|
||||||
INTERFACE_LINK_LIBRARIES Vulkan::Headers)
|
|
||||||
set(Vulkan_FOUND TRUE)
|
|
||||||
message(STATUS "Found Vulkan headers for macOS cross-compile via vcpkg VulkanHeaders")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
# Fallback: some distros / CMake versions need pkg-config to locate Vulkan.
|
|
||||||
find_package(PkgConfig QUIET)
|
|
||||||
if(PkgConfig_FOUND)
|
|
||||||
pkg_check_modules(VULKAN_PKG vulkan)
|
|
||||||
if(VULKAN_PKG_FOUND)
|
|
||||||
add_library(Vulkan::Vulkan INTERFACE IMPORTED)
|
|
||||||
set_target_properties(Vulkan::Vulkan PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${VULKAN_PKG_INCLUDE_DIRS}"
|
|
||||||
INTERFACE_LINK_LIBRARIES "${VULKAN_PKG_LIBRARIES}"
|
|
||||||
)
|
|
||||||
if(VULKAN_PKG_LIBRARY_DIRS)
|
|
||||||
set_property(TARGET Vulkan::Vulkan APPEND PROPERTY
|
|
||||||
INTERFACE_LINK_DIRECTORIES "${VULKAN_PKG_LIBRARY_DIRS}")
|
|
||||||
endif()
|
|
||||||
set(Vulkan_FOUND TRUE)
|
|
||||||
message(STATUS "Found Vulkan via pkg-config: ${VULKAN_PKG_LIBRARIES}")
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
if(NOT Vulkan_FOUND)
|
if(NOT Vulkan_FOUND)
|
||||||
message(FATAL_ERROR "Could not find Vulkan. Install libvulkan-dev (Linux), vulkan-loader (macOS), or the Vulkan SDK (Windows).")
|
message(FATAL_ERROR "Could not find Vulkan. Install libvulkan-dev (Linux), vulkan-loader (macOS), or the Vulkan SDK (Windows).")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
# macOS cross-compilation: the Vulkan loader (MoltenVK) is not available at link
|
# GL/GLEW kept temporarily for unconverted sub-renderers during Vulkan migration.
|
||||||
# time. Allow unresolved Vulkan symbols — they are resolved at runtime.
|
# These files compile against GL types but their code is never called — the Vulkan
|
||||||
if(CMAKE_CROSSCOMPILING AND CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
# path is the only active rendering backend. Remove in Phase 7 when all renderers
|
||||||
set(WOWEE_MACOS_CROSS_COMPILE TRUE)
|
# are converted and grep confirms zero GL references.
|
||||||
endif()
|
find_package(OpenGL QUIET)
|
||||||
|
find_package(GLEW QUIET)
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
find_package(ZLIB REQUIRED)
|
find_package(ZLIB REQUIRED)
|
||||||
|
|
@ -487,26 +422,11 @@ endif()
|
||||||
set(WOWEE_SOURCES
|
set(WOWEE_SOURCES
|
||||||
# Core
|
# Core
|
||||||
src/core/application.cpp
|
src/core/application.cpp
|
||||||
src/core/entity_spawner.cpp
|
|
||||||
src/core/entity_spawner_player.cpp
|
|
||||||
src/core/entity_spawner_processing.cpp
|
|
||||||
src/core/appearance_composer.cpp
|
|
||||||
src/core/world_loader.cpp
|
|
||||||
src/core/npc_interaction_callback_handler.cpp
|
|
||||||
src/core/audio_callback_handler.cpp
|
|
||||||
src/core/entity_spawn_callback_handler.cpp
|
|
||||||
src/core/animation_callback_handler.cpp
|
|
||||||
src/core/transport_callback_handler.cpp
|
|
||||||
src/core/world_entry_callback_handler.cpp
|
|
||||||
src/core/ui_screen_callback_handler.cpp
|
|
||||||
src/core/window.cpp
|
src/core/window.cpp
|
||||||
src/core/input.cpp
|
src/core/input.cpp
|
||||||
src/core/logger.cpp
|
src/core/logger.cpp
|
||||||
src/core/memory_monitor.cpp
|
src/core/memory_monitor.cpp
|
||||||
|
|
||||||
# Math
|
|
||||||
src/math/spline.cpp
|
|
||||||
|
|
||||||
# Network
|
# Network
|
||||||
src/network/socket.cpp
|
src/network/socket.cpp
|
||||||
src/network/packet.cpp
|
src/network/packet.cpp
|
||||||
|
|
@ -530,35 +450,16 @@ set(WOWEE_SOURCES
|
||||||
src/game/opcode_table.cpp
|
src/game/opcode_table.cpp
|
||||||
src/game/update_field_table.cpp
|
src/game/update_field_table.cpp
|
||||||
src/game/game_handler.cpp
|
src/game/game_handler.cpp
|
||||||
src/game/game_handler_packets.cpp
|
|
||||||
src/game/game_handler_callbacks.cpp
|
|
||||||
src/game/chat_handler.cpp
|
|
||||||
src/game/movement_handler.cpp
|
|
||||||
src/game/combat_handler.cpp
|
|
||||||
src/game/spell_handler.cpp
|
|
||||||
src/game/inventory_handler.cpp
|
|
||||||
src/game/social_handler.cpp
|
|
||||||
src/game/quest_handler.cpp
|
|
||||||
src/game/entity_controller.cpp
|
|
||||||
src/game/warden_handler.cpp
|
|
||||||
src/game/warden_crypto.cpp
|
src/game/warden_crypto.cpp
|
||||||
src/game/warden_module.cpp
|
src/game/warden_module.cpp
|
||||||
src/game/warden_emulator.cpp
|
src/game/warden_emulator.cpp
|
||||||
src/game/warden_memory.cpp
|
src/game/warden_memory.cpp
|
||||||
src/game/transport_manager.cpp
|
src/game/transport_manager.cpp
|
||||||
src/game/transport_path_repository.cpp
|
|
||||||
src/game/transport_clock_sync.cpp
|
|
||||||
src/game/transport_animator.cpp
|
|
||||||
src/game/world.cpp
|
src/game/world.cpp
|
||||||
src/game/player.cpp
|
src/game/player.cpp
|
||||||
src/game/entity.cpp
|
src/game/entity.cpp
|
||||||
src/game/opcodes.cpp
|
src/game/opcodes.cpp
|
||||||
src/game/world_packets.cpp
|
src/game/world_packets.cpp
|
||||||
src/game/world_packets_social.cpp
|
|
||||||
src/game/world_packets_entity.cpp
|
|
||||||
src/game/world_packets_world.cpp
|
|
||||||
src/game/world_packets_economy.cpp
|
|
||||||
src/game/spline_packet.cpp
|
|
||||||
src/game/packet_parsers_tbc.cpp
|
src/game/packet_parsers_tbc.cpp
|
||||||
src/game/packet_parsers_classic.cpp
|
src/game/packet_parsers_classic.cpp
|
||||||
src/game/character.cpp
|
src/game/character.cpp
|
||||||
|
|
@ -567,7 +468,6 @@ set(WOWEE_SOURCES
|
||||||
|
|
||||||
# Audio
|
# Audio
|
||||||
src/audio/audio_engine.cpp
|
src/audio/audio_engine.cpp
|
||||||
src/audio/audio_coordinator.cpp
|
|
||||||
src/audio/music_manager.cpp
|
src/audio/music_manager.cpp
|
||||||
src/audio/footstep_manager.cpp
|
src/audio/footstep_manager.cpp
|
||||||
src/audio/activity_sound_manager.cpp
|
src/audio/activity_sound_manager.cpp
|
||||||
|
|
@ -605,9 +505,12 @@ set(WOWEE_SOURCES
|
||||||
# Rendering
|
# Rendering
|
||||||
src/rendering/renderer.cpp
|
src/rendering/renderer.cpp
|
||||||
src/rendering/amd_fsr3_runtime.cpp
|
src/rendering/amd_fsr3_runtime.cpp
|
||||||
|
src/rendering/shader.cpp
|
||||||
|
src/rendering/mesh.cpp
|
||||||
src/rendering/camera.cpp
|
src/rendering/camera.cpp
|
||||||
src/rendering/camera_controller.cpp
|
src/rendering/camera_controller.cpp
|
||||||
src/rendering/material.cpp
|
src/rendering/material.cpp
|
||||||
|
src/rendering/scene.cpp
|
||||||
src/rendering/terrain_renderer.cpp
|
src/rendering/terrain_renderer.cpp
|
||||||
src/rendering/terrain_manager.cpp
|
src/rendering/terrain_manager.cpp
|
||||||
src/rendering/frustum.cpp
|
src/rendering/frustum.cpp
|
||||||
|
|
@ -626,53 +529,15 @@ set(WOWEE_SOURCES
|
||||||
src/rendering/character_preview.cpp
|
src/rendering/character_preview.cpp
|
||||||
src/rendering/wmo_renderer.cpp
|
src/rendering/wmo_renderer.cpp
|
||||||
src/rendering/m2_renderer.cpp
|
src/rendering/m2_renderer.cpp
|
||||||
src/rendering/m2_renderer_render.cpp
|
|
||||||
src/rendering/m2_renderer_particles.cpp
|
|
||||||
src/rendering/m2_renderer_instance.cpp
|
|
||||||
src/rendering/m2_model_classifier.cpp
|
|
||||||
src/rendering/render_graph.cpp
|
|
||||||
src/rendering/hiz_system.cpp
|
|
||||||
src/rendering/quest_marker_renderer.cpp
|
src/rendering/quest_marker_renderer.cpp
|
||||||
src/rendering/minimap.cpp
|
src/rendering/minimap.cpp
|
||||||
src/rendering/world_map/coordinate_projection.cpp
|
src/rendering/world_map.cpp
|
||||||
src/rendering/world_map/map_resolver.cpp
|
|
||||||
src/rendering/world_map/exploration_state.cpp
|
|
||||||
src/rendering/world_map/zone_metadata.cpp
|
|
||||||
src/rendering/world_map/data_repository.cpp
|
|
||||||
src/rendering/world_map/view_state_machine.cpp
|
|
||||||
src/rendering/world_map/composite_renderer.cpp
|
|
||||||
src/rendering/world_map/overlay_renderer.cpp
|
|
||||||
src/rendering/world_map/input_handler.cpp
|
|
||||||
src/rendering/world_map/world_map_facade.cpp
|
|
||||||
src/rendering/world_map/layers/player_marker_layer.cpp
|
|
||||||
src/rendering/world_map/layers/party_dot_layer.cpp
|
|
||||||
src/rendering/world_map/layers/taxi_node_layer.cpp
|
|
||||||
src/rendering/world_map/layers/poi_marker_layer.cpp
|
|
||||||
src/rendering/world_map/layers/quest_poi_layer.cpp
|
|
||||||
src/rendering/world_map/layers/corpse_marker_layer.cpp
|
|
||||||
src/rendering/world_map/layers/zone_highlight_layer.cpp
|
|
||||||
src/rendering/world_map/layers/coordinate_display.cpp
|
|
||||||
src/rendering/world_map/layers/subzone_tooltip_layer.cpp
|
|
||||||
src/rendering/swim_effects.cpp
|
src/rendering/swim_effects.cpp
|
||||||
src/rendering/mount_dust.cpp
|
src/rendering/mount_dust.cpp
|
||||||
src/rendering/levelup_effect.cpp
|
src/rendering/levelup_effect.cpp
|
||||||
src/rendering/charge_effect.cpp
|
src/rendering/charge_effect.cpp
|
||||||
src/rendering/spell_visual_system.cpp
|
|
||||||
src/rendering/post_process_pipeline.cpp
|
|
||||||
src/rendering/overlay_system.cpp
|
|
||||||
src/rendering/animation_controller.cpp
|
|
||||||
src/rendering/animation/animation_ids.cpp
|
|
||||||
src/rendering/animation/emote_registry.cpp
|
|
||||||
src/rendering/animation/footstep_driver.cpp
|
|
||||||
src/rendering/animation/sfx_state_driver.cpp
|
|
||||||
src/rendering/animation/anim_capability_probe.cpp
|
|
||||||
src/rendering/animation/locomotion_fsm.cpp
|
|
||||||
src/rendering/animation/combat_fsm.cpp
|
|
||||||
src/rendering/animation/activity_fsm.cpp
|
|
||||||
src/rendering/animation/mount_fsm.cpp
|
|
||||||
src/rendering/animation/character_animator.cpp # Renamed from player_animator.cpp; npc_animator.cpp removed
|
|
||||||
src/rendering/animation/animation_manager.cpp
|
|
||||||
src/rendering/loading_screen.cpp
|
src/rendering/loading_screen.cpp
|
||||||
|
$<$<BOOL:${HAVE_FFMPEG}>:${CMAKE_CURRENT_SOURCE_DIR}/src/rendering/video_player.cpp>
|
||||||
|
|
||||||
# UI
|
# UI
|
||||||
src/ui/ui_manager.cpp
|
src/ui/ui_manager.cpp
|
||||||
|
|
@ -681,60 +546,12 @@ set(WOWEE_SOURCES
|
||||||
src/ui/character_create_screen.cpp
|
src/ui/character_create_screen.cpp
|
||||||
src/ui/character_screen.cpp
|
src/ui/character_screen.cpp
|
||||||
src/ui/game_screen.cpp
|
src/ui/game_screen.cpp
|
||||||
src/ui/game_screen_frames.cpp
|
|
||||||
src/ui/game_screen_hud.cpp
|
|
||||||
src/ui/game_screen_minimap.cpp
|
|
||||||
src/ui/chat_panel.cpp
|
|
||||||
src/ui/chat/chat_settings.cpp
|
|
||||||
src/ui/chat/chat_input.cpp
|
|
||||||
src/ui/chat/chat_utils.cpp
|
|
||||||
src/ui/chat/chat_tab_manager.cpp
|
|
||||||
src/ui/chat/chat_bubble_manager.cpp
|
|
||||||
src/ui/chat/chat_markup_parser.cpp
|
|
||||||
src/ui/chat/chat_markup_renderer.cpp
|
|
||||||
src/ui/chat/item_tooltip_renderer.cpp
|
|
||||||
src/ui/chat/chat_command_registry.cpp
|
|
||||||
src/ui/chat/chat_tab_completer.cpp
|
|
||||||
src/ui/chat/macro_evaluator.cpp
|
|
||||||
src/ui/chat/macro_eval_convenience.cpp
|
|
||||||
src/ui/chat/game_state_adapter.cpp
|
|
||||||
src/ui/chat/input_modifier_adapter.cpp
|
|
||||||
src/ui/chat/commands/system_commands.cpp
|
|
||||||
src/ui/chat/commands/social_commands.cpp
|
|
||||||
src/ui/chat/commands/channel_commands.cpp
|
|
||||||
src/ui/chat/commands/combat_commands.cpp
|
|
||||||
src/ui/chat/commands/group_commands.cpp
|
|
||||||
src/ui/chat/commands/guild_commands.cpp
|
|
||||||
src/ui/chat/commands/target_commands.cpp
|
|
||||||
src/ui/chat/commands/emote_commands.cpp
|
|
||||||
src/ui/chat/commands/misc_commands.cpp
|
|
||||||
src/ui/chat/commands/help_commands.cpp
|
|
||||||
src/ui/chat/commands/gm_commands.cpp
|
|
||||||
src/ui/toast_manager.cpp
|
|
||||||
src/ui/dialog_manager.cpp
|
|
||||||
src/ui/settings_panel.cpp
|
|
||||||
src/ui/combat_ui.cpp
|
|
||||||
src/ui/social_panel.cpp
|
|
||||||
src/ui/action_bar_panel.cpp
|
|
||||||
src/ui/window_manager.cpp
|
|
||||||
src/ui/inventory_screen.cpp
|
src/ui/inventory_screen.cpp
|
||||||
src/ui/quest_log_screen.cpp
|
src/ui/quest_log_screen.cpp
|
||||||
src/ui/spellbook_screen.cpp
|
src/ui/spellbook_screen.cpp
|
||||||
src/ui/talent_screen.cpp
|
src/ui/talent_screen.cpp
|
||||||
src/ui/keybinding_manager.cpp
|
src/ui/keybinding_manager.cpp
|
||||||
|
|
||||||
# Addons
|
|
||||||
src/addons/addon_manager.cpp
|
|
||||||
src/addons/lua_engine.cpp
|
|
||||||
src/addons/lua_unit_api.cpp
|
|
||||||
src/addons/lua_spell_api.cpp
|
|
||||||
src/addons/lua_inventory_api.cpp
|
|
||||||
src/addons/lua_quest_api.cpp
|
|
||||||
src/addons/lua_social_api.cpp
|
|
||||||
src/addons/lua_system_api.cpp
|
|
||||||
src/addons/lua_action_api.cpp
|
|
||||||
src/addons/toc_parser.cpp
|
|
||||||
|
|
||||||
# Main
|
# Main
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
)
|
)
|
||||||
|
|
@ -802,9 +619,12 @@ set(WOWEE_HEADERS
|
||||||
include/rendering/vk_pipeline.hpp
|
include/rendering/vk_pipeline.hpp
|
||||||
include/rendering/vk_render_target.hpp
|
include/rendering/vk_render_target.hpp
|
||||||
include/rendering/renderer.hpp
|
include/rendering/renderer.hpp
|
||||||
|
include/rendering/shader.hpp
|
||||||
|
include/rendering/mesh.hpp
|
||||||
include/rendering/camera.hpp
|
include/rendering/camera.hpp
|
||||||
include/rendering/camera_controller.hpp
|
include/rendering/camera_controller.hpp
|
||||||
include/rendering/material.hpp
|
include/rendering/material.hpp
|
||||||
|
include/rendering/scene.hpp
|
||||||
include/rendering/terrain_renderer.hpp
|
include/rendering/terrain_renderer.hpp
|
||||||
include/rendering/terrain_manager.hpp
|
include/rendering/terrain_manager.hpp
|
||||||
include/rendering/frustum.hpp
|
include/rendering/frustum.hpp
|
||||||
|
|
@ -819,30 +639,11 @@ set(WOWEE_HEADERS
|
||||||
include/rendering/lightning.hpp
|
include/rendering/lightning.hpp
|
||||||
include/rendering/swim_effects.hpp
|
include/rendering/swim_effects.hpp
|
||||||
include/rendering/world_map.hpp
|
include/rendering/world_map.hpp
|
||||||
include/rendering/world_map/world_map_types.hpp
|
|
||||||
include/rendering/world_map/coordinate_projection.hpp
|
|
||||||
include/rendering/world_map/map_resolver.hpp
|
|
||||||
include/rendering/world_map/exploration_state.hpp
|
|
||||||
include/rendering/world_map/zone_metadata.hpp
|
|
||||||
include/rendering/world_map/data_repository.hpp
|
|
||||||
include/rendering/world_map/view_state_machine.hpp
|
|
||||||
include/rendering/world_map/composite_renderer.hpp
|
|
||||||
include/rendering/world_map/overlay_renderer.hpp
|
|
||||||
include/rendering/world_map/input_handler.hpp
|
|
||||||
include/rendering/world_map/world_map_facade.hpp
|
|
||||||
include/rendering/world_map/layers/player_marker_layer.hpp
|
|
||||||
include/rendering/world_map/layers/party_dot_layer.hpp
|
|
||||||
include/rendering/world_map/layers/taxi_node_layer.hpp
|
|
||||||
include/rendering/world_map/layers/poi_marker_layer.hpp
|
|
||||||
include/rendering/world_map/layers/quest_poi_layer.hpp
|
|
||||||
include/rendering/world_map/layers/corpse_marker_layer.hpp
|
|
||||||
include/rendering/world_map/layers/zone_highlight_layer.hpp
|
|
||||||
include/rendering/world_map/layers/coordinate_display.hpp
|
|
||||||
include/rendering/world_map/layers/subzone_tooltip_layer.hpp
|
|
||||||
include/rendering/character_renderer.hpp
|
include/rendering/character_renderer.hpp
|
||||||
include/rendering/character_preview.hpp
|
include/rendering/character_preview.hpp
|
||||||
include/rendering/wmo_renderer.hpp
|
include/rendering/wmo_renderer.hpp
|
||||||
include/rendering/loading_screen.hpp
|
include/rendering/loading_screen.hpp
|
||||||
|
include/rendering/video_player.hpp
|
||||||
|
|
||||||
include/ui/ui_manager.hpp
|
include/ui/ui_manager.hpp
|
||||||
include/ui/auth_screen.hpp
|
include/ui/auth_screen.hpp
|
||||||
|
|
@ -858,59 +659,20 @@ set(WOWEE_HEADERS
|
||||||
|
|
||||||
set(WOWEE_PLATFORM_SOURCES)
|
set(WOWEE_PLATFORM_SOURCES)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
# Copy icon into build tree so windres can find it via the relative path
|
# Copy icon into build tree so llvm-rc can find it via the relative path in wowee.rc
|
||||||
# in wowee.rc ("assets\\wowee.ico"). Tell the RC compiler to also search
|
|
||||||
# the build directory — GNU windres uses cwd (already the build dir) but
|
|
||||||
# llvm-windres resolves relative to the .rc file, so it needs the hint.
|
|
||||||
configure_file(
|
configure_file(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/assets/Wowee.ico
|
${CMAKE_CURRENT_SOURCE_DIR}/assets/Wowee.ico
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/assets/wowee.ico
|
${CMAKE_CURRENT_BINARY_DIR}/assets/wowee.ico
|
||||||
COPYONLY
|
COPYONLY
|
||||||
)
|
)
|
||||||
set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -I ${CMAKE_CURRENT_BINARY_DIR} -I ${CMAKE_CURRENT_SOURCE_DIR}")
|
|
||||||
list(APPEND WOWEE_PLATFORM_SOURCES resources/wowee.rc)
|
list(APPEND WOWEE_PLATFORM_SOURCES resources/wowee.rc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ---- Lua 5.1.5 (vendored, static library) ----
|
|
||||||
set(LUA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/lua-5.1.5/src)
|
|
||||||
set(LUA_SOURCES
|
|
||||||
${LUA_DIR}/lapi.c ${LUA_DIR}/lcode.c ${LUA_DIR}/ldebug.c
|
|
||||||
${LUA_DIR}/ldo.c ${LUA_DIR}/ldump.c ${LUA_DIR}/lfunc.c
|
|
||||||
${LUA_DIR}/lgc.c ${LUA_DIR}/llex.c ${LUA_DIR}/lmem.c
|
|
||||||
${LUA_DIR}/lobject.c ${LUA_DIR}/lopcodes.c ${LUA_DIR}/lparser.c
|
|
||||||
${LUA_DIR}/lstate.c ${LUA_DIR}/lstring.c ${LUA_DIR}/ltable.c
|
|
||||||
${LUA_DIR}/ltm.c ${LUA_DIR}/lundump.c ${LUA_DIR}/lvm.c
|
|
||||||
${LUA_DIR}/lzio.c ${LUA_DIR}/lauxlib.c ${LUA_DIR}/lbaselib.c
|
|
||||||
${LUA_DIR}/ldblib.c ${LUA_DIR}/liolib.c ${LUA_DIR}/lmathlib.c
|
|
||||||
${LUA_DIR}/loslib.c ${LUA_DIR}/ltablib.c ${LUA_DIR}/lstrlib.c
|
|
||||||
${LUA_DIR}/linit.c
|
|
||||||
)
|
|
||||||
add_library(lua51 STATIC ${LUA_SOURCES})
|
|
||||||
set_target_properties(lua51 PROPERTIES LINKER_LANGUAGE C C_STANDARD 99 POSITION_INDEPENDENT_CODE ON)
|
|
||||||
target_include_directories(lua51 PUBLIC ${LUA_DIR})
|
|
||||||
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
|
|
||||||
target_compile_options(lua51 PRIVATE -w)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Create executable
|
# Create executable
|
||||||
add_executable(wowee ${WOWEE_SOURCES} ${WOWEE_HEADERS} ${WOWEE_PLATFORM_SOURCES})
|
add_executable(wowee ${WOWEE_SOURCES} ${WOWEE_HEADERS} ${WOWEE_PLATFORM_SOURCES})
|
||||||
|
|
||||||
# Tracy profiler — zero overhead when WOWEE_ENABLE_TRACY is OFF
|
|
||||||
if(WOWEE_ENABLE_TRACY)
|
|
||||||
target_sources(wowee PRIVATE ${CMAKE_SOURCE_DIR}/extern/tracy/public/TracyClient.cpp)
|
|
||||||
target_compile_definitions(wowee PRIVATE TRACY_ENABLE)
|
|
||||||
target_include_directories(wowee SYSTEM PRIVATE ${CMAKE_SOURCE_DIR}/extern/tracy/public)
|
|
||||||
message(STATUS "Tracy profiler: ENABLED")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(TARGET opcodes-generate)
|
if(TARGET opcodes-generate)
|
||||||
add_dependencies(wowee opcodes-generate)
|
add_dependencies(wowee opcodes-generate)
|
||||||
endif()
|
endif()
|
||||||
# macOS cross-compilation: MoltenVK is not available at link time.
|
|
||||||
# Allow unresolved Vulkan symbols — resolved at runtime. Scoped to wowee only.
|
|
||||||
if(WOWEE_MACOS_CROSS_COMPILE)
|
|
||||||
target_link_options(wowee PRIVATE "-undefined" "dynamic_lookup")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# FidelityFX-SDK headers can trigger compiler-specific pragma/unused-static noise
|
# FidelityFX-SDK headers can trigger compiler-specific pragma/unused-static noise
|
||||||
# when included through the runtime bridge; keep suppression scoped to that TU.
|
# when included through the runtime bridge; keep suppression scoped to that TU.
|
||||||
|
|
@ -947,10 +709,17 @@ target_link_libraries(wowee PRIVATE
|
||||||
OpenSSL::Crypto
|
OpenSSL::Crypto
|
||||||
Threads::Threads
|
Threads::Threads
|
||||||
ZLIB::ZLIB
|
ZLIB::ZLIB
|
||||||
lua51
|
|
||||||
${CMAKE_DL_LIBS}
|
${CMAKE_DL_LIBS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# GL/GLEW linked temporarily for unconverted sub-renderers (removed in Phase 7)
|
||||||
|
if(TARGET OpenGL::GL)
|
||||||
|
target_link_libraries(wowee PRIVATE OpenGL::GL)
|
||||||
|
endif()
|
||||||
|
if(TARGET GLEW::GLEW)
|
||||||
|
target_link_libraries(wowee PRIVATE GLEW::GLEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(HAVE_FFMPEG)
|
if(HAVE_FFMPEG)
|
||||||
target_compile_definitions(wowee PRIVATE HAVE_FFMPEG)
|
target_compile_definitions(wowee PRIVATE HAVE_FFMPEG)
|
||||||
target_link_libraries(wowee PRIVATE ${FFMPEG_LIBRARIES})
|
target_link_libraries(wowee PRIVATE ${FFMPEG_LIBRARIES})
|
||||||
|
|
@ -1040,20 +809,12 @@ else()
|
||||||
# -g3 — maximum DWARF debug info (includes macro definitions)
|
# -g3 — maximum DWARF debug info (includes macro definitions)
|
||||||
# -Og — optimise for debugging (better than -O0, keeps most frames)
|
# -Og — optimise for debugging (better than -O0, keeps most frames)
|
||||||
# -fno-omit-frame-pointer — preserve frame pointers so stack traces are clean
|
# -fno-omit-frame-pointer — preserve frame pointers so stack traces are clean
|
||||||
# Frame pointers in all configs (negligible perf cost, critical for crash backtraces)
|
|
||||||
target_compile_options(wowee PRIVATE
|
target_compile_options(wowee PRIVATE
|
||||||
-fno-omit-frame-pointer
|
$<$<CONFIG:Debug>:-g3 -Og -fno-omit-frame-pointer>
|
||||||
$<$<CONFIG:Debug>:-g3 -Og>
|
$<$<CONFIG:RelWithDebInfo>:-g -fno-omit-frame-pointer>
|
||||||
$<$<CONFIG:RelWithDebInfo>:-g>
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ── Unit tests (Catch2) ──────────────────────────────────────
|
|
||||||
if(WOWEE_BUILD_TESTS)
|
|
||||||
enable_testing()
|
|
||||||
add_subdirectory(tests)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# AddressSanitizer — catch buffer overflows, use-after-free, etc.
|
# AddressSanitizer — catch buffer overflows, use-after-free, etc.
|
||||||
# Enable with: cmake ... -DWOWEE_ENABLE_ASAN=ON -DCMAKE_BUILD_TYPE=Debug
|
# Enable with: cmake ... -DWOWEE_ENABLE_ASAN=ON -DCMAKE_BUILD_TYPE=Debug
|
||||||
if(WOWEE_ENABLE_ASAN)
|
if(WOWEE_ENABLE_ASAN)
|
||||||
|
|
@ -1065,10 +826,10 @@ if(WOWEE_ENABLE_ASAN)
|
||||||
$<$<CONFIG:Release>:/MD>
|
$<$<CONFIG:Release>:/MD>
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
target_compile_options(wowee PRIVATE -fsanitize=address,undefined -fno-omit-frame-pointer)
|
target_compile_options(wowee PRIVATE -fsanitize=address -fno-omit-frame-pointer)
|
||||||
target_link_options(wowee PRIVATE -fsanitize=address,undefined)
|
target_link_options(wowee PRIVATE -fsanitize=address)
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "AddressSanitizer + UBSan: ENABLED")
|
message(STATUS "AddressSanitizer: ENABLED")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Release build optimizations
|
# Release build optimizations
|
||||||
|
|
@ -1095,17 +856,6 @@ add_custom_command(TARGET wowee POST_BUILD
|
||||||
COMMENT "Syncing assets to $<TARGET_FILE_DIR:wowee>/assets"
|
COMMENT "Syncing assets to $<TARGET_FILE_DIR:wowee>/assets"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Symlink Data/ next to the executable so expansion profiles, opcode tables,
|
|
||||||
# and other runtime data files are found when running from the build directory.
|
|
||||||
if(NOT WIN32)
|
|
||||||
add_custom_command(TARGET wowee POST_BUILD
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E create_symlink
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Data
|
|
||||||
$<TARGET_FILE_DIR:wowee>/Data
|
|
||||||
COMMENT "Symlinking Data to $<TARGET_FILE_DIR:wowee>/Data"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# On Windows, SDL 2.28+ uses LoadLibraryExW with LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
|
# On Windows, SDL 2.28+ uses LoadLibraryExW with LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
|
||||||
# which does NOT include System32. Copy vulkan-1.dll into the output directory so
|
# which does NOT include System32. Copy vulkan-1.dll into the output directory so
|
||||||
# SDL_Vulkan_LoadLibrary can locate it without needing a full system PATH search.
|
# SDL_Vulkan_LoadLibrary can locate it without needing a full system PATH search.
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
# Contributing to Wowee
|
|
||||||
|
|
||||||
## Build Setup
|
|
||||||
|
|
||||||
See [BUILD_INSTRUCTIONS.md](BUILD_INSTRUCTIONS.md) for full platform-specific details.
|
|
||||||
The short version: CMake + Make on Linux/macOS, MSYS2 on Windows.
|
|
||||||
|
|
||||||
```
|
|
||||||
cmake -B build -DCMAKE_BUILD_TYPE=Debug
|
|
||||||
make -C build -j$(nproc)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Code Style
|
|
||||||
|
|
||||||
- **C++20**. Use `#pragma once` for include guards.
|
|
||||||
- Namespaces: `wowee::game`, `wowee::rendering`, `wowee::ui`, `wowee::core`, `wowee::network`.
|
|
||||||
- Conventional commit messages in imperative mood:
|
|
||||||
- `feat:` new feature
|
|
||||||
- `fix:` bug fix
|
|
||||||
- `refactor:` code restructuring with no behavior change
|
|
||||||
- `perf:` performance improvement
|
|
||||||
- Prefer `constexpr` over `static const` for compile-time data.
|
|
||||||
- Mark functions whose return value should not be ignored with `[[nodiscard]]`.
|
|
||||||
|
|
||||||
## Pull Request Process
|
|
||||||
|
|
||||||
1. Branch from `master`.
|
|
||||||
2. Keep commits focused -- one logical change per commit.
|
|
||||||
3. Describe *what* changed and *why* in the PR description.
|
|
||||||
4. Ensure the project compiles cleanly before submitting.
|
|
||||||
5. Manual testing against a WoW 3.3.5a server (e.g. AzerothCore/ChromieCraft) is expected
|
|
||||||
for gameplay-affecting changes.
|
|
||||||
|
|
||||||
## Architecture Overview
|
|
||||||
|
|
||||||
See [docs/architecture.md](docs/architecture.md) for the full picture. Key namespaces:
|
|
||||||
|
|
||||||
| Namespace | Responsibility |
|
|
||||||
|---|---|
|
|
||||||
| `wowee::game` | Game state, packet handling (`GameHandler`), opcode dispatch |
|
|
||||||
| `wowee::rendering` | Vulkan renderer, M2/WMO/terrain, sky system |
|
|
||||||
| `wowee::ui` | ImGui windows and HUD (`GameScreen`) |
|
|
||||||
| `wowee::core` | Coordinates, math, utilities |
|
|
||||||
| `wowee::network` | Connection, `Packet` read/write API |
|
|
||||||
|
|
||||||
## Packet Handlers
|
|
||||||
|
|
||||||
The standard pattern for adding a new server packet handler:
|
|
||||||
|
|
||||||
1. Define a `struct FooData` holding the parsed fields.
|
|
||||||
2. Write `void GameHandler::handleFoo(network::Packet& packet)` to parse into `FooData`.
|
|
||||||
3. Register it in the dispatch table: `registerHandler(LogicalOpcode::SMSG_FOO, &GameHandler::handleFoo)`.
|
|
||||||
|
|
||||||
Helper variants: `registerWorldHandler` (requires `isInWorld()`), `registerSkipHandler` (discard),
|
|
||||||
`registerErrorHandler` (log warning).
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
There is no automated test suite. Changes are verified by manual testing against
|
|
||||||
WoW 3.3.5a private servers (primarily ChromieCraft/AzerothCore). Classic and TBC
|
|
||||||
expansion paths are tested against their respective server builds.
|
|
||||||
|
|
||||||
## Key Files for New Contributors
|
|
||||||
|
|
||||||
| File | What it does |
|
|
||||||
|---|---|
|
|
||||||
| `include/game/game_handler.hpp` | Central game state and all packet handler declarations |
|
|
||||||
| `src/game/game_handler.cpp` | Packet dispatch registration and handler implementations |
|
|
||||||
| `include/network/packet.hpp` | `Packet` class -- the read/write API every handler uses |
|
|
||||||
| `include/ui/game_screen.hpp` | Main gameplay UI screen (ImGui) |
|
|
||||||
| `src/rendering/m2_renderer.cpp` | M2 model loading and rendering |
|
|
||||||
| `docs/architecture.md` | High-level system architecture reference |
|
|
||||||
|
|
@ -1,267 +1,98 @@
|
||||||
{
|
{
|
||||||
"AreaTable": {
|
"Spell": {
|
||||||
"ExploreFlag": 3,
|
"ID": 0, "Attributes": 5, "IconID": 117,
|
||||||
"ID": 0,
|
"Name": 120, "Tooltip": 147, "Rank": 129, "SchoolEnum": 1,
|
||||||
"MapID": 1,
|
"CastingTimeIndex": 15, "PowerType": 28, "ManaCost": 29, "RangeIndex": 33
|
||||||
"ParentAreaNum": 2
|
|
||||||
},
|
},
|
||||||
"CharHairGeosets": {
|
"SpellRange": { "MaxRange": 2 },
|
||||||
"GeosetID": 4,
|
"ItemDisplayInfo": {
|
||||||
"RaceID": 1,
|
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||||
"SexID": 2,
|
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||||
"Variation": 3
|
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||||
|
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||||
|
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||||
},
|
},
|
||||||
"CharSections": {
|
"CharSections": {
|
||||||
"BaseSection": 3,
|
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||||
"ColorIndex": 5,
|
"VariationIndex": 4, "ColorIndex": 5,
|
||||||
"Flags": 9,
|
"Texture1": 6, "Texture2": 7, "Texture3": 8,
|
||||||
"RaceID": 1,
|
"Flags": 9
|
||||||
"SexID": 2,
|
|
||||||
"Texture1": 6,
|
|
||||||
"Texture2": 7,
|
|
||||||
"Texture3": 8,
|
|
||||||
"VariationIndex": 4
|
|
||||||
},
|
},
|
||||||
"CharacterFacialHairStyles": {
|
"SpellIcon": { "ID": 0, "Path": 1 },
|
||||||
"Geoset100": 3,
|
"FactionTemplate": {
|
||||||
"Geoset200": 5,
|
"ID": 0, "Faction": 1, "FactionGroup": 3,
|
||||||
"Geoset300": 4,
|
"FriendGroup": 4, "EnemyGroup": 5,
|
||||||
"RaceID": 0,
|
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
|
||||||
"SexID": 1,
|
|
||||||
"Variation": 2
|
|
||||||
},
|
|
||||||
"CreatureDisplayInfo": {
|
|
||||||
"ExtraDisplayId": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"ModelID": 1,
|
|
||||||
"Skin1": 6,
|
|
||||||
"Skin2": 7,
|
|
||||||
"Skin3": 8
|
|
||||||
},
|
|
||||||
"CreatureDisplayInfoExtra": {
|
|
||||||
"BakeName": 20,
|
|
||||||
"EquipDisplay0": 8,
|
|
||||||
"EquipDisplay1": 9,
|
|
||||||
"EquipDisplay10": 18,
|
|
||||||
"EquipDisplay2": 10,
|
|
||||||
"EquipDisplay3": 11,
|
|
||||||
"EquipDisplay4": 12,
|
|
||||||
"EquipDisplay5": 13,
|
|
||||||
"EquipDisplay6": 14,
|
|
||||||
"EquipDisplay7": 15,
|
|
||||||
"EquipDisplay8": 16,
|
|
||||||
"EquipDisplay9": 17,
|
|
||||||
"FaceID": 4,
|
|
||||||
"FacialHairID": 7,
|
|
||||||
"HairColorID": 6,
|
|
||||||
"HairStyleID": 5,
|
|
||||||
"ID": 0,
|
|
||||||
"RaceID": 1,
|
|
||||||
"SexID": 2,
|
|
||||||
"SkinID": 3
|
|
||||||
},
|
|
||||||
"CreatureModelData": {
|
|
||||||
"ID": 0,
|
|
||||||
"ModelPath": 2
|
|
||||||
},
|
|
||||||
"Emotes": {
|
|
||||||
"AnimID": 2,
|
|
||||||
"ID": 0
|
|
||||||
},
|
|
||||||
"EmotesText": {
|
|
||||||
"Command": 1,
|
|
||||||
"EmoteRef": 2,
|
|
||||||
"ID": 0,
|
|
||||||
"OthersNoTargetTextID": 7,
|
|
||||||
"OthersTargetTextID": 3,
|
|
||||||
"SenderNoTargetTextID": 9,
|
|
||||||
"SenderTargetTextID": 5
|
|
||||||
},
|
|
||||||
"EmotesTextData": {
|
|
||||||
"ID": 0,
|
|
||||||
"Text": 1
|
|
||||||
},
|
},
|
||||||
"Faction": {
|
"Faction": {
|
||||||
"ID": 0,
|
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
|
||||||
"ReputationBase0": 10,
|
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
|
||||||
"ReputationBase1": 11,
|
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||||
"ReputationBase2": 12,
|
"ReputationBase2": 12, "ReputationBase3": 13
|
||||||
"ReputationBase3": 13,
|
|
||||||
"ReputationRaceMask0": 2,
|
|
||||||
"ReputationRaceMask1": 3,
|
|
||||||
"ReputationRaceMask2": 4,
|
|
||||||
"ReputationRaceMask3": 5
|
|
||||||
},
|
},
|
||||||
"FactionTemplate": {
|
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||||
"Enemy0": 6,
|
"CreatureDisplayInfoExtra": {
|
||||||
"Enemy1": 7,
|
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
||||||
"Enemy2": 8,
|
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
|
||||||
"Enemy3": 9,
|
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
|
||||||
"EnemyGroup": 5,
|
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
|
||||||
"Faction": 1,
|
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
|
||||||
"FactionGroup": 3,
|
"EquipDisplay9": 17, "EquipDisplay10": 18, "BakeName": 20
|
||||||
"FriendGroup": 4,
|
|
||||||
"ID": 0
|
|
||||||
},
|
},
|
||||||
"GameObjectDisplayInfo": {
|
"CreatureDisplayInfo": {
|
||||||
"ID": 0,
|
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
|
||||||
"ModelName": 1
|
"Skin1": 6, "Skin2": 7, "Skin3": 8
|
||||||
},
|
|
||||||
"ItemDisplayInfo": {
|
|
||||||
"GeosetGroup1": 7,
|
|
||||||
"GeosetGroup3": 9,
|
|
||||||
"ID": 0,
|
|
||||||
"InventoryIcon": 5,
|
|
||||||
"LeftModel": 1,
|
|
||||||
"LeftModelTexture": 3,
|
|
||||||
"TextureArmLower": 15,
|
|
||||||
"TextureArmUpper": 14,
|
|
||||||
"TextureFoot": 21,
|
|
||||||
"TextureHand": 16,
|
|
||||||
"TextureLegLower": 20,
|
|
||||||
"TextureLegUpper": 19,
|
|
||||||
"TextureTorsoLower": 18,
|
|
||||||
"TextureTorsoUpper": 17
|
|
||||||
},
|
|
||||||
"Light": {
|
|
||||||
"ID": 0,
|
|
||||||
"InnerRadius": 5,
|
|
||||||
"LightParamsID": 7,
|
|
||||||
"LightParamsIDRain": 8,
|
|
||||||
"LightParamsIDUnderwater": 9,
|
|
||||||
"MapID": 1,
|
|
||||||
"OuterRadius": 6,
|
|
||||||
"X": 2,
|
|
||||||
"Y": 4,
|
|
||||||
"Z": 3
|
|
||||||
},
|
|
||||||
"LightFloatBand": {
|
|
||||||
"BlockIndex": 1,
|
|
||||||
"NumKeyframes": 2,
|
|
||||||
"TimeKey0": 3,
|
|
||||||
"Value0": 19
|
|
||||||
},
|
|
||||||
"LightIntBand": {
|
|
||||||
"BlockIndex": 1,
|
|
||||||
"NumKeyframes": 2,
|
|
||||||
"TimeKey0": 3,
|
|
||||||
"Value0": 19
|
|
||||||
},
|
|
||||||
"LightParams": {
|
|
||||||
"LightParamsID": 0
|
|
||||||
},
|
|
||||||
"Map": {
|
|
||||||
"ID": 0,
|
|
||||||
"InternalName": 1
|
|
||||||
},
|
|
||||||
"SkillLine": {
|
|
||||||
"Category": 1,
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 3
|
|
||||||
},
|
|
||||||
"SkillLineAbility": {
|
|
||||||
"SkillLineID": 1,
|
|
||||||
"SpellID": 2
|
|
||||||
},
|
|
||||||
"Spell": {
|
|
||||||
"Attributes": 5,
|
|
||||||
"AttributesEx": 6,
|
|
||||||
"CastingTimeIndex": 15,
|
|
||||||
"DispelType": 4,
|
|
||||||
"DurationIndex": 40,
|
|
||||||
"EffectBasePoints0": 80,
|
|
||||||
"EffectBasePoints1": 81,
|
|
||||||
"EffectBasePoints2": 82,
|
|
||||||
"ID": 0,
|
|
||||||
"IconID": 117,
|
|
||||||
"ManaCost": 29,
|
|
||||||
"Name": 120,
|
|
||||||
"PowerType": 28,
|
|
||||||
"RangeIndex": 33,
|
|
||||||
"Rank": 129,
|
|
||||||
"SchoolEnum": 1,
|
|
||||||
"SpellVisualID": 115,
|
|
||||||
"Tooltip": 147
|
|
||||||
},
|
|
||||||
"SpellIcon": {
|
|
||||||
"ID": 0,
|
|
||||||
"Path": 1
|
|
||||||
},
|
|
||||||
"SpellRange": {
|
|
||||||
"MaxRange": 2
|
|
||||||
},
|
|
||||||
"SpellVisual": {
|
|
||||||
"CastKit": 2,
|
|
||||||
"ID": 0,
|
|
||||||
"ImpactKit": 3,
|
|
||||||
"MissileModel": 8,
|
|
||||||
"PrecastKit": 1
|
|
||||||
},
|
|
||||||
"SpellVisualEffectName": {
|
|
||||||
"FilePath": 2,
|
|
||||||
"ID": 0
|
|
||||||
},
|
|
||||||
"SpellVisualKit": {
|
|
||||||
"BaseEffect": 5,
|
|
||||||
"BreathEffect": 8,
|
|
||||||
"ChestEffect": 4,
|
|
||||||
"HeadEffect": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"LeftHandEffect": 6,
|
|
||||||
"RightHandEffect": 7,
|
|
||||||
"SpecialEffect0": 11,
|
|
||||||
"SpecialEffect1": 12,
|
|
||||||
"SpecialEffect2": 13
|
|
||||||
},
|
|
||||||
"Talent": {
|
|
||||||
"Column": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"PrereqRank0": 12,
|
|
||||||
"PrereqTalent0": 9,
|
|
||||||
"RankSpell0": 4,
|
|
||||||
"Row": 2,
|
|
||||||
"TabID": 1
|
|
||||||
},
|
|
||||||
"TalentTab": {
|
|
||||||
"BackgroundFile": 15,
|
|
||||||
"ClassMask": 12,
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 1,
|
|
||||||
"OrderIndex": 14
|
|
||||||
},
|
},
|
||||||
"TaxiNodes": {
|
"TaxiNodes": {
|
||||||
"ID": 0,
|
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5
|
||||||
"MapID": 1,
|
|
||||||
"Name": 5,
|
|
||||||
"X": 2,
|
|
||||||
"Y": 3,
|
|
||||||
"Z": 4
|
|
||||||
},
|
|
||||||
"TaxiPath": {
|
|
||||||
"Cost": 3,
|
|
||||||
"FromNode": 1,
|
|
||||||
"ID": 0,
|
|
||||||
"ToNode": 2
|
|
||||||
},
|
},
|
||||||
|
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
|
||||||
"TaxiPathNode": {
|
"TaxiPathNode": {
|
||||||
"ID": 0,
|
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
|
||||||
"MapID": 3,
|
"X": 4, "Y": 5, "Z": 6
|
||||||
"NodeIndex": 2,
|
},
|
||||||
"PathID": 1,
|
"TalentTab": {
|
||||||
"X": 4,
|
"ID": 0, "Name": 1, "ClassMask": 12,
|
||||||
"Y": 5,
|
"OrderIndex": 14, "BackgroundFile": 15
|
||||||
"Z": 6
|
},
|
||||||
|
"Talent": {
|
||||||
|
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
|
||||||
|
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
|
||||||
|
},
|
||||||
|
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
|
||||||
|
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
|
||||||
|
"Map": { "ID": 0, "InternalName": 1 },
|
||||||
|
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
|
||||||
|
"CharHairGeosets": {
|
||||||
|
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
|
||||||
|
},
|
||||||
|
"CharacterFacialHairStyles": {
|
||||||
|
"RaceID": 0, "SexID": 1, "Variation": 2,
|
||||||
|
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
|
||||||
|
},
|
||||||
|
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
|
||||||
|
"Emotes": { "ID": 0, "AnimID": 2 },
|
||||||
|
"EmotesText": {
|
||||||
|
"ID": 0, "Command": 1, "EmoteRef": 2,
|
||||||
|
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
|
||||||
|
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
|
||||||
|
},
|
||||||
|
"EmotesTextData": { "ID": 0, "Text": 1 },
|
||||||
|
"Light": {
|
||||||
|
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
|
||||||
|
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
|
||||||
|
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
|
||||||
|
},
|
||||||
|
"LightParams": { "LightParamsID": 0 },
|
||||||
|
"LightIntBand": {
|
||||||
|
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||||
|
},
|
||||||
|
"LightFloatBand": {
|
||||||
|
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||||
},
|
},
|
||||||
"WorldMapArea": {
|
"WorldMapArea": {
|
||||||
"AreaID": 2,
|
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
|
||||||
"AreaName": 3,
|
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
|
||||||
"DisplayMapID": 8,
|
"DisplayMapID": 8, "ParentWorldMapID": 10
|
||||||
"ID": 0,
|
|
||||||
"LocBottom": 7,
|
|
||||||
"LocLeft": 4,
|
|
||||||
"LocRight": 5,
|
|
||||||
"LocTop": 6,
|
|
||||||
"MapID": 1,
|
|
||||||
"ParentWorldMapID": 10
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -273,7 +273,7 @@
|
||||||
"SMSG_INVENTORY_CHANGE_FAILURE": "0x112",
|
"SMSG_INVENTORY_CHANGE_FAILURE": "0x112",
|
||||||
"SMSG_OPEN_CONTAINER": "0x113",
|
"SMSG_OPEN_CONTAINER": "0x113",
|
||||||
"CMSG_INSPECT": "0x114",
|
"CMSG_INSPECT": "0x114",
|
||||||
"SMSG_INSPECT_RESULTS_UPDATE": "0x115",
|
"SMSG_INSPECT": "0x115",
|
||||||
"CMSG_INITIATE_TRADE": "0x116",
|
"CMSG_INITIATE_TRADE": "0x116",
|
||||||
"CMSG_BEGIN_TRADE": "0x117",
|
"CMSG_BEGIN_TRADE": "0x117",
|
||||||
"CMSG_BUSY_TRADE": "0x118",
|
"CMSG_BUSY_TRADE": "0x118",
|
||||||
|
|
@ -300,7 +300,7 @@
|
||||||
"CMSG_NEW_SPELL_SLOT": "0x12D",
|
"CMSG_NEW_SPELL_SLOT": "0x12D",
|
||||||
"CMSG_CAST_SPELL": "0x12E",
|
"CMSG_CAST_SPELL": "0x12E",
|
||||||
"CMSG_CANCEL_CAST": "0x12F",
|
"CMSG_CANCEL_CAST": "0x12F",
|
||||||
"SMSG_CAST_FAILED": "0x130",
|
"SMSG_CAST_RESULT": "0x130",
|
||||||
"SMSG_SPELL_START": "0x131",
|
"SMSG_SPELL_START": "0x131",
|
||||||
"SMSG_SPELL_GO": "0x132",
|
"SMSG_SPELL_GO": "0x132",
|
||||||
"SMSG_SPELL_FAILURE": "0x133",
|
"SMSG_SPELL_FAILURE": "0x133",
|
||||||
|
|
@ -504,7 +504,8 @@
|
||||||
"CMSG_GM_SET_SECURITY_GROUP": "0x1F9",
|
"CMSG_GM_SET_SECURITY_GROUP": "0x1F9",
|
||||||
"CMSG_GM_NUKE": "0x1FA",
|
"CMSG_GM_NUKE": "0x1FA",
|
||||||
"MSG_RANDOM_ROLL": "0x1FB",
|
"MSG_RANDOM_ROLL": "0x1FB",
|
||||||
"SMSG_ENVIRONMENTAL_DAMAGE_LOG": "0x1FC",
|
"SMSG_ENVIRONMENTALDAMAGELOG": "0x1FC",
|
||||||
|
"CMSG_RWHOIS_OBSOLETE": "0x1FD",
|
||||||
"SMSG_RWHOIS": "0x1FE",
|
"SMSG_RWHOIS": "0x1FE",
|
||||||
"MSG_LOOKING_FOR_GROUP": "0x1FF",
|
"MSG_LOOKING_FOR_GROUP": "0x1FF",
|
||||||
"CMSG_SET_LOOKING_FOR_GROUP": "0x200",
|
"CMSG_SET_LOOKING_FOR_GROUP": "0x200",
|
||||||
|
|
@ -527,6 +528,7 @@
|
||||||
"CMSG_GMTICKET_GETTICKET": "0x211",
|
"CMSG_GMTICKET_GETTICKET": "0x211",
|
||||||
"SMSG_GMTICKET_GETTICKET": "0x212",
|
"SMSG_GMTICKET_GETTICKET": "0x212",
|
||||||
"CMSG_UNLEARN_TALENTS": "0x213",
|
"CMSG_UNLEARN_TALENTS": "0x213",
|
||||||
|
"SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE": "0x214",
|
||||||
"SMSG_GAMEOBJECT_DESPAWN_ANIM": "0x215",
|
"SMSG_GAMEOBJECT_DESPAWN_ANIM": "0x215",
|
||||||
"MSG_CORPSE_QUERY": "0x216",
|
"MSG_CORPSE_QUERY": "0x216",
|
||||||
"CMSG_GMTICKET_DELETETICKET": "0x217",
|
"CMSG_GMTICKET_DELETETICKET": "0x217",
|
||||||
|
|
@ -536,7 +538,7 @@
|
||||||
"SMSG_GMTICKET_SYSTEMSTATUS": "0x21B",
|
"SMSG_GMTICKET_SYSTEMSTATUS": "0x21B",
|
||||||
"CMSG_SPIRIT_HEALER_ACTIVATE": "0x21C",
|
"CMSG_SPIRIT_HEALER_ACTIVATE": "0x21C",
|
||||||
"CMSG_SET_STAT_CHEAT": "0x21D",
|
"CMSG_SET_STAT_CHEAT": "0x21D",
|
||||||
"SMSG_QUEST_FORCE_REMOVE": "0x21E",
|
"SMSG_SET_REST_START": "0x21E",
|
||||||
"CMSG_SKILL_BUY_STEP": "0x21F",
|
"CMSG_SKILL_BUY_STEP": "0x21F",
|
||||||
"CMSG_SKILL_BUY_RANK": "0x220",
|
"CMSG_SKILL_BUY_RANK": "0x220",
|
||||||
"CMSG_XP_CHEAT": "0x221",
|
"CMSG_XP_CHEAT": "0x221",
|
||||||
|
|
@ -569,6 +571,8 @@
|
||||||
"CMSG_BATTLEFIELD_LIST": "0x23C",
|
"CMSG_BATTLEFIELD_LIST": "0x23C",
|
||||||
"SMSG_BATTLEFIELD_LIST": "0x23D",
|
"SMSG_BATTLEFIELD_LIST": "0x23D",
|
||||||
"CMSG_BATTLEFIELD_JOIN": "0x23E",
|
"CMSG_BATTLEFIELD_JOIN": "0x23E",
|
||||||
|
"SMSG_BATTLEFIELD_WIN_OBSOLETE": "0x23F",
|
||||||
|
"SMSG_BATTLEFIELD_LOSE_OBSOLETE": "0x240",
|
||||||
"CMSG_TAXICLEARNODE": "0x241",
|
"CMSG_TAXICLEARNODE": "0x241",
|
||||||
"CMSG_TAXIENABLENODE": "0x242",
|
"CMSG_TAXIENABLENODE": "0x242",
|
||||||
"CMSG_ITEM_TEXT_QUERY": "0x243",
|
"CMSG_ITEM_TEXT_QUERY": "0x243",
|
||||||
|
|
@ -601,6 +605,7 @@
|
||||||
"SMSG_AUCTION_BIDDER_NOTIFICATION": "0x25E",
|
"SMSG_AUCTION_BIDDER_NOTIFICATION": "0x25E",
|
||||||
"SMSG_AUCTION_OWNER_NOTIFICATION": "0x25F",
|
"SMSG_AUCTION_OWNER_NOTIFICATION": "0x25F",
|
||||||
"SMSG_PROCRESIST": "0x260",
|
"SMSG_PROCRESIST": "0x260",
|
||||||
|
"SMSG_STANDSTATE_CHANGE_FAILURE_OBSOLETE": "0x261",
|
||||||
"SMSG_DISPEL_FAILED": "0x262",
|
"SMSG_DISPEL_FAILED": "0x262",
|
||||||
"SMSG_SPELLORDAMAGE_IMMUNE": "0x263",
|
"SMSG_SPELLORDAMAGE_IMMUNE": "0x263",
|
||||||
"CMSG_AUCTION_LIST_BIDDER_ITEMS": "0x264",
|
"CMSG_AUCTION_LIST_BIDDER_ITEMS": "0x264",
|
||||||
|
|
@ -688,8 +693,8 @@
|
||||||
"SMSG_SCRIPT_MESSAGE": "0x2B6",
|
"SMSG_SCRIPT_MESSAGE": "0x2B6",
|
||||||
"SMSG_DUEL_COUNTDOWN": "0x2B7",
|
"SMSG_DUEL_COUNTDOWN": "0x2B7",
|
||||||
"SMSG_AREA_TRIGGER_MESSAGE": "0x2B8",
|
"SMSG_AREA_TRIGGER_MESSAGE": "0x2B8",
|
||||||
"CMSG_SHOWING_HELM": "0x2B9",
|
"CMSG_TOGGLE_HELM": "0x2B9",
|
||||||
"CMSG_SHOWING_CLOAK": "0x2BA",
|
"CMSG_TOGGLE_CLOAK": "0x2BA",
|
||||||
"SMSG_MEETINGSTONE_JOINFAILED": "0x2BB",
|
"SMSG_MEETINGSTONE_JOINFAILED": "0x2BB",
|
||||||
"SMSG_PLAYER_SKINNED": "0x2BC",
|
"SMSG_PLAYER_SKINNED": "0x2BC",
|
||||||
"SMSG_DURABILITY_DAMAGE_DEATH": "0x2BD",
|
"SMSG_DURABILITY_DAMAGE_DEATH": "0x2BD",
|
||||||
|
|
@ -816,5 +821,6 @@
|
||||||
"SMSG_LOTTERY_RESULT_OBSOLETE": "0x337",
|
"SMSG_LOTTERY_RESULT_OBSOLETE": "0x337",
|
||||||
"SMSG_CHARACTER_PROFILE": "0x338",
|
"SMSG_CHARACTER_PROFILE": "0x338",
|
||||||
"SMSG_CHARACTER_PROFILE_REALM_CONNECTED": "0x339",
|
"SMSG_CHARACTER_PROFILE_REALM_CONNECTED": "0x339",
|
||||||
|
"SMSG_UNK": "0x33A",
|
||||||
"SMSG_DEFENSE_MESSAGE": "0x33B"
|
"SMSG_DEFENSE_MESSAGE": "0x33B"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,47 @@
|
||||||
{
|
{
|
||||||
"CONTAINER_FIELD_NUM_SLOTS": 48,
|
|
||||||
"CONTAINER_FIELD_SLOT_1": 50,
|
|
||||||
"GAMEOBJECT_DISPLAYID": 8,
|
|
||||||
"GAMEOBJECT_BYTES_1": 14,
|
|
||||||
"ITEM_FIELD_DURABILITY": 48,
|
|
||||||
"ITEM_FIELD_MAXDURABILITY": 49,
|
|
||||||
"ITEM_FIELD_STACK_COUNT": 14,
|
|
||||||
"OBJECT_FIELD_ENTRY": 3,
|
"OBJECT_FIELD_ENTRY": 3,
|
||||||
"OBJECT_FIELD_SCALE_X": 4,
|
"OBJECT_FIELD_SCALE_X": 4,
|
||||||
"PLAYER_BYTES": 191,
|
"UNIT_FIELD_TARGET_LO": 16,
|
||||||
"PLAYER_BYTES_2": 192,
|
"UNIT_FIELD_TARGET_HI": 17,
|
||||||
"PLAYER_END": 1282,
|
|
||||||
"PLAYER_EXPLORED_ZONES_START": 1111,
|
|
||||||
"PLAYER_FIELD_BANKBAG_SLOT_1": 612,
|
|
||||||
"PLAYER_FIELD_BANK_SLOT_1": 564,
|
|
||||||
"PLAYER_FIELD_COINAGE": 1176,
|
|
||||||
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
|
|
||||||
"PLAYER_FIELD_PACK_SLOT_1": 532,
|
|
||||||
"PLAYER_FLAGS": 190,
|
|
||||||
"PLAYER_NEXT_LEVEL_XP": 717,
|
|
||||||
"PLAYER_QUEST_LOG_START": 198,
|
|
||||||
"PLAYER_REST_STATE_EXPERIENCE": 1175,
|
|
||||||
"PLAYER_SKILL_INFO_START": 718,
|
|
||||||
"PLAYER_XP": 716,
|
|
||||||
"UNIT_DYNAMIC_FLAGS": 143,
|
|
||||||
"UNIT_END": 188,
|
|
||||||
"UNIT_FIELD_AURAFLAGS": 98,
|
|
||||||
"UNIT_FIELD_AURAS": 50,
|
|
||||||
"UNIT_FIELD_BYTES_0": 36,
|
"UNIT_FIELD_BYTES_0": 36,
|
||||||
"UNIT_FIELD_BYTES_1": 133,
|
|
||||||
"UNIT_FIELD_DISPLAYID": 131,
|
|
||||||
"UNIT_FIELD_FACTIONTEMPLATE": 35,
|
|
||||||
"UNIT_FIELD_FLAGS": 46,
|
|
||||||
"UNIT_FIELD_HEALTH": 22,
|
"UNIT_FIELD_HEALTH": 22,
|
||||||
"UNIT_FIELD_LEVEL": 34,
|
"UNIT_FIELD_POWER1": 23,
|
||||||
"UNIT_FIELD_MAXHEALTH": 28,
|
"UNIT_FIELD_MAXHEALTH": 28,
|
||||||
"UNIT_FIELD_MAXPOWER1": 29,
|
"UNIT_FIELD_MAXPOWER1": 29,
|
||||||
|
"UNIT_FIELD_LEVEL": 34,
|
||||||
|
"UNIT_FIELD_FACTIONTEMPLATE": 35,
|
||||||
|
"UNIT_FIELD_FLAGS": 46,
|
||||||
|
"UNIT_FIELD_DISPLAYID": 131,
|
||||||
"UNIT_FIELD_MOUNTDISPLAYID": 133,
|
"UNIT_FIELD_MOUNTDISPLAYID": 133,
|
||||||
"UNIT_FIELD_POWER1": 23,
|
"UNIT_FIELD_AURAS": 50,
|
||||||
|
"UNIT_NPC_FLAGS": 147,
|
||||||
|
"UNIT_DYNAMIC_FLAGS": 143,
|
||||||
"UNIT_FIELD_RESISTANCES": 154,
|
"UNIT_FIELD_RESISTANCES": 154,
|
||||||
"UNIT_FIELD_STAT0": 138,
|
"UNIT_FIELD_STAT0": 138,
|
||||||
"UNIT_FIELD_STAT1": 139,
|
"UNIT_FIELD_STAT1": 139,
|
||||||
"UNIT_FIELD_STAT2": 140,
|
"UNIT_FIELD_STAT2": 140,
|
||||||
"UNIT_FIELD_STAT3": 141,
|
"UNIT_FIELD_STAT3": 141,
|
||||||
"UNIT_FIELD_STAT4": 142,
|
"UNIT_FIELD_STAT4": 142,
|
||||||
"UNIT_FIELD_TARGET_HI": 17,
|
"UNIT_END": 188,
|
||||||
"UNIT_FIELD_TARGET_LO": 16,
|
"PLAYER_FLAGS": 190,
|
||||||
"UNIT_NPC_FLAGS": 147
|
"PLAYER_BYTES": 191,
|
||||||
|
"PLAYER_BYTES_2": 192,
|
||||||
|
"PLAYER_XP": 716,
|
||||||
|
"PLAYER_NEXT_LEVEL_XP": 717,
|
||||||
|
"PLAYER_REST_STATE_EXPERIENCE": 1175,
|
||||||
|
"PLAYER_FIELD_COINAGE": 1176,
|
||||||
|
"PLAYER_QUEST_LOG_START": 198,
|
||||||
|
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
|
||||||
|
"PLAYER_FIELD_PACK_SLOT_1": 532,
|
||||||
|
"PLAYER_FIELD_BANK_SLOT_1": 564,
|
||||||
|
"PLAYER_FIELD_BANKBAG_SLOT_1": 612,
|
||||||
|
"PLAYER_SKILL_INFO_START": 718,
|
||||||
|
"PLAYER_EXPLORED_ZONES_START": 1111,
|
||||||
|
"PLAYER_END": 1282,
|
||||||
|
"GAMEOBJECT_DISPLAYID": 8,
|
||||||
|
"ITEM_FIELD_STACK_COUNT": 14,
|
||||||
|
"ITEM_FIELD_DURABILITY": 48,
|
||||||
|
"ITEM_FIELD_MAXDURABILITY": 49,
|
||||||
|
"CONTAINER_FIELD_NUM_SLOTS": 48,
|
||||||
|
"CONTAINER_FIELD_SLOT_1": 50
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,314 +1,100 @@
|
||||||
{
|
{
|
||||||
"AreaTable": {
|
"Spell": {
|
||||||
"ExploreFlag": 3,
|
"ID": 0, "Attributes": 5, "IconID": 124,
|
||||||
"ID": 0,
|
"Name": 127, "Tooltip": 154, "Rank": 136, "SchoolMask": 215,
|
||||||
"MapID": 1,
|
"CastingTimeIndex": 22, "PowerType": 35, "ManaCost": 36, "RangeIndex": 40
|
||||||
"ParentAreaNum": 2
|
|
||||||
},
|
},
|
||||||
"CharHairGeosets": {
|
"SpellRange": { "MaxRange": 4 },
|
||||||
"GeosetID": 4,
|
"ItemDisplayInfo": {
|
||||||
"RaceID": 1,
|
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||||
"SexID": 2,
|
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||||
"Variation": 3
|
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||||
|
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||||
|
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||||
},
|
},
|
||||||
"CharSections": {
|
"CharSections": {
|
||||||
"BaseSection": 3,
|
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||||
"ColorIndex": 5,
|
"VariationIndex": 4, "ColorIndex": 5,
|
||||||
"Flags": 9,
|
"Texture1": 6, "Texture2": 7, "Texture3": 8,
|
||||||
"RaceID": 1,
|
"Flags": 9
|
||||||
"SexID": 2,
|
|
||||||
"Texture1": 6,
|
|
||||||
"Texture2": 7,
|
|
||||||
"Texture3": 8,
|
|
||||||
"VariationIndex": 4
|
|
||||||
},
|
},
|
||||||
"CharTitles": {
|
"SpellIcon": { "ID": 0, "Path": 1 },
|
||||||
"ID": 0,
|
"FactionTemplate": {
|
||||||
"Title": 2,
|
"ID": 0, "Faction": 1, "FactionGroup": 3,
|
||||||
"TitleBit": 20
|
"FriendGroup": 4, "EnemyGroup": 5,
|
||||||
},
|
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
|
||||||
"CharacterFacialHairStyles": {
|
|
||||||
"Geoset100": 3,
|
|
||||||
"Geoset200": 5,
|
|
||||||
"Geoset300": 4,
|
|
||||||
"RaceID": 0,
|
|
||||||
"SexID": 1,
|
|
||||||
"Variation": 2
|
|
||||||
},
|
|
||||||
"CreatureDisplayInfo": {
|
|
||||||
"ExtraDisplayId": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"ModelID": 1,
|
|
||||||
"Skin1": 6,
|
|
||||||
"Skin2": 7,
|
|
||||||
"Skin3": 8
|
|
||||||
},
|
|
||||||
"CreatureDisplayInfoExtra": {
|
|
||||||
"BakeName": 20,
|
|
||||||
"EquipDisplay0": 8,
|
|
||||||
"EquipDisplay1": 9,
|
|
||||||
"EquipDisplay10": 18,
|
|
||||||
"EquipDisplay2": 10,
|
|
||||||
"EquipDisplay3": 11,
|
|
||||||
"EquipDisplay4": 12,
|
|
||||||
"EquipDisplay5": 13,
|
|
||||||
"EquipDisplay6": 14,
|
|
||||||
"EquipDisplay7": 15,
|
|
||||||
"EquipDisplay8": 16,
|
|
||||||
"EquipDisplay9": 17,
|
|
||||||
"FaceID": 4,
|
|
||||||
"FacialHairID": 7,
|
|
||||||
"HairColorID": 6,
|
|
||||||
"HairStyleID": 5,
|
|
||||||
"ID": 0,
|
|
||||||
"RaceID": 1,
|
|
||||||
"SexID": 2,
|
|
||||||
"SkinID": 3
|
|
||||||
},
|
|
||||||
"CreatureModelData": {
|
|
||||||
"ID": 0,
|
|
||||||
"ModelPath": 2
|
|
||||||
},
|
|
||||||
"Emotes": {
|
|
||||||
"AnimID": 2,
|
|
||||||
"ID": 0
|
|
||||||
},
|
|
||||||
"EmotesText": {
|
|
||||||
"Command": 1,
|
|
||||||
"EmoteRef": 2,
|
|
||||||
"ID": 0,
|
|
||||||
"OthersNoTargetTextID": 7,
|
|
||||||
"OthersTargetTextID": 3,
|
|
||||||
"SenderNoTargetTextID": 9,
|
|
||||||
"SenderTargetTextID": 5
|
|
||||||
},
|
|
||||||
"EmotesTextData": {
|
|
||||||
"ID": 0,
|
|
||||||
"Text": 1
|
|
||||||
},
|
},
|
||||||
"Faction": {
|
"Faction": {
|
||||||
"ID": 0,
|
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
|
||||||
"ReputationBase0": 10,
|
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
|
||||||
"ReputationBase1": 11,
|
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||||
"ReputationBase2": 12,
|
"ReputationBase2": 12, "ReputationBase3": 13
|
||||||
"ReputationBase3": 13,
|
|
||||||
"ReputationRaceMask0": 2,
|
|
||||||
"ReputationRaceMask1": 3,
|
|
||||||
"ReputationRaceMask2": 4,
|
|
||||||
"ReputationRaceMask3": 5
|
|
||||||
},
|
},
|
||||||
"FactionTemplate": {
|
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||||
"Enemy0": 6,
|
"CreatureDisplayInfoExtra": {
|
||||||
"Enemy1": 7,
|
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
||||||
"Enemy2": 8,
|
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
|
||||||
"Enemy3": 9,
|
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
|
||||||
"EnemyGroup": 5,
|
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
|
||||||
"Faction": 1,
|
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
|
||||||
"FactionGroup": 3,
|
"EquipDisplay9": 17, "EquipDisplay10": 18, "BakeName": 20
|
||||||
"FriendGroup": 4,
|
|
||||||
"ID": 0
|
|
||||||
},
|
},
|
||||||
"GameObjectDisplayInfo": {
|
"CreatureDisplayInfo": {
|
||||||
"ID": 0,
|
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
|
||||||
"ModelName": 1
|
"Skin1": 6, "Skin2": 7, "Skin3": 8
|
||||||
},
|
|
||||||
"ItemDisplayInfo": {
|
|
||||||
"GeosetGroup1": 7,
|
|
||||||
"GeosetGroup3": 9,
|
|
||||||
"ID": 0,
|
|
||||||
"InventoryIcon": 5,
|
|
||||||
"LeftModel": 1,
|
|
||||||
"LeftModelTexture": 3,
|
|
||||||
"TextureArmLower": 15,
|
|
||||||
"TextureArmUpper": 14,
|
|
||||||
"TextureFoot": 21,
|
|
||||||
"TextureHand": 16,
|
|
||||||
"TextureLegLower": 20,
|
|
||||||
"TextureLegUpper": 19,
|
|
||||||
"TextureTorsoLower": 18,
|
|
||||||
"TextureTorsoUpper": 17
|
|
||||||
},
|
|
||||||
"ItemSet": {
|
|
||||||
"ID": 0,
|
|
||||||
"Item0": 18,
|
|
||||||
"Item1": 19,
|
|
||||||
"Item2": 20,
|
|
||||||
"Item3": 21,
|
|
||||||
"Item4": 22,
|
|
||||||
"Item5": 23,
|
|
||||||
"Item6": 24,
|
|
||||||
"Item7": 25,
|
|
||||||
"Item8": 26,
|
|
||||||
"Item9": 27,
|
|
||||||
"Name": 1,
|
|
||||||
"Spell0": 28,
|
|
||||||
"Spell1": 29,
|
|
||||||
"Spell2": 30,
|
|
||||||
"Spell3": 31,
|
|
||||||
"Spell4": 32,
|
|
||||||
"Spell5": 33,
|
|
||||||
"Spell6": 34,
|
|
||||||
"Spell7": 35,
|
|
||||||
"Spell8": 36,
|
|
||||||
"Spell9": 37,
|
|
||||||
"Threshold0": 38,
|
|
||||||
"Threshold1": 39,
|
|
||||||
"Threshold2": 40,
|
|
||||||
"Threshold3": 41,
|
|
||||||
"Threshold4": 42,
|
|
||||||
"Threshold5": 43,
|
|
||||||
"Threshold6": 44,
|
|
||||||
"Threshold7": 45,
|
|
||||||
"Threshold8": 46,
|
|
||||||
"Threshold9": 47
|
|
||||||
},
|
|
||||||
"Light": {
|
|
||||||
"ID": 0,
|
|
||||||
"InnerRadius": 5,
|
|
||||||
"LightParamsID": 7,
|
|
||||||
"LightParamsIDRain": 8,
|
|
||||||
"LightParamsIDUnderwater": 9,
|
|
||||||
"MapID": 1,
|
|
||||||
"OuterRadius": 6,
|
|
||||||
"X": 2,
|
|
||||||
"Y": 4,
|
|
||||||
"Z": 3
|
|
||||||
},
|
|
||||||
"LightFloatBand": {
|
|
||||||
"BlockIndex": 1,
|
|
||||||
"NumKeyframes": 2,
|
|
||||||
"TimeKey0": 3,
|
|
||||||
"Value0": 19
|
|
||||||
},
|
|
||||||
"LightIntBand": {
|
|
||||||
"BlockIndex": 1,
|
|
||||||
"NumKeyframes": 2,
|
|
||||||
"TimeKey0": 3,
|
|
||||||
"Value0": 19
|
|
||||||
},
|
|
||||||
"LightParams": {
|
|
||||||
"LightParamsID": 0
|
|
||||||
},
|
|
||||||
"Map": {
|
|
||||||
"ID": 0,
|
|
||||||
"InternalName": 1
|
|
||||||
},
|
|
||||||
"SkillLine": {
|
|
||||||
"Category": 1,
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 3
|
|
||||||
},
|
|
||||||
"SkillLineAbility": {
|
|
||||||
"SkillLineID": 1,
|
|
||||||
"SpellID": 2
|
|
||||||
},
|
|
||||||
"Spell": {
|
|
||||||
"Attributes": 5,
|
|
||||||
"AttributesEx": 6,
|
|
||||||
"CastingTimeIndex": 22,
|
|
||||||
"DispelType": 3,
|
|
||||||
"DurationIndex": 40,
|
|
||||||
"EffectBasePoints0": 80,
|
|
||||||
"EffectBasePoints1": 81,
|
|
||||||
"EffectBasePoints2": 82,
|
|
||||||
"ID": 0,
|
|
||||||
"IconID": 124,
|
|
||||||
"ManaCost": 36,
|
|
||||||
"Name": 127,
|
|
||||||
"PowerType": 35,
|
|
||||||
"RangeIndex": 40,
|
|
||||||
"Rank": 136,
|
|
||||||
"SchoolMask": 215,
|
|
||||||
"SpellVisualID": 122,
|
|
||||||
"Tooltip": 154
|
|
||||||
},
|
|
||||||
"SpellIcon": {
|
|
||||||
"ID": 0,
|
|
||||||
"Path": 1
|
|
||||||
},
|
|
||||||
"SpellItemEnchantment": {
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 8
|
|
||||||
},
|
|
||||||
"SpellRange": {
|
|
||||||
"MaxRange": 4
|
|
||||||
},
|
|
||||||
"SpellVisual": {
|
|
||||||
"CastKit": 2,
|
|
||||||
"ID": 0,
|
|
||||||
"ImpactKit": 3,
|
|
||||||
"MissileModel": 8,
|
|
||||||
"PrecastKit": 1
|
|
||||||
},
|
|
||||||
"SpellVisualEffectName": {
|
|
||||||
"FilePath": 2,
|
|
||||||
"ID": 0
|
|
||||||
},
|
|
||||||
"SpellVisualKit": {
|
|
||||||
"BaseEffect": 5,
|
|
||||||
"BreathEffect": 8,
|
|
||||||
"ChestEffect": 4,
|
|
||||||
"HeadEffect": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"LeftHandEffect": 6,
|
|
||||||
"RightHandEffect": 7,
|
|
||||||
"SpecialEffect0": 11,
|
|
||||||
"SpecialEffect1": 12,
|
|
||||||
"SpecialEffect2": 13
|
|
||||||
},
|
|
||||||
"Talent": {
|
|
||||||
"Column": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"PrereqRank0": 12,
|
|
||||||
"PrereqTalent0": 9,
|
|
||||||
"RankSpell0": 4,
|
|
||||||
"Row": 2,
|
|
||||||
"TabID": 1
|
|
||||||
},
|
|
||||||
"TalentTab": {
|
|
||||||
"BackgroundFile": 15,
|
|
||||||
"ClassMask": 12,
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 1,
|
|
||||||
"OrderIndex": 14
|
|
||||||
},
|
},
|
||||||
"TaxiNodes": {
|
"TaxiNodes": {
|
||||||
"ID": 0,
|
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5,
|
||||||
"MapID": 1,
|
"MountDisplayIdAllianceFallback": 12, "MountDisplayIdHordeFallback": 13,
|
||||||
"MountDisplayIdAlliance": 14,
|
"MountDisplayIdAlliance": 14, "MountDisplayIdHorde": 15
|
||||||
"MountDisplayIdAllianceFallback": 12,
|
|
||||||
"MountDisplayIdHorde": 15,
|
|
||||||
"MountDisplayIdHordeFallback": 13,
|
|
||||||
"Name": 5,
|
|
||||||
"X": 2,
|
|
||||||
"Y": 3,
|
|
||||||
"Z": 4
|
|
||||||
},
|
|
||||||
"TaxiPath": {
|
|
||||||
"Cost": 3,
|
|
||||||
"FromNode": 1,
|
|
||||||
"ID": 0,
|
|
||||||
"ToNode": 2
|
|
||||||
},
|
},
|
||||||
|
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
|
||||||
"TaxiPathNode": {
|
"TaxiPathNode": {
|
||||||
"ID": 0,
|
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
|
||||||
"MapID": 3,
|
"X": 4, "Y": 5, "Z": 6
|
||||||
"NodeIndex": 2,
|
},
|
||||||
"PathID": 1,
|
"TalentTab": {
|
||||||
"X": 4,
|
"ID": 0, "Name": 1, "ClassMask": 12,
|
||||||
"Y": 5,
|
"OrderIndex": 14, "BackgroundFile": 15
|
||||||
"Z": 6
|
},
|
||||||
|
"Talent": {
|
||||||
|
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
|
||||||
|
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
|
||||||
|
},
|
||||||
|
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
|
||||||
|
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
|
||||||
|
"Map": { "ID": 0, "InternalName": 1 },
|
||||||
|
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
|
||||||
|
"CharHairGeosets": {
|
||||||
|
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
|
||||||
|
},
|
||||||
|
"CharacterFacialHairStyles": {
|
||||||
|
"RaceID": 0, "SexID": 1, "Variation": 2,
|
||||||
|
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
|
||||||
|
},
|
||||||
|
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
|
||||||
|
"Emotes": { "ID": 0, "AnimID": 2 },
|
||||||
|
"EmotesText": {
|
||||||
|
"ID": 0, "Command": 1, "EmoteRef": 2,
|
||||||
|
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
|
||||||
|
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
|
||||||
|
},
|
||||||
|
"EmotesTextData": { "ID": 0, "Text": 1 },
|
||||||
|
"Light": {
|
||||||
|
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
|
||||||
|
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
|
||||||
|
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
|
||||||
|
},
|
||||||
|
"LightParams": { "LightParamsID": 0 },
|
||||||
|
"LightIntBand": {
|
||||||
|
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||||
|
},
|
||||||
|
"LightFloatBand": {
|
||||||
|
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||||
},
|
},
|
||||||
"WorldMapArea": {
|
"WorldMapArea": {
|
||||||
"AreaID": 2,
|
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
|
||||||
"AreaName": 3,
|
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
|
||||||
"DisplayMapID": 8,
|
"DisplayMapID": 8, "ParentWorldMapID": 10
|
||||||
"ID": 0,
|
|
||||||
"LocBottom": 7,
|
|
||||||
"LocLeft": 4,
|
|
||||||
"LocRight": 5,
|
|
||||||
"LocTop": 6,
|
|
||||||
"MapID": 1,
|
|
||||||
"ParentWorldMapID": 10
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,46 @@
|
||||||
{
|
{
|
||||||
"CONTAINER_FIELD_NUM_SLOTS": 64,
|
|
||||||
"CONTAINER_FIELD_SLOT_1": 66,
|
|
||||||
"GAMEOBJECT_DISPLAYID": 8,
|
|
||||||
"GAMEOBJECT_BYTES_1": 17,
|
|
||||||
"ITEM_FIELD_DURABILITY": 60,
|
|
||||||
"ITEM_FIELD_MAXDURABILITY": 61,
|
|
||||||
"ITEM_FIELD_STACK_COUNT": 14,
|
|
||||||
"OBJECT_FIELD_ENTRY": 3,
|
"OBJECT_FIELD_ENTRY": 3,
|
||||||
"OBJECT_FIELD_SCALE_X": 4,
|
"OBJECT_FIELD_SCALE_X": 4,
|
||||||
"PLAYER_BYTES": 237,
|
"UNIT_FIELD_TARGET_LO": 16,
|
||||||
"PLAYER_BYTES_2": 238,
|
"UNIT_FIELD_TARGET_HI": 17,
|
||||||
"PLAYER_EXPLORED_ZONES_START": 1312,
|
|
||||||
"PLAYER_FIELD_ARENA_CURRENCY": 1506,
|
|
||||||
"PLAYER_FIELD_BANKBAG_SLOT_1": 784,
|
|
||||||
"PLAYER_FIELD_BANK_SLOT_1": 728,
|
|
||||||
"PLAYER_FIELD_COINAGE": 1441,
|
|
||||||
"PLAYER_FIELD_HONOR_CURRENCY": 1505,
|
|
||||||
"PLAYER_FIELD_INV_SLOT_HEAD": 650,
|
|
||||||
"PLAYER_FIELD_PACK_SLOT_1": 696,
|
|
||||||
"PLAYER_FLAGS": 236,
|
|
||||||
"PLAYER_NEXT_LEVEL_XP": 927,
|
|
||||||
"PLAYER_QUEST_LOG_START": 244,
|
|
||||||
"PLAYER_REST_STATE_EXPERIENCE": 1440,
|
|
||||||
"PLAYER_SKILL_INFO_START": 928,
|
|
||||||
"PLAYER_XP": 926,
|
|
||||||
"UNIT_DYNAMIC_FLAGS": 164,
|
|
||||||
"UNIT_END": 234,
|
|
||||||
"UNIT_FIELD_BYTES_0": 36,
|
"UNIT_FIELD_BYTES_0": 36,
|
||||||
"UNIT_FIELD_BYTES_1": 137,
|
"UNIT_FIELD_HEALTH": 22,
|
||||||
"UNIT_FIELD_DISPLAYID": 152,
|
"UNIT_FIELD_POWER1": 23,
|
||||||
|
"UNIT_FIELD_MAXHEALTH": 28,
|
||||||
|
"UNIT_FIELD_MAXPOWER1": 29,
|
||||||
|
"UNIT_FIELD_LEVEL": 34,
|
||||||
"UNIT_FIELD_FACTIONTEMPLATE": 35,
|
"UNIT_FIELD_FACTIONTEMPLATE": 35,
|
||||||
"UNIT_FIELD_FLAGS": 46,
|
"UNIT_FIELD_FLAGS": 46,
|
||||||
"UNIT_FIELD_FLAGS_2": 47,
|
"UNIT_FIELD_FLAGS_2": 47,
|
||||||
"UNIT_FIELD_HEALTH": 22,
|
"UNIT_FIELD_DISPLAYID": 152,
|
||||||
"UNIT_FIELD_LEVEL": 34,
|
|
||||||
"UNIT_FIELD_MAXHEALTH": 28,
|
|
||||||
"UNIT_FIELD_MAXPOWER1": 29,
|
|
||||||
"UNIT_FIELD_MOUNTDISPLAYID": 154,
|
"UNIT_FIELD_MOUNTDISPLAYID": 154,
|
||||||
"UNIT_FIELD_POWER1": 23,
|
"UNIT_NPC_FLAGS": 168,
|
||||||
|
"UNIT_DYNAMIC_FLAGS": 164,
|
||||||
"UNIT_FIELD_RESISTANCES": 185,
|
"UNIT_FIELD_RESISTANCES": 185,
|
||||||
"UNIT_FIELD_STAT0": 159,
|
"UNIT_FIELD_STAT0": 159,
|
||||||
"UNIT_FIELD_STAT1": 160,
|
"UNIT_FIELD_STAT1": 160,
|
||||||
"UNIT_FIELD_STAT2": 161,
|
"UNIT_FIELD_STAT2": 161,
|
||||||
"UNIT_FIELD_STAT3": 162,
|
"UNIT_FIELD_STAT3": 162,
|
||||||
"UNIT_FIELD_STAT4": 163,
|
"UNIT_FIELD_STAT4": 163,
|
||||||
"UNIT_FIELD_TARGET_HI": 17,
|
"UNIT_END": 234,
|
||||||
"UNIT_FIELD_TARGET_LO": 16,
|
"PLAYER_FLAGS": 236,
|
||||||
"UNIT_NPC_FLAGS": 168
|
"PLAYER_BYTES": 237,
|
||||||
|
"PLAYER_BYTES_2": 238,
|
||||||
|
"PLAYER_XP": 926,
|
||||||
|
"PLAYER_NEXT_LEVEL_XP": 927,
|
||||||
|
"PLAYER_REST_STATE_EXPERIENCE": 1440,
|
||||||
|
"PLAYER_FIELD_COINAGE": 1441,
|
||||||
|
"PLAYER_QUEST_LOG_START": 244,
|
||||||
|
"PLAYER_FIELD_INV_SLOT_HEAD": 650,
|
||||||
|
"PLAYER_FIELD_PACK_SLOT_1": 696,
|
||||||
|
"PLAYER_FIELD_BANK_SLOT_1": 728,
|
||||||
|
"PLAYER_FIELD_BANKBAG_SLOT_1": 784,
|
||||||
|
"PLAYER_SKILL_INFO_START": 928,
|
||||||
|
"PLAYER_EXPLORED_ZONES_START": 1312,
|
||||||
|
"GAMEOBJECT_DISPLAYID": 8,
|
||||||
|
"ITEM_FIELD_STACK_COUNT": 14,
|
||||||
|
"ITEM_FIELD_DURABILITY": 60,
|
||||||
|
"ITEM_FIELD_MAXDURABILITY": 61,
|
||||||
|
"CONTAINER_FIELD_NUM_SLOTS": 64,
|
||||||
|
"CONTAINER_FIELD_SLOT_1": 66
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,304 +1,98 @@
|
||||||
{
|
{
|
||||||
"AreaTable": {
|
"Spell": {
|
||||||
"ExploreFlag": 3,
|
"ID": 0, "Attributes": 5, "IconID": 117,
|
||||||
"ID": 0,
|
"Name": 120, "Tooltip": 147, "Rank": 129, "SchoolEnum": 1,
|
||||||
"MapID": 1,
|
"CastingTimeIndex": 15, "PowerType": 28, "ManaCost": 29, "RangeIndex": 33
|
||||||
"ParentAreaNum": 2
|
|
||||||
},
|
},
|
||||||
"CharHairGeosets": {
|
"SpellRange": { "MaxRange": 2 },
|
||||||
"GeosetID": 4,
|
"ItemDisplayInfo": {
|
||||||
"RaceID": 1,
|
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||||
"SexID": 2,
|
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||||
"Variation": 3
|
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||||
|
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||||
|
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||||
},
|
},
|
||||||
"CharSections": {
|
"CharSections": {
|
||||||
"BaseSection": 3,
|
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||||
"ColorIndex": 5,
|
"VariationIndex": 4, "ColorIndex": 5,
|
||||||
"Flags": 9,
|
"Texture1": 6, "Texture2": 7, "Texture3": 8,
|
||||||
"RaceID": 1,
|
"Flags": 9
|
||||||
"SexID": 2,
|
|
||||||
"Texture1": 6,
|
|
||||||
"Texture2": 7,
|
|
||||||
"Texture3": 8,
|
|
||||||
"VariationIndex": 4
|
|
||||||
},
|
},
|
||||||
"CharacterFacialHairStyles": {
|
"SpellIcon": { "ID": 0, "Path": 1 },
|
||||||
"Geoset100": 3,
|
"FactionTemplate": {
|
||||||
"Geoset200": 5,
|
"ID": 0, "Faction": 1, "FactionGroup": 3,
|
||||||
"Geoset300": 4,
|
"FriendGroup": 4, "EnemyGroup": 5,
|
||||||
"RaceID": 0,
|
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
|
||||||
"SexID": 1,
|
|
||||||
"Variation": 2
|
|
||||||
},
|
|
||||||
"CreatureDisplayInfo": {
|
|
||||||
"ExtraDisplayId": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"ModelID": 1,
|
|
||||||
"Skin1": 6,
|
|
||||||
"Skin2": 7,
|
|
||||||
"Skin3": 8
|
|
||||||
},
|
|
||||||
"CreatureDisplayInfoExtra": {
|
|
||||||
"BakeName": 18,
|
|
||||||
"EquipDisplay0": 8,
|
|
||||||
"EquipDisplay1": 9,
|
|
||||||
"EquipDisplay2": 10,
|
|
||||||
"EquipDisplay3": 11,
|
|
||||||
"EquipDisplay4": 12,
|
|
||||||
"EquipDisplay5": 13,
|
|
||||||
"EquipDisplay6": 14,
|
|
||||||
"EquipDisplay7": 15,
|
|
||||||
"EquipDisplay8": 16,
|
|
||||||
"EquipDisplay9": 17,
|
|
||||||
"FaceID": 4,
|
|
||||||
"FacialHairID": 7,
|
|
||||||
"HairColorID": 6,
|
|
||||||
"HairStyleID": 5,
|
|
||||||
"ID": 0,
|
|
||||||
"RaceID": 1,
|
|
||||||
"SexID": 2,
|
|
||||||
"SkinID": 3
|
|
||||||
},
|
|
||||||
"CreatureModelData": {
|
|
||||||
"ID": 0,
|
|
||||||
"ModelPath": 2
|
|
||||||
},
|
|
||||||
"Emotes": {
|
|
||||||
"AnimID": 2,
|
|
||||||
"ID": 0
|
|
||||||
},
|
|
||||||
"EmotesText": {
|
|
||||||
"Command": 1,
|
|
||||||
"EmoteRef": 2,
|
|
||||||
"ID": 0,
|
|
||||||
"OthersNoTargetTextID": 7,
|
|
||||||
"OthersTargetTextID": 3,
|
|
||||||
"SenderNoTargetTextID": 9,
|
|
||||||
"SenderTargetTextID": 5
|
|
||||||
},
|
|
||||||
"EmotesTextData": {
|
|
||||||
"ID": 0,
|
|
||||||
"Text": 1
|
|
||||||
},
|
},
|
||||||
"Faction": {
|
"Faction": {
|
||||||
"ID": 0,
|
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
|
||||||
"ReputationBase0": 10,
|
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
|
||||||
"ReputationBase1": 11,
|
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||||
"ReputationBase2": 12,
|
"ReputationBase2": 12, "ReputationBase3": 13
|
||||||
"ReputationBase3": 13,
|
|
||||||
"ReputationRaceMask0": 2,
|
|
||||||
"ReputationRaceMask1": 3,
|
|
||||||
"ReputationRaceMask2": 4,
|
|
||||||
"ReputationRaceMask3": 5
|
|
||||||
},
|
},
|
||||||
"FactionTemplate": {
|
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||||
"Enemy0": 6,
|
"CreatureDisplayInfoExtra": {
|
||||||
"Enemy1": 7,
|
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
||||||
"Enemy2": 8,
|
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
|
||||||
"Enemy3": 9,
|
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
|
||||||
"EnemyGroup": 5,
|
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
|
||||||
"Faction": 1,
|
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
|
||||||
"FactionGroup": 3,
|
"EquipDisplay9": 17, "BakeName": 18
|
||||||
"FriendGroup": 4,
|
|
||||||
"ID": 0
|
|
||||||
},
|
},
|
||||||
"GameObjectDisplayInfo": {
|
"CreatureDisplayInfo": {
|
||||||
"ID": 0,
|
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
|
||||||
"ModelName": 1
|
"Skin1": 6, "Skin2": 7, "Skin3": 8
|
||||||
},
|
|
||||||
"ItemDisplayInfo": {
|
|
||||||
"GeosetGroup1": 7,
|
|
||||||
"GeosetGroup3": 9,
|
|
||||||
"ID": 0,
|
|
||||||
"InventoryIcon": 5,
|
|
||||||
"LeftModel": 1,
|
|
||||||
"LeftModelTexture": 3,
|
|
||||||
"TextureArmLower": 15,
|
|
||||||
"TextureArmUpper": 14,
|
|
||||||
"TextureFoot": 21,
|
|
||||||
"TextureHand": 16,
|
|
||||||
"TextureLegLower": 20,
|
|
||||||
"TextureLegUpper": 19,
|
|
||||||
"TextureTorsoLower": 18,
|
|
||||||
"TextureTorsoUpper": 17
|
|
||||||
},
|
|
||||||
"ItemSet": {
|
|
||||||
"ID": 0,
|
|
||||||
"Item0": 10,
|
|
||||||
"Item1": 11,
|
|
||||||
"Item2": 12,
|
|
||||||
"Item3": 13,
|
|
||||||
"Item4": 14,
|
|
||||||
"Item5": 15,
|
|
||||||
"Item6": 16,
|
|
||||||
"Item7": 17,
|
|
||||||
"Item8": 18,
|
|
||||||
"Item9": 19,
|
|
||||||
"Name": 1,
|
|
||||||
"Spell0": 20,
|
|
||||||
"Spell1": 21,
|
|
||||||
"Spell2": 22,
|
|
||||||
"Spell3": 23,
|
|
||||||
"Spell4": 24,
|
|
||||||
"Spell5": 25,
|
|
||||||
"Spell6": 26,
|
|
||||||
"Spell7": 27,
|
|
||||||
"Spell8": 28,
|
|
||||||
"Spell9": 29,
|
|
||||||
"Threshold0": 30,
|
|
||||||
"Threshold1": 31,
|
|
||||||
"Threshold2": 32,
|
|
||||||
"Threshold3": 33,
|
|
||||||
"Threshold4": 34,
|
|
||||||
"Threshold5": 35,
|
|
||||||
"Threshold6": 36,
|
|
||||||
"Threshold7": 37,
|
|
||||||
"Threshold8": 38,
|
|
||||||
"Threshold9": 39
|
|
||||||
},
|
|
||||||
"Light": {
|
|
||||||
"ID": 0,
|
|
||||||
"InnerRadius": 5,
|
|
||||||
"LightParamsID": 7,
|
|
||||||
"LightParamsIDRain": 8,
|
|
||||||
"LightParamsIDUnderwater": 9,
|
|
||||||
"MapID": 1,
|
|
||||||
"OuterRadius": 6,
|
|
||||||
"X": 2,
|
|
||||||
"Y": 4,
|
|
||||||
"Z": 3
|
|
||||||
},
|
|
||||||
"LightFloatBand": {
|
|
||||||
"BlockIndex": 1,
|
|
||||||
"NumKeyframes": 2,
|
|
||||||
"TimeKey0": 3,
|
|
||||||
"Value0": 19
|
|
||||||
},
|
|
||||||
"LightIntBand": {
|
|
||||||
"BlockIndex": 1,
|
|
||||||
"NumKeyframes": 2,
|
|
||||||
"TimeKey0": 3,
|
|
||||||
"Value0": 19
|
|
||||||
},
|
|
||||||
"LightParams": {
|
|
||||||
"LightParamsID": 0
|
|
||||||
},
|
|
||||||
"Map": {
|
|
||||||
"ID": 0,
|
|
||||||
"InternalName": 1
|
|
||||||
},
|
|
||||||
"SkillLine": {
|
|
||||||
"Category": 1,
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 3
|
|
||||||
},
|
|
||||||
"SkillLineAbility": {
|
|
||||||
"SkillLineID": 1,
|
|
||||||
"SpellID": 2
|
|
||||||
},
|
|
||||||
"Spell": {
|
|
||||||
"Attributes": 5,
|
|
||||||
"AttributesEx": 6,
|
|
||||||
"CastingTimeIndex": 15,
|
|
||||||
"DispelType": 4,
|
|
||||||
"DurationIndex": 40,
|
|
||||||
"EffectBasePoints0": 80,
|
|
||||||
"EffectBasePoints1": 81,
|
|
||||||
"EffectBasePoints2": 82,
|
|
||||||
"ID": 0,
|
|
||||||
"IconID": 117,
|
|
||||||
"ManaCost": 29,
|
|
||||||
"Name": 120,
|
|
||||||
"PowerType": 28,
|
|
||||||
"RangeIndex": 33,
|
|
||||||
"Rank": 129,
|
|
||||||
"SchoolEnum": 1,
|
|
||||||
"SpellVisualID": 115,
|
|
||||||
"Tooltip": 147
|
|
||||||
},
|
|
||||||
"SpellIcon": {
|
|
||||||
"ID": 0,
|
|
||||||
"Path": 1
|
|
||||||
},
|
|
||||||
"SpellItemEnchantment": {
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 8
|
|
||||||
},
|
|
||||||
"SpellRange": {
|
|
||||||
"MaxRange": 2
|
|
||||||
},
|
|
||||||
"SpellVisual": {
|
|
||||||
"CastKit": 2,
|
|
||||||
"ID": 0,
|
|
||||||
"ImpactKit": 3,
|
|
||||||
"MissileModel": 8,
|
|
||||||
"PrecastKit": 1
|
|
||||||
},
|
|
||||||
"SpellVisualEffectName": {
|
|
||||||
"FilePath": 2,
|
|
||||||
"ID": 0
|
|
||||||
},
|
|
||||||
"SpellVisualKit": {
|
|
||||||
"BaseEffect": 5,
|
|
||||||
"BreathEffect": 8,
|
|
||||||
"ChestEffect": 4,
|
|
||||||
"HeadEffect": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"LeftHandEffect": 6,
|
|
||||||
"RightHandEffect": 7,
|
|
||||||
"SpecialEffect0": 11,
|
|
||||||
"SpecialEffect1": 12,
|
|
||||||
"SpecialEffect2": 13
|
|
||||||
},
|
|
||||||
"Talent": {
|
|
||||||
"Column": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"PrereqRank0": 12,
|
|
||||||
"PrereqTalent0": 9,
|
|
||||||
"RankSpell0": 4,
|
|
||||||
"Row": 2,
|
|
||||||
"TabID": 1
|
|
||||||
},
|
|
||||||
"TalentTab": {
|
|
||||||
"BackgroundFile": 15,
|
|
||||||
"ClassMask": 12,
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 1,
|
|
||||||
"OrderIndex": 14
|
|
||||||
},
|
},
|
||||||
"TaxiNodes": {
|
"TaxiNodes": {
|
||||||
"ID": 0,
|
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5
|
||||||
"MapID": 1,
|
|
||||||
"Name": 5,
|
|
||||||
"X": 2,
|
|
||||||
"Y": 3,
|
|
||||||
"Z": 4
|
|
||||||
},
|
|
||||||
"TaxiPath": {
|
|
||||||
"Cost": 3,
|
|
||||||
"FromNode": 1,
|
|
||||||
"ID": 0,
|
|
||||||
"ToNode": 2
|
|
||||||
},
|
},
|
||||||
|
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
|
||||||
"TaxiPathNode": {
|
"TaxiPathNode": {
|
||||||
"ID": 0,
|
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
|
||||||
"MapID": 3,
|
"X": 4, "Y": 5, "Z": 6
|
||||||
"NodeIndex": 2,
|
},
|
||||||
"PathID": 1,
|
"TalentTab": {
|
||||||
"X": 4,
|
"ID": 0, "Name": 1, "ClassMask": 12,
|
||||||
"Y": 5,
|
"OrderIndex": 14, "BackgroundFile": 15
|
||||||
"Z": 6
|
},
|
||||||
|
"Talent": {
|
||||||
|
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
|
||||||
|
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
|
||||||
|
},
|
||||||
|
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
|
||||||
|
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
|
||||||
|
"Map": { "ID": 0, "InternalName": 1 },
|
||||||
|
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
|
||||||
|
"CharHairGeosets": {
|
||||||
|
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
|
||||||
|
},
|
||||||
|
"CharacterFacialHairStyles": {
|
||||||
|
"RaceID": 0, "SexID": 1, "Variation": 2,
|
||||||
|
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
|
||||||
|
},
|
||||||
|
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
|
||||||
|
"Emotes": { "ID": 0, "AnimID": 2 },
|
||||||
|
"EmotesText": {
|
||||||
|
"ID": 0, "Command": 1, "EmoteRef": 2,
|
||||||
|
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
|
||||||
|
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
|
||||||
|
},
|
||||||
|
"EmotesTextData": { "ID": 0, "Text": 1 },
|
||||||
|
"Light": {
|
||||||
|
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
|
||||||
|
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
|
||||||
|
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
|
||||||
|
},
|
||||||
|
"LightParams": { "LightParamsID": 0 },
|
||||||
|
"LightIntBand": {
|
||||||
|
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||||
|
},
|
||||||
|
"LightFloatBand": {
|
||||||
|
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||||
},
|
},
|
||||||
"WorldMapArea": {
|
"WorldMapArea": {
|
||||||
"AreaID": 2,
|
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
|
||||||
"AreaName": 3,
|
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
|
||||||
"DisplayMapID": 8,
|
"DisplayMapID": 8, "ParentWorldMapID": 10
|
||||||
"ID": 0,
|
|
||||||
"LocBottom": 7,
|
|
||||||
"LocLeft": 4,
|
|
||||||
"LocRight": 5,
|
|
||||||
"LocTop": 6,
|
|
||||||
"MapID": 1,
|
|
||||||
"ParentWorldMapID": 10
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,300 @@
|
||||||
{
|
{
|
||||||
"_extends": "../classic/opcodes.json",
|
"CMSG_PING": "0x1DC",
|
||||||
"_remove": [
|
"CMSG_AUTH_SESSION": "0x1ED",
|
||||||
"MSG_SET_DUNGEON_DIFFICULTY"
|
"CMSG_CHAR_CREATE": "0x036",
|
||||||
]
|
"CMSG_CHAR_ENUM": "0x037",
|
||||||
|
"CMSG_CHAR_DELETE": "0x038",
|
||||||
|
"CMSG_PLAYER_LOGIN": "0x03D",
|
||||||
|
"MSG_MOVE_START_FORWARD": "0x0B5",
|
||||||
|
"MSG_MOVE_START_BACKWARD": "0x0B6",
|
||||||
|
"MSG_MOVE_STOP": "0x0B7",
|
||||||
|
"MSG_MOVE_START_STRAFE_LEFT": "0x0B8",
|
||||||
|
"MSG_MOVE_START_STRAFE_RIGHT": "0x0B9",
|
||||||
|
"MSG_MOVE_STOP_STRAFE": "0x0BA",
|
||||||
|
"MSG_MOVE_JUMP": "0x0BB",
|
||||||
|
"MSG_MOVE_START_TURN_LEFT": "0x0BC",
|
||||||
|
"MSG_MOVE_START_TURN_RIGHT": "0x0BD",
|
||||||
|
"MSG_MOVE_STOP_TURN": "0x0BE",
|
||||||
|
"MSG_MOVE_SET_FACING": "0x0DA",
|
||||||
|
"MSG_MOVE_FALL_LAND": "0x0C9",
|
||||||
|
"MSG_MOVE_START_SWIM": "0x0CA",
|
||||||
|
"MSG_MOVE_STOP_SWIM": "0x0CB",
|
||||||
|
"MSG_MOVE_HEARTBEAT": "0x0EE",
|
||||||
|
"SMSG_AUTH_CHALLENGE": "0x1EC",
|
||||||
|
"SMSG_AUTH_RESPONSE": "0x1EE",
|
||||||
|
"SMSG_CHAR_CREATE": "0x03A",
|
||||||
|
"SMSG_CHAR_ENUM": "0x03B",
|
||||||
|
"SMSG_CHAR_DELETE": "0x03C",
|
||||||
|
"SMSG_CHARACTER_LOGIN_FAILED": "0x041",
|
||||||
|
"SMSG_PONG": "0x1DD",
|
||||||
|
"SMSG_LOGIN_VERIFY_WORLD": "0x236",
|
||||||
|
"SMSG_INIT_WORLD_STATES": "0x2C2",
|
||||||
|
"SMSG_LOGIN_SETTIMESPEED": "0x042",
|
||||||
|
"SMSG_TUTORIAL_FLAGS": "0x0FD",
|
||||||
|
"SMSG_INITIALIZE_FACTIONS": "0x122",
|
||||||
|
"SMSG_WARDEN_DATA": "0x2E6",
|
||||||
|
"CMSG_WARDEN_DATA": "0x2E7",
|
||||||
|
"SMSG_NOTIFICATION": "0x1CB",
|
||||||
|
"SMSG_ACCOUNT_DATA_TIMES": "0x209",
|
||||||
|
"SMSG_UPDATE_OBJECT": "0x0A9",
|
||||||
|
"SMSG_COMPRESSED_UPDATE_OBJECT": "0x1F6",
|
||||||
|
"SMSG_PARTYKILLLOG": "0x1F5",
|
||||||
|
"SMSG_MONSTER_MOVE_TRANSPORT": "0x2AE",
|
||||||
|
"SMSG_SPLINE_MOVE_SET_WALK_MODE": "0x30E",
|
||||||
|
"SMSG_SPLINE_MOVE_SET_RUN_MODE": "0x30D",
|
||||||
|
"SMSG_SPLINE_SET_RUN_SPEED": "0x2FE",
|
||||||
|
"SMSG_SPLINE_SET_RUN_BACK_SPEED": "0x2FF",
|
||||||
|
"SMSG_SPLINE_SET_SWIM_SPEED": "0x300",
|
||||||
|
"SMSG_DESTROY_OBJECT": "0x0AA",
|
||||||
|
"CMSG_MESSAGECHAT": "0x095",
|
||||||
|
"SMSG_MESSAGECHAT": "0x096",
|
||||||
|
"CMSG_WHO": "0x062",
|
||||||
|
"SMSG_WHO": "0x063",
|
||||||
|
"CMSG_PLAYED_TIME": "0x1CC",
|
||||||
|
"SMSG_PLAYED_TIME": "0x1CD",
|
||||||
|
"CMSG_QUERY_TIME": "0x1CE",
|
||||||
|
"SMSG_QUERY_TIME_RESPONSE": "0x1CF",
|
||||||
|
"SMSG_FRIEND_STATUS": "0x068",
|
||||||
|
"SMSG_CONTACT_LIST": "0x067",
|
||||||
|
"CMSG_ADD_FRIEND": "0x069",
|
||||||
|
"CMSG_DEL_FRIEND": "0x06A",
|
||||||
|
"CMSG_ADD_IGNORE": "0x06C",
|
||||||
|
"CMSG_DEL_IGNORE": "0x06D",
|
||||||
|
"CMSG_PLAYER_LOGOUT": "0x04A",
|
||||||
|
"CMSG_LOGOUT_REQUEST": "0x04B",
|
||||||
|
"CMSG_LOGOUT_CANCEL": "0x04E",
|
||||||
|
"SMSG_LOGOUT_RESPONSE": "0x04C",
|
||||||
|
"SMSG_LOGOUT_COMPLETE": "0x04D",
|
||||||
|
"CMSG_STANDSTATECHANGE": "0x101",
|
||||||
|
"CMSG_SHOWING_HELM": "0x2B9",
|
||||||
|
"CMSG_SHOWING_CLOAK": "0x2BA",
|
||||||
|
"CMSG_TOGGLE_PVP": "0x253",
|
||||||
|
"CMSG_GUILD_INVITE": "0x082",
|
||||||
|
"CMSG_GUILD_ACCEPT": "0x084",
|
||||||
|
"CMSG_GUILD_DECLINE": "0x085",
|
||||||
|
"CMSG_GUILD_INFO": "0x087",
|
||||||
|
"CMSG_GUILD_ROSTER": "0x089",
|
||||||
|
"CMSG_GUILD_PROMOTE": "0x08B",
|
||||||
|
"CMSG_GUILD_DEMOTE": "0x08C",
|
||||||
|
"CMSG_GUILD_LEAVE": "0x08D",
|
||||||
|
"CMSG_GUILD_MOTD": "0x091",
|
||||||
|
"SMSG_GUILD_INFO": "0x088",
|
||||||
|
"SMSG_GUILD_ROSTER": "0x08A",
|
||||||
|
"CMSG_GUILD_QUERY": "0x054",
|
||||||
|
"SMSG_GUILD_QUERY_RESPONSE": "0x055",
|
||||||
|
"SMSG_GUILD_INVITE": "0x083",
|
||||||
|
"CMSG_GUILD_REMOVE": "0x08E",
|
||||||
|
"SMSG_GUILD_EVENT": "0x092",
|
||||||
|
"SMSG_GUILD_COMMAND_RESULT": "0x093",
|
||||||
|
"MSG_RAID_READY_CHECK": "0x322",
|
||||||
|
"SMSG_ITEM_PUSH_RESULT": "0x166",
|
||||||
|
"CMSG_DUEL_ACCEPTED": "0x16C",
|
||||||
|
"CMSG_DUEL_CANCELLED": "0x16D",
|
||||||
|
"SMSG_DUEL_REQUESTED": "0x167",
|
||||||
|
"CMSG_INITIATE_TRADE": "0x116",
|
||||||
|
"MSG_RANDOM_ROLL": "0x1FB",
|
||||||
|
"CMSG_SET_SELECTION": "0x13D",
|
||||||
|
"CMSG_NAME_QUERY": "0x050",
|
||||||
|
"SMSG_NAME_QUERY_RESPONSE": "0x051",
|
||||||
|
"CMSG_CREATURE_QUERY": "0x060",
|
||||||
|
"SMSG_CREATURE_QUERY_RESPONSE": "0x061",
|
||||||
|
"CMSG_GAMEOBJECT_QUERY": "0x05E",
|
||||||
|
"SMSG_GAMEOBJECT_QUERY_RESPONSE": "0x05F",
|
||||||
|
"CMSG_SET_ACTIVE_MOVER": "0x26A",
|
||||||
|
"CMSG_BINDER_ACTIVATE": "0x1B5",
|
||||||
|
"SMSG_LOG_XPGAIN": "0x1D0",
|
||||||
|
"_NOTE_MONSTER_MOVE": "These look swapped vs vanilla (0x0DD/0x2FB) but may be intentional Turtle WoW changes. Check if NPC movement breaks.",
|
||||||
|
"SMSG_MONSTER_MOVE": "0x2FB",
|
||||||
|
"SMSG_COMPRESSED_MOVES": "0x06B",
|
||||||
|
"CMSG_ATTACKSWING": "0x141",
|
||||||
|
"CMSG_ATTACKSTOP": "0x142",
|
||||||
|
"SMSG_ATTACKSTART": "0x143",
|
||||||
|
"SMSG_ATTACKSTOP": "0x144",
|
||||||
|
"SMSG_ATTACKERSTATEUPDATE": "0x14A",
|
||||||
|
"SMSG_AI_REACTION": "0x13C",
|
||||||
|
"SMSG_SPELLNONMELEEDAMAGELOG": "0x250",
|
||||||
|
"SMSG_PLAY_SPELL_VISUAL": "0x1F3",
|
||||||
|
"SMSG_SPELLHEALLOG": "0x150",
|
||||||
|
"SMSG_SPELLENERGIZELOG": "0x151",
|
||||||
|
"SMSG_PERIODICAURALOG": "0x24E",
|
||||||
|
"SMSG_ENVIRONMENTAL_DAMAGE_LOG": "0x1FC",
|
||||||
|
"CMSG_CAST_SPELL": "0x12E",
|
||||||
|
"CMSG_CANCEL_CAST": "0x12F",
|
||||||
|
"CMSG_CANCEL_AURA": "0x136",
|
||||||
|
"SMSG_CAST_FAILED": "0x130",
|
||||||
|
"SMSG_SPELL_START": "0x131",
|
||||||
|
"SMSG_SPELL_GO": "0x132",
|
||||||
|
"SMSG_SPELL_FAILURE": "0x133",
|
||||||
|
"SMSG_SPELL_COOLDOWN": "0x134",
|
||||||
|
"SMSG_COOLDOWN_EVENT": "0x135",
|
||||||
|
"SMSG_EQUIPMENT_SET_SAVED": "0x137",
|
||||||
|
"SMSG_INITIAL_SPELLS": "0x12A",
|
||||||
|
"SMSG_LEARNED_SPELL": "0x12B",
|
||||||
|
"SMSG_SUPERCEDED_SPELL": "0x12C",
|
||||||
|
"SMSG_REMOVED_SPELL": "0x203",
|
||||||
|
"SMSG_SPELL_DELAYED": "0x1E2",
|
||||||
|
"SMSG_SET_FLAT_SPELL_MODIFIER": "0x266",
|
||||||
|
"SMSG_SET_PCT_SPELL_MODIFIER": "0x267",
|
||||||
|
"CMSG_LEARN_TALENT": "0x251",
|
||||||
|
"MSG_TALENT_WIPE_CONFIRM": "0x2AA",
|
||||||
|
"CMSG_GROUP_INVITE": "0x06E",
|
||||||
|
"SMSG_GROUP_INVITE": "0x06F",
|
||||||
|
"CMSG_GROUP_ACCEPT": "0x072",
|
||||||
|
"CMSG_GROUP_DECLINE": "0x073",
|
||||||
|
"SMSG_GROUP_DECLINE": "0x074",
|
||||||
|
"CMSG_GROUP_UNINVITE_GUID": "0x076",
|
||||||
|
"SMSG_GROUP_UNINVITE": "0x077",
|
||||||
|
"CMSG_GROUP_SET_LEADER": "0x078",
|
||||||
|
"SMSG_GROUP_SET_LEADER": "0x079",
|
||||||
|
"CMSG_GROUP_DISBAND": "0x07B",
|
||||||
|
"SMSG_GROUP_LIST": "0x07D",
|
||||||
|
"SMSG_PARTY_COMMAND_RESULT": "0x07F",
|
||||||
|
"MSG_RAID_TARGET_UPDATE": "0x321",
|
||||||
|
"CMSG_REQUEST_RAID_INFO": "0x2CD",
|
||||||
|
"SMSG_RAID_INSTANCE_INFO": "0x2CC",
|
||||||
|
"CMSG_AUTOSTORE_LOOT_ITEM": "0x108",
|
||||||
|
"CMSG_LOOT": "0x15D",
|
||||||
|
"CMSG_LOOT_MONEY": "0x15E",
|
||||||
|
"CMSG_LOOT_RELEASE": "0x15F",
|
||||||
|
"SMSG_LOOT_RESPONSE": "0x160",
|
||||||
|
"SMSG_LOOT_RELEASE_RESPONSE": "0x161",
|
||||||
|
"SMSG_LOOT_REMOVED": "0x162",
|
||||||
|
"SMSG_LOOT_MONEY_NOTIFY": "0x163",
|
||||||
|
"SMSG_LOOT_CLEAR_MONEY": "0x165",
|
||||||
|
"CMSG_ACTIVATETAXI": "0x1AD",
|
||||||
|
"CMSG_GOSSIP_HELLO": "0x17B",
|
||||||
|
"CMSG_GOSSIP_SELECT_OPTION": "0x17C",
|
||||||
|
"SMSG_GOSSIP_MESSAGE": "0x17D",
|
||||||
|
"SMSG_GOSSIP_COMPLETE": "0x17E",
|
||||||
|
"SMSG_NPC_TEXT_UPDATE": "0x180",
|
||||||
|
"CMSG_GAMEOBJ_USE": "0x0B1",
|
||||||
|
"CMSG_QUESTGIVER_STATUS_QUERY": "0x182",
|
||||||
|
"SMSG_QUESTGIVER_STATUS": "0x183",
|
||||||
|
"CMSG_QUESTGIVER_HELLO": "0x184",
|
||||||
|
"SMSG_QUESTGIVER_QUEST_LIST": "0x185",
|
||||||
|
"CMSG_QUESTGIVER_QUERY_QUEST": "0x186",
|
||||||
|
"SMSG_QUESTGIVER_QUEST_DETAILS": "0x188",
|
||||||
|
"CMSG_QUESTGIVER_ACCEPT_QUEST": "0x189",
|
||||||
|
"CMSG_QUESTGIVER_COMPLETE_QUEST": "0x18A",
|
||||||
|
"SMSG_QUESTGIVER_REQUEST_ITEMS": "0x18B",
|
||||||
|
"CMSG_QUESTGIVER_REQUEST_REWARD": "0x18C",
|
||||||
|
"SMSG_QUESTGIVER_OFFER_REWARD": "0x18D",
|
||||||
|
"CMSG_QUESTGIVER_CHOOSE_REWARD": "0x18E",
|
||||||
|
"SMSG_QUESTGIVER_QUEST_INVALID": "0x18F",
|
||||||
|
"SMSG_QUESTGIVER_QUEST_COMPLETE": "0x191",
|
||||||
|
"CMSG_QUESTLOG_REMOVE_QUEST": "0x194",
|
||||||
|
"SMSG_QUESTUPDATE_ADD_KILL": "0x199",
|
||||||
|
"SMSG_QUESTUPDATE_COMPLETE": "0x198",
|
||||||
|
"SMSG_QUEST_FORCE_REMOVE": "0x21E",
|
||||||
|
"CMSG_QUEST_QUERY": "0x05C",
|
||||||
|
"SMSG_QUEST_QUERY_RESPONSE": "0x05D",
|
||||||
|
"SMSG_QUESTLOG_FULL": "0x195",
|
||||||
|
"CMSG_LIST_INVENTORY": "0x19E",
|
||||||
|
"SMSG_LIST_INVENTORY": "0x19F",
|
||||||
|
"CMSG_SELL_ITEM": "0x1A0",
|
||||||
|
"SMSG_SELL_ITEM": "0x1A1",
|
||||||
|
"CMSG_BUY_ITEM": "0x1A2",
|
||||||
|
"CMSG_BUYBACK_ITEM": "0x1A6",
|
||||||
|
"SMSG_BUY_FAILED": "0x1A5",
|
||||||
|
"CMSG_TRAINER_LIST": "0x1B0",
|
||||||
|
"SMSG_TRAINER_LIST": "0x1B1",
|
||||||
|
"CMSG_TRAINER_BUY_SPELL": "0x1B2",
|
||||||
|
"SMSG_TRAINER_BUY_FAILED": "0x1B4",
|
||||||
|
"CMSG_ITEM_QUERY_SINGLE": "0x056",
|
||||||
|
"SMSG_ITEM_QUERY_SINGLE_RESPONSE": "0x058",
|
||||||
|
"CMSG_USE_ITEM": "0x0AB",
|
||||||
|
"CMSG_AUTOEQUIP_ITEM": "0x10A",
|
||||||
|
"CMSG_SWAP_ITEM": "0x10C",
|
||||||
|
"CMSG_SWAP_INV_ITEM": "0x10D",
|
||||||
|
"SMSG_INVENTORY_CHANGE_FAILURE": "0x112",
|
||||||
|
"CMSG_INSPECT": "0x114",
|
||||||
|
"SMSG_INSPECT_RESULTS_UPDATE": "0x115",
|
||||||
|
"CMSG_REPOP_REQUEST": "0x15A",
|
||||||
|
"SMSG_RESURRECT_REQUEST": "0x15B",
|
||||||
|
"CMSG_RESURRECT_RESPONSE": "0x15C",
|
||||||
|
"CMSG_SPIRIT_HEALER_ACTIVATE": "0x21C",
|
||||||
|
"SMSG_SPIRIT_HEALER_CONFIRM": "0x222",
|
||||||
|
"MSG_MOVE_TELEPORT_ACK": "0x0C7",
|
||||||
|
"SMSG_TRANSFER_PENDING": "0x03F",
|
||||||
|
"SMSG_NEW_WORLD": "0x03E",
|
||||||
|
"MSG_MOVE_WORLDPORT_ACK": "0x0DC",
|
||||||
|
"SMSG_TRANSFER_ABORTED": "0x040",
|
||||||
|
"SMSG_FORCE_RUN_SPEED_CHANGE": "0x0E2",
|
||||||
|
"SMSG_CLIENT_CONTROL_UPDATE": "0x159",
|
||||||
|
"CMSG_FORCE_RUN_SPEED_CHANGE_ACK": "0x0E3",
|
||||||
|
"SMSG_SHOWTAXINODES": "0x1A9",
|
||||||
|
"SMSG_ACTIVATETAXIREPLY": "0x1AE",
|
||||||
|
"SMSG_NEW_TAXI_PATH": "0x1AF",
|
||||||
|
"CMSG_ACTIVATETAXIEXPRESS": "0x312",
|
||||||
|
"CMSG_TAXINODE_STATUS_QUERY": "0x1AA",
|
||||||
|
"SMSG_TAXINODE_STATUS": "0x1AB",
|
||||||
|
"SMSG_TRAINER_BUY_SUCCEEDED": "0x1B3",
|
||||||
|
"SMSG_BINDPOINTUPDATE": "0x155",
|
||||||
|
"SMSG_SET_PROFICIENCY": "0x127",
|
||||||
|
"SMSG_ACTION_BUTTONS": "0x129",
|
||||||
|
"SMSG_LEVELUP_INFO": "0x1D4",
|
||||||
|
"SMSG_PLAY_SOUND": "0x2D2",
|
||||||
|
"CMSG_UPDATE_ACCOUNT_DATA": "0x20B",
|
||||||
|
"CMSG_BATTLEFIELD_LIST": "0x23C",
|
||||||
|
"SMSG_BATTLEFIELD_LIST": "0x23D",
|
||||||
|
"CMSG_BATTLEFIELD_JOIN": "0x23E",
|
||||||
|
"CMSG_BATTLEFIELD_STATUS": "0x2D3",
|
||||||
|
"SMSG_BATTLEFIELD_STATUS": "0x2D4",
|
||||||
|
"CMSG_BATTLEFIELD_PORT": "0x2D5",
|
||||||
|
"CMSG_BATTLEMASTER_HELLO": "0x2D7",
|
||||||
|
"MSG_PVP_LOG_DATA": "0x2E0",
|
||||||
|
"CMSG_LEAVE_BATTLEFIELD": "0x2E1",
|
||||||
|
"SMSG_GROUP_JOINED_BATTLEGROUND": "0x2E8",
|
||||||
|
"MSG_BATTLEGROUND_PLAYER_POSITIONS": "0x2E9",
|
||||||
|
"SMSG_BATTLEGROUND_PLAYER_JOINED": "0x2EC",
|
||||||
|
"SMSG_BATTLEGROUND_PLAYER_LEFT": "0x2ED",
|
||||||
|
"CMSG_BATTLEMASTER_JOIN": "0x2EE",
|
||||||
|
"CMSG_EMOTE": "0x102",
|
||||||
|
"SMSG_EMOTE": "0x103",
|
||||||
|
"CMSG_TEXT_EMOTE": "0x104",
|
||||||
|
"SMSG_TEXT_EMOTE": "0x105",
|
||||||
|
"CMSG_JOIN_CHANNEL": "0x097",
|
||||||
|
"CMSG_LEAVE_CHANNEL": "0x098",
|
||||||
|
"SMSG_CHANNEL_NOTIFY": "0x099",
|
||||||
|
"CMSG_CHANNEL_LIST": "0x09A",
|
||||||
|
"SMSG_CHANNEL_LIST": "0x09B",
|
||||||
|
"SMSG_INSPECT_TALENT": "0x3F4",
|
||||||
|
"SMSG_SHOW_MAILBOX": "0x297",
|
||||||
|
"CMSG_GET_MAIL_LIST": "0x23A",
|
||||||
|
"SMSG_MAIL_LIST_RESULT": "0x23B",
|
||||||
|
"CMSG_SEND_MAIL": "0x238",
|
||||||
|
"SMSG_SEND_MAIL_RESULT": "0x239",
|
||||||
|
"CMSG_MAIL_TAKE_MONEY": "0x245",
|
||||||
|
"CMSG_MAIL_TAKE_ITEM": "0x246",
|
||||||
|
"CMSG_MAIL_DELETE": "0x249",
|
||||||
|
"CMSG_MAIL_MARK_AS_READ": "0x247",
|
||||||
|
"SMSG_RECEIVED_MAIL": "0x285",
|
||||||
|
"MSG_QUERY_NEXT_MAIL_TIME": "0x284",
|
||||||
|
"CMSG_BANKER_ACTIVATE": "0x1B7",
|
||||||
|
"SMSG_SHOW_BANK": "0x1B8",
|
||||||
|
"CMSG_BUY_BANK_SLOT": "0x1B9",
|
||||||
|
"SMSG_BUY_BANK_SLOT_RESULT": "0x1BA",
|
||||||
|
"CMSG_AUTOSTORE_BANK_ITEM": "0x282",
|
||||||
|
"CMSG_AUTOBANK_ITEM": "0x283",
|
||||||
|
"MSG_AUCTION_HELLO": "0x255",
|
||||||
|
"CMSG_AUCTION_SELL_ITEM": "0x256",
|
||||||
|
"CMSG_AUCTION_REMOVE_ITEM": "0x257",
|
||||||
|
"CMSG_AUCTION_LIST_ITEMS": "0x258",
|
||||||
|
"CMSG_AUCTION_LIST_OWNER_ITEMS": "0x259",
|
||||||
|
"CMSG_AUCTION_PLACE_BID": "0x25A",
|
||||||
|
"SMSG_AUCTION_COMMAND_RESULT": "0x25B",
|
||||||
|
"SMSG_AUCTION_LIST_RESULT": "0x25C",
|
||||||
|
"SMSG_AUCTION_OWNER_LIST_RESULT": "0x25D",
|
||||||
|
"SMSG_AUCTION_OWNER_NOTIFICATION": "0x25E",
|
||||||
|
"SMSG_AUCTION_BIDDER_NOTIFICATION": "0x260",
|
||||||
|
"CMSG_AUCTION_LIST_BIDDER_ITEMS": "0x264",
|
||||||
|
"SMSG_AUCTION_BIDDER_LIST_RESULT": "0x265",
|
||||||
|
"MSG_MOVE_TIME_SKIPPED": "0x319",
|
||||||
|
"SMSG_CANCEL_AUTO_REPEAT": "0x29C",
|
||||||
|
"SMSG_WEATHER": "0x2F4",
|
||||||
|
"SMSG_QUESTUPDATE_ADD_ITEM": "0x19A",
|
||||||
|
"CMSG_GUILD_DISBAND": "0x08F",
|
||||||
|
"CMSG_GUILD_LEADER": "0x090",
|
||||||
|
"CMSG_GUILD_SET_PUBLIC_NOTE": "0x234",
|
||||||
|
"CMSG_GUILD_SET_OFFICER_NOTE": "0x235"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,47 @@
|
||||||
{
|
{
|
||||||
"CONTAINER_FIELD_NUM_SLOTS": 48,
|
|
||||||
"CONTAINER_FIELD_SLOT_1": 50,
|
|
||||||
"GAMEOBJECT_DISPLAYID": 8,
|
|
||||||
"GAMEOBJECT_BYTES_1": 14,
|
|
||||||
"ITEM_FIELD_DURABILITY": 48,
|
|
||||||
"ITEM_FIELD_MAXDURABILITY": 49,
|
|
||||||
"ITEM_FIELD_STACK_COUNT": 14,
|
|
||||||
"OBJECT_FIELD_ENTRY": 3,
|
"OBJECT_FIELD_ENTRY": 3,
|
||||||
"OBJECT_FIELD_SCALE_X": 4,
|
"OBJECT_FIELD_SCALE_X": 4,
|
||||||
"PLAYER_BYTES": 191,
|
"UNIT_FIELD_TARGET_LO": 16,
|
||||||
"PLAYER_BYTES_2": 192,
|
"UNIT_FIELD_TARGET_HI": 17,
|
||||||
"PLAYER_END": 1282,
|
|
||||||
"PLAYER_EXPLORED_ZONES_START": 1111,
|
|
||||||
"PLAYER_FIELD_BANKBAG_SLOT_1": 612,
|
|
||||||
"PLAYER_FIELD_BANK_SLOT_1": 564,
|
|
||||||
"PLAYER_FIELD_COINAGE": 1176,
|
|
||||||
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
|
|
||||||
"PLAYER_FIELD_PACK_SLOT_1": 532,
|
|
||||||
"PLAYER_FLAGS": 190,
|
|
||||||
"PLAYER_NEXT_LEVEL_XP": 717,
|
|
||||||
"PLAYER_QUEST_LOG_START": 198,
|
|
||||||
"PLAYER_REST_STATE_EXPERIENCE": 1175,
|
|
||||||
"PLAYER_SKILL_INFO_START": 718,
|
|
||||||
"PLAYER_XP": 716,
|
|
||||||
"UNIT_DYNAMIC_FLAGS": 143,
|
|
||||||
"UNIT_END": 188,
|
|
||||||
"UNIT_FIELD_AURAFLAGS": 98,
|
|
||||||
"UNIT_FIELD_AURAS": 50,
|
|
||||||
"UNIT_FIELD_BYTES_0": 36,
|
"UNIT_FIELD_BYTES_0": 36,
|
||||||
"UNIT_FIELD_BYTES_1": 133,
|
|
||||||
"UNIT_FIELD_DISPLAYID": 131,
|
|
||||||
"UNIT_FIELD_FACTIONTEMPLATE": 35,
|
|
||||||
"UNIT_FIELD_FLAGS": 46,
|
|
||||||
"UNIT_FIELD_HEALTH": 22,
|
"UNIT_FIELD_HEALTH": 22,
|
||||||
"UNIT_FIELD_LEVEL": 34,
|
"UNIT_FIELD_POWER1": 23,
|
||||||
"UNIT_FIELD_MAXHEALTH": 28,
|
"UNIT_FIELD_MAXHEALTH": 28,
|
||||||
"UNIT_FIELD_MAXPOWER1": 29,
|
"UNIT_FIELD_MAXPOWER1": 29,
|
||||||
|
"UNIT_FIELD_LEVEL": 34,
|
||||||
|
"UNIT_FIELD_FACTIONTEMPLATE": 35,
|
||||||
|
"UNIT_FIELD_FLAGS": 46,
|
||||||
|
"UNIT_FIELD_DISPLAYID": 131,
|
||||||
"UNIT_FIELD_MOUNTDISPLAYID": 133,
|
"UNIT_FIELD_MOUNTDISPLAYID": 133,
|
||||||
"UNIT_FIELD_POWER1": 23,
|
"UNIT_FIELD_AURAS": 50,
|
||||||
|
"UNIT_NPC_FLAGS": 147,
|
||||||
|
"UNIT_DYNAMIC_FLAGS": 143,
|
||||||
"UNIT_FIELD_RESISTANCES": 154,
|
"UNIT_FIELD_RESISTANCES": 154,
|
||||||
"UNIT_FIELD_STAT0": 138,
|
"UNIT_FIELD_STAT0": 138,
|
||||||
"UNIT_FIELD_STAT1": 139,
|
"UNIT_FIELD_STAT1": 139,
|
||||||
"UNIT_FIELD_STAT2": 140,
|
"UNIT_FIELD_STAT2": 140,
|
||||||
"UNIT_FIELD_STAT3": 141,
|
"UNIT_FIELD_STAT3": 141,
|
||||||
"UNIT_FIELD_STAT4": 142,
|
"UNIT_FIELD_STAT4": 142,
|
||||||
"UNIT_FIELD_TARGET_HI": 17,
|
"UNIT_END": 188,
|
||||||
"UNIT_FIELD_TARGET_LO": 16,
|
"PLAYER_FLAGS": 190,
|
||||||
"UNIT_NPC_FLAGS": 147
|
"PLAYER_BYTES": 191,
|
||||||
|
"PLAYER_BYTES_2": 192,
|
||||||
|
"PLAYER_XP": 716,
|
||||||
|
"PLAYER_NEXT_LEVEL_XP": 717,
|
||||||
|
"PLAYER_REST_STATE_EXPERIENCE": 1175,
|
||||||
|
"PLAYER_FIELD_COINAGE": 1176,
|
||||||
|
"PLAYER_QUEST_LOG_START": 198,
|
||||||
|
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
|
||||||
|
"PLAYER_FIELD_PACK_SLOT_1": 532,
|
||||||
|
"PLAYER_FIELD_BANK_SLOT_1": 564,
|
||||||
|
"PLAYER_FIELD_BANKBAG_SLOT_1": 612,
|
||||||
|
"PLAYER_SKILL_INFO_START": 718,
|
||||||
|
"PLAYER_EXPLORED_ZONES_START": 1111,
|
||||||
|
"PLAYER_END": 1282,
|
||||||
|
"GAMEOBJECT_DISPLAYID": 8,
|
||||||
|
"ITEM_FIELD_STACK_COUNT": 14,
|
||||||
|
"ITEM_FIELD_DURABILITY": 48,
|
||||||
|
"ITEM_FIELD_MAXDURABILITY": 49,
|
||||||
|
"CONTAINER_FIELD_NUM_SLOTS": 48,
|
||||||
|
"CONTAINER_FIELD_SLOT_1": 50
|
||||||
}
|
}
|
||||||
|
|
@ -1,330 +1,101 @@
|
||||||
{
|
{
|
||||||
"Achievement": {
|
"Spell": {
|
||||||
"Description": 21,
|
"ID": 0, "Attributes": 4, "IconID": 133,
|
||||||
"ID": 0,
|
"Name": 136, "Tooltip": 139, "Rank": 153, "SchoolMask": 225,
|
||||||
"Points": 39,
|
"PowerType": 14, "ManaCost": 39, "CastingTimeIndex": 47, "RangeIndex": 49
|
||||||
"Title": 4
|
|
||||||
},
|
},
|
||||||
"AchievementCriteria": {
|
"SpellRange": { "MaxRange": 4 },
|
||||||
"AchievementID": 1,
|
"ItemDisplayInfo": {
|
||||||
"Description": 9,
|
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||||
"ID": 0,
|
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||||
"Quantity": 4
|
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||||
},
|
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||||
"AreaTable": {
|
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||||
"ExploreFlag": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"MapID": 1,
|
|
||||||
"ParentAreaNum": 2
|
|
||||||
},
|
|
||||||
"CharHairGeosets": {
|
|
||||||
"GeosetID": 4,
|
|
||||||
"RaceID": 1,
|
|
||||||
"SexID": 2,
|
|
||||||
"Variation": 3
|
|
||||||
},
|
},
|
||||||
"CharSections": {
|
"CharSections": {
|
||||||
"BaseSection": 3,
|
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||||
"ColorIndex": 5,
|
"VariationIndex": 4, "ColorIndex": 5,
|
||||||
"Flags": 9,
|
"Texture1": 6, "Texture2": 7, "Texture3": 8,
|
||||||
"RaceID": 1,
|
"Flags": 9
|
||||||
"SexID": 2,
|
|
||||||
"Texture1": 6,
|
|
||||||
"Texture2": 7,
|
|
||||||
"Texture3": 8,
|
|
||||||
"VariationIndex": 4
|
|
||||||
},
|
},
|
||||||
"CharTitles": {
|
"SpellIcon": { "ID": 0, "Path": 1 },
|
||||||
"ID": 0,
|
"FactionTemplate": {
|
||||||
"Title": 2,
|
"ID": 0, "Faction": 1, "FactionGroup": 3,
|
||||||
"TitleBit": 36
|
"FriendGroup": 4, "EnemyGroup": 5,
|
||||||
},
|
"Enemy0": 6, "Enemy1": 7, "Enemy2": 8, "Enemy3": 9
|
||||||
"CharacterFacialHairStyles": {
|
|
||||||
"Geoset100": 3,
|
|
||||||
"Geoset200": 5,
|
|
||||||
"Geoset300": 4,
|
|
||||||
"RaceID": 0,
|
|
||||||
"SexID": 1,
|
|
||||||
"Variation": 2
|
|
||||||
},
|
|
||||||
"CreatureDisplayInfo": {
|
|
||||||
"ExtraDisplayId": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"ModelID": 1,
|
|
||||||
"Skin1": 6,
|
|
||||||
"Skin2": 7,
|
|
||||||
"Skin3": 8
|
|
||||||
},
|
|
||||||
"CreatureDisplayInfoExtra": {
|
|
||||||
"BakeName": 20,
|
|
||||||
"EquipDisplay0": 8,
|
|
||||||
"EquipDisplay1": 9,
|
|
||||||
"EquipDisplay10": 18,
|
|
||||||
"EquipDisplay2": 10,
|
|
||||||
"EquipDisplay3": 11,
|
|
||||||
"EquipDisplay4": 12,
|
|
||||||
"EquipDisplay5": 13,
|
|
||||||
"EquipDisplay6": 14,
|
|
||||||
"EquipDisplay7": 15,
|
|
||||||
"EquipDisplay8": 16,
|
|
||||||
"EquipDisplay9": 17,
|
|
||||||
"FaceID": 4,
|
|
||||||
"FacialHairID": 7,
|
|
||||||
"HairColorID": 6,
|
|
||||||
"HairStyleID": 5,
|
|
||||||
"ID": 0,
|
|
||||||
"RaceID": 1,
|
|
||||||
"SexID": 2,
|
|
||||||
"SkinID": 3
|
|
||||||
},
|
|
||||||
"CreatureModelData": {
|
|
||||||
"ID": 0,
|
|
||||||
"ModelPath": 2
|
|
||||||
},
|
|
||||||
"Emotes": {
|
|
||||||
"AnimID": 2,
|
|
||||||
"ID": 0
|
|
||||||
},
|
|
||||||
"EmotesText": {
|
|
||||||
"Command": 1,
|
|
||||||
"EmoteRef": 2,
|
|
||||||
"ID": 0,
|
|
||||||
"OthersNoTargetTextID": 7,
|
|
||||||
"OthersTargetTextID": 3,
|
|
||||||
"SenderNoTargetTextID": 9,
|
|
||||||
"SenderTargetTextID": 5
|
|
||||||
},
|
|
||||||
"EmotesTextData": {
|
|
||||||
"ID": 0,
|
|
||||||
"Text": 1
|
|
||||||
},
|
},
|
||||||
"Faction": {
|
"Faction": {
|
||||||
"ID": 0,
|
"ID": 0, "ReputationRaceMask0": 2, "ReputationRaceMask1": 3,
|
||||||
"ReputationBase0": 10,
|
"ReputationRaceMask2": 4, "ReputationRaceMask3": 5,
|
||||||
"ReputationBase1": 11,
|
"ReputationBase0": 10, "ReputationBase1": 11,
|
||||||
"ReputationBase2": 12,
|
"ReputationBase2": 12, "ReputationBase3": 13
|
||||||
"ReputationBase3": 13,
|
|
||||||
"ReputationRaceMask0": 2,
|
|
||||||
"ReputationRaceMask1": 3,
|
|
||||||
"ReputationRaceMask2": 4,
|
|
||||||
"ReputationRaceMask3": 5
|
|
||||||
},
|
},
|
||||||
"FactionTemplate": {
|
"Achievement": { "ID": 0, "Title": 4, "Description": 21 },
|
||||||
"Enemy0": 6,
|
"AreaTable": { "ID": 0, "MapID": 1, "ParentAreaNum": 2, "ExploreFlag": 3 },
|
||||||
"Enemy1": 7,
|
"CreatureDisplayInfoExtra": {
|
||||||
"Enemy2": 8,
|
"ID": 0, "RaceID": 1, "SexID": 2, "SkinID": 3, "FaceID": 4,
|
||||||
"Enemy3": 9,
|
"HairStyleID": 5, "HairColorID": 6, "FacialHairID": 7,
|
||||||
"EnemyGroup": 5,
|
"EquipDisplay0": 8, "EquipDisplay1": 9, "EquipDisplay2": 10,
|
||||||
"Faction": 1,
|
"EquipDisplay3": 11, "EquipDisplay4": 12, "EquipDisplay5": 13,
|
||||||
"FactionGroup": 3,
|
"EquipDisplay6": 14, "EquipDisplay7": 15, "EquipDisplay8": 16,
|
||||||
"FriendGroup": 4,
|
"EquipDisplay9": 17, "EquipDisplay10": 18, "BakeName": 20
|
||||||
"ID": 0
|
|
||||||
},
|
},
|
||||||
"GameObjectDisplayInfo": {
|
"CreatureDisplayInfo": {
|
||||||
"ID": 0,
|
"ID": 0, "ModelID": 1, "ExtraDisplayId": 3,
|
||||||
"ModelName": 1
|
"Skin1": 6, "Skin2": 7, "Skin3": 8
|
||||||
},
|
|
||||||
"ItemDisplayInfo": {
|
|
||||||
"GeosetGroup1": 7,
|
|
||||||
"GeosetGroup3": 9,
|
|
||||||
"ID": 0,
|
|
||||||
"InventoryIcon": 5,
|
|
||||||
"LeftModel": 1,
|
|
||||||
"LeftModelTexture": 3,
|
|
||||||
"TextureArmLower": 15,
|
|
||||||
"TextureArmUpper": 14,
|
|
||||||
"TextureFoot": 21,
|
|
||||||
"TextureHand": 16,
|
|
||||||
"TextureLegLower": 20,
|
|
||||||
"TextureLegUpper": 19,
|
|
||||||
"TextureTorsoLower": 18,
|
|
||||||
"TextureTorsoUpper": 17
|
|
||||||
},
|
|
||||||
"ItemSet": {
|
|
||||||
"ID": 0,
|
|
||||||
"Item0": 18,
|
|
||||||
"Item1": 19,
|
|
||||||
"Item2": 20,
|
|
||||||
"Item3": 21,
|
|
||||||
"Item4": 22,
|
|
||||||
"Item5": 23,
|
|
||||||
"Item6": 24,
|
|
||||||
"Item7": 25,
|
|
||||||
"Item8": 26,
|
|
||||||
"Item9": 27,
|
|
||||||
"Name": 1,
|
|
||||||
"Spell0": 28,
|
|
||||||
"Spell1": 29,
|
|
||||||
"Spell2": 30,
|
|
||||||
"Spell3": 31,
|
|
||||||
"Spell4": 32,
|
|
||||||
"Spell5": 33,
|
|
||||||
"Spell6": 34,
|
|
||||||
"Spell7": 35,
|
|
||||||
"Spell8": 36,
|
|
||||||
"Spell9": 37,
|
|
||||||
"Threshold0": 38,
|
|
||||||
"Threshold1": 39,
|
|
||||||
"Threshold2": 40,
|
|
||||||
"Threshold3": 41,
|
|
||||||
"Threshold4": 42,
|
|
||||||
"Threshold5": 43,
|
|
||||||
"Threshold6": 44,
|
|
||||||
"Threshold7": 45,
|
|
||||||
"Threshold8": 46,
|
|
||||||
"Threshold9": 47
|
|
||||||
},
|
|
||||||
"LFGDungeons": {
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 1
|
|
||||||
},
|
|
||||||
"Light": {
|
|
||||||
"ID": 0,
|
|
||||||
"InnerRadius": 5,
|
|
||||||
"LightParamsID": 7,
|
|
||||||
"LightParamsIDRain": 8,
|
|
||||||
"LightParamsIDUnderwater": 9,
|
|
||||||
"MapID": 1,
|
|
||||||
"OuterRadius": 6,
|
|
||||||
"X": 2,
|
|
||||||
"Y": 4,
|
|
||||||
"Z": 3
|
|
||||||
},
|
|
||||||
"LightFloatBand": {
|
|
||||||
"BlockIndex": 1,
|
|
||||||
"NumKeyframes": 2,
|
|
||||||
"TimeKey0": 3,
|
|
||||||
"Value0": 19
|
|
||||||
},
|
|
||||||
"LightIntBand": {
|
|
||||||
"BlockIndex": 1,
|
|
||||||
"NumKeyframes": 2,
|
|
||||||
"TimeKey0": 3,
|
|
||||||
"Value0": 19
|
|
||||||
},
|
|
||||||
"LightParams": {
|
|
||||||
"LightParamsID": 0
|
|
||||||
},
|
|
||||||
"Map": {
|
|
||||||
"ID": 0,
|
|
||||||
"InternalName": 1
|
|
||||||
},
|
|
||||||
"SkillLine": {
|
|
||||||
"Category": 1,
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 3
|
|
||||||
},
|
|
||||||
"SkillLineAbility": {
|
|
||||||
"SkillLineID": 1,
|
|
||||||
"SpellID": 2
|
|
||||||
},
|
|
||||||
"Spell": {
|
|
||||||
"Attributes": 4,
|
|
||||||
"AttributesEx": 5,
|
|
||||||
"CastingTimeIndex": 47,
|
|
||||||
"DispelType": 2,
|
|
||||||
"DurationIndex": 40,
|
|
||||||
"EffectBasePoints0": 80,
|
|
||||||
"EffectBasePoints1": 81,
|
|
||||||
"EffectBasePoints2": 82,
|
|
||||||
"ID": 0,
|
|
||||||
"IconID": 133,
|
|
||||||
"ManaCost": 39,
|
|
||||||
"Name": 136,
|
|
||||||
"PowerType": 14,
|
|
||||||
"RangeIndex": 49,
|
|
||||||
"Rank": 153,
|
|
||||||
"SchoolMask": 225,
|
|
||||||
"SpellVisualID": 131,
|
|
||||||
"Tooltip": 139
|
|
||||||
},
|
|
||||||
"SpellIcon": {
|
|
||||||
"ID": 0,
|
|
||||||
"Path": 1
|
|
||||||
},
|
|
||||||
"SpellItemEnchantment": {
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 8
|
|
||||||
},
|
|
||||||
"SpellRange": {
|
|
||||||
"MaxRange": 4
|
|
||||||
},
|
|
||||||
"SpellVisual": {
|
|
||||||
"CastKit": 2,
|
|
||||||
"ID": 0,
|
|
||||||
"ImpactKit": 3,
|
|
||||||
"MissileModel": 8,
|
|
||||||
"PrecastKit": 1
|
|
||||||
},
|
|
||||||
"SpellVisualEffectName": {
|
|
||||||
"FilePath": 2,
|
|
||||||
"ID": 0
|
|
||||||
},
|
|
||||||
"SpellVisualKit": {
|
|
||||||
"BaseEffect": 5,
|
|
||||||
"BreathEffect": 8,
|
|
||||||
"ChestEffect": 4,
|
|
||||||
"HeadEffect": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"LeftHandEffect": 6,
|
|
||||||
"RightHandEffect": 7,
|
|
||||||
"SpecialEffect0": 11,
|
|
||||||
"SpecialEffect1": 12,
|
|
||||||
"SpecialEffect2": 13
|
|
||||||
},
|
|
||||||
"Talent": {
|
|
||||||
"Column": 3,
|
|
||||||
"ID": 0,
|
|
||||||
"PrereqRank0": 12,
|
|
||||||
"PrereqTalent0": 9,
|
|
||||||
"RankSpell0": 4,
|
|
||||||
"Row": 2,
|
|
||||||
"TabID": 1
|
|
||||||
},
|
|
||||||
"TalentTab": {
|
|
||||||
"BackgroundFile": 23,
|
|
||||||
"ClassMask": 20,
|
|
||||||
"ID": 0,
|
|
||||||
"Name": 1,
|
|
||||||
"OrderIndex": 22
|
|
||||||
},
|
},
|
||||||
"TaxiNodes": {
|
"TaxiNodes": {
|
||||||
"ID": 0,
|
"ID": 0, "MapID": 1, "X": 2, "Y": 3, "Z": 4, "Name": 5,
|
||||||
"MapID": 1,
|
"MountDisplayIdAllianceFallback": 20, "MountDisplayIdHordeFallback": 21,
|
||||||
"MountDisplayIdAlliance": 22,
|
"MountDisplayIdAlliance": 22, "MountDisplayIdHorde": 23
|
||||||
"MountDisplayIdAllianceFallback": 20,
|
|
||||||
"MountDisplayIdHorde": 23,
|
|
||||||
"MountDisplayIdHordeFallback": 21,
|
|
||||||
"Name": 5,
|
|
||||||
"X": 2,
|
|
||||||
"Y": 3,
|
|
||||||
"Z": 4
|
|
||||||
},
|
|
||||||
"TaxiPath": {
|
|
||||||
"Cost": 3,
|
|
||||||
"FromNode": 1,
|
|
||||||
"ID": 0,
|
|
||||||
"ToNode": 2
|
|
||||||
},
|
},
|
||||||
|
"TaxiPath": { "ID": 0, "FromNode": 1, "ToNode": 2, "Cost": 3 },
|
||||||
"TaxiPathNode": {
|
"TaxiPathNode": {
|
||||||
"ID": 0,
|
"ID": 0, "PathID": 1, "NodeIndex": 2, "MapID": 3,
|
||||||
"MapID": 3,
|
"X": 4, "Y": 5, "Z": 6
|
||||||
"NodeIndex": 2,
|
},
|
||||||
"PathID": 1,
|
"TalentTab": {
|
||||||
"X": 4,
|
"ID": 0, "Name": 1, "ClassMask": 20,
|
||||||
"Y": 5,
|
"OrderIndex": 22, "BackgroundFile": 23
|
||||||
"Z": 6
|
},
|
||||||
|
"Talent": {
|
||||||
|
"ID": 0, "TabID": 1, "Row": 2, "Column": 3,
|
||||||
|
"RankSpell0": 4, "PrereqTalent0": 9, "PrereqRank0": 12
|
||||||
|
},
|
||||||
|
"SkillLineAbility": { "SkillLineID": 1, "SpellID": 2 },
|
||||||
|
"SkillLine": { "ID": 0, "Category": 1, "Name": 3 },
|
||||||
|
"Map": { "ID": 0, "InternalName": 1 },
|
||||||
|
"CreatureModelData": { "ID": 0, "ModelPath": 2 },
|
||||||
|
"CharHairGeosets": {
|
||||||
|
"RaceID": 1, "SexID": 2, "Variation": 3, "GeosetID": 4
|
||||||
|
},
|
||||||
|
"CharacterFacialHairStyles": {
|
||||||
|
"RaceID": 0, "SexID": 1, "Variation": 2,
|
||||||
|
"Geoset100": 3, "Geoset300": 4, "Geoset200": 5
|
||||||
|
},
|
||||||
|
"GameObjectDisplayInfo": { "ID": 0, "ModelName": 1 },
|
||||||
|
"Emotes": { "ID": 0, "AnimID": 2 },
|
||||||
|
"EmotesText": {
|
||||||
|
"ID": 0, "Command": 1, "EmoteRef": 2,
|
||||||
|
"OthersTargetTextID": 3, "SenderTargetTextID": 5,
|
||||||
|
"OthersNoTargetTextID": 7, "SenderNoTargetTextID": 9
|
||||||
|
},
|
||||||
|
"EmotesTextData": { "ID": 0, "Text": 1 },
|
||||||
|
"Light": {
|
||||||
|
"ID": 0, "MapID": 1, "X": 2, "Z": 3, "Y": 4,
|
||||||
|
"InnerRadius": 5, "OuterRadius": 6, "LightParamsID": 7,
|
||||||
|
"LightParamsIDRain": 8, "LightParamsIDUnderwater": 9
|
||||||
|
},
|
||||||
|
"LightParams": { "LightParamsID": 0 },
|
||||||
|
"LightIntBand": {
|
||||||
|
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||||
|
},
|
||||||
|
"LightFloatBand": {
|
||||||
|
"BlockIndex": 1, "NumKeyframes": 2, "TimeKey0": 3, "Value0": 19
|
||||||
},
|
},
|
||||||
"WorldMapArea": {
|
"WorldMapArea": {
|
||||||
"AreaID": 2,
|
"ID": 0, "MapID": 1, "AreaID": 2, "AreaName": 3,
|
||||||
"AreaName": 3,
|
"LocLeft": 4, "LocRight": 5, "LocTop": 6, "LocBottom": 7,
|
||||||
"DisplayMapID": 8,
|
"DisplayMapID": 8, "ParentWorldMapID": 10
|
||||||
"ID": 0,
|
|
||||||
"LocBottom": 7,
|
|
||||||
"LocLeft": 4,
|
|
||||||
"LocRight": 5,
|
|
||||||
"LocTop": 6,
|
|
||||||
"MapID": 1,
|
|
||||||
"ParentWorldMapID": 10
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,46 @@
|
||||||
{
|
{
|
||||||
"CONTAINER_FIELD_NUM_SLOTS": 64,
|
|
||||||
"CONTAINER_FIELD_SLOT_1": 66,
|
|
||||||
"GAMEOBJECT_DISPLAYID": 8,
|
|
||||||
"GAMEOBJECT_BYTES_1": 17,
|
|
||||||
"ITEM_FIELD_DURABILITY": 60,
|
|
||||||
"ITEM_FIELD_MAXDURABILITY": 61,
|
|
||||||
"ITEM_FIELD_STACK_COUNT": 14,
|
|
||||||
"OBJECT_FIELD_ENTRY": 3,
|
"OBJECT_FIELD_ENTRY": 3,
|
||||||
"OBJECT_FIELD_SCALE_X": 4,
|
"OBJECT_FIELD_SCALE_X": 4,
|
||||||
"PLAYER_BLOCK_PERCENTAGE": 1024,
|
"UNIT_FIELD_TARGET_LO": 6,
|
||||||
"PLAYER_BYTES": 153,
|
"UNIT_FIELD_TARGET_HI": 7,
|
||||||
"PLAYER_BYTES_2": 154,
|
|
||||||
"PLAYER_CHOSEN_TITLE": 1349,
|
|
||||||
"PLAYER_CRIT_PERCENTAGE": 1029,
|
|
||||||
"PLAYER_DODGE_PERCENTAGE": 1025,
|
|
||||||
"PLAYER_EXPLORED_ZONES_START": 1041,
|
|
||||||
"PLAYER_FIELD_ARENA_CURRENCY": 1423,
|
|
||||||
"PLAYER_FIELD_BANKBAG_SLOT_1": 458,
|
|
||||||
"PLAYER_FIELD_BANK_SLOT_1": 402,
|
|
||||||
"PLAYER_FIELD_COINAGE": 1170,
|
|
||||||
"PLAYER_FIELD_COMBAT_RATING_1": 1231,
|
|
||||||
"PLAYER_FIELD_HONOR_CURRENCY": 1422,
|
|
||||||
"PLAYER_FIELD_INV_SLOT_HEAD": 324,
|
|
||||||
"PLAYER_FIELD_MOD_DAMAGE_DONE_POS": 1171,
|
|
||||||
"PLAYER_FIELD_MOD_HEALING_DONE_POS": 1192,
|
|
||||||
"PLAYER_FIELD_PACK_SLOT_1": 370,
|
|
||||||
"PLAYER_FLAGS": 150,
|
|
||||||
"PLAYER_NEXT_LEVEL_XP": 635,
|
|
||||||
"PLAYER_PARRY_PERCENTAGE": 1026,
|
|
||||||
"PLAYER_QUEST_LOG_START": 158,
|
|
||||||
"PLAYER_RANGED_CRIT_PERCENTAGE": 1030,
|
|
||||||
"PLAYER_REST_STATE_EXPERIENCE": 1169,
|
|
||||||
"PLAYER_SKILL_INFO_START": 636,
|
|
||||||
"PLAYER_SPELL_CRIT_PERCENTAGE1": 1032,
|
|
||||||
"PLAYER_XP": 634,
|
|
||||||
"UNIT_DYNAMIC_FLAGS": 147,
|
|
||||||
"UNIT_END": 148,
|
|
||||||
"UNIT_FIELD_ATTACK_POWER": 123,
|
|
||||||
"UNIT_FIELD_BYTES_0": 23,
|
"UNIT_FIELD_BYTES_0": 23,
|
||||||
"UNIT_FIELD_BYTES_1": 137,
|
"UNIT_FIELD_HEALTH": 24,
|
||||||
"UNIT_FIELD_DISPLAYID": 67,
|
"UNIT_FIELD_POWER1": 25,
|
||||||
|
"UNIT_FIELD_MAXHEALTH": 32,
|
||||||
|
"UNIT_FIELD_MAXPOWER1": 33,
|
||||||
|
"UNIT_FIELD_LEVEL": 54,
|
||||||
"UNIT_FIELD_FACTIONTEMPLATE": 55,
|
"UNIT_FIELD_FACTIONTEMPLATE": 55,
|
||||||
"UNIT_FIELD_FLAGS": 59,
|
"UNIT_FIELD_FLAGS": 59,
|
||||||
"UNIT_FIELD_FLAGS_2": 60,
|
"UNIT_FIELD_FLAGS_2": 60,
|
||||||
"UNIT_FIELD_HEALTH": 24,
|
"UNIT_FIELD_DISPLAYID": 67,
|
||||||
"UNIT_FIELD_LEVEL": 54,
|
|
||||||
"UNIT_FIELD_MAXHEALTH": 32,
|
|
||||||
"UNIT_FIELD_MAXPOWER1": 33,
|
|
||||||
"UNIT_FIELD_MOUNTDISPLAYID": 69,
|
"UNIT_FIELD_MOUNTDISPLAYID": 69,
|
||||||
"UNIT_FIELD_POWER1": 25,
|
"UNIT_NPC_FLAGS": 82,
|
||||||
"UNIT_FIELD_RANGED_ATTACK_POWER": 126,
|
"UNIT_DYNAMIC_FLAGS": 147,
|
||||||
"UNIT_FIELD_RESISTANCES": 99,
|
"UNIT_FIELD_RESISTANCES": 99,
|
||||||
"UNIT_FIELD_STAT0": 84,
|
"UNIT_FIELD_STAT0": 84,
|
||||||
"UNIT_FIELD_STAT1": 85,
|
"UNIT_FIELD_STAT1": 85,
|
||||||
"UNIT_FIELD_STAT2": 86,
|
"UNIT_FIELD_STAT2": 86,
|
||||||
"UNIT_FIELD_STAT3": 87,
|
"UNIT_FIELD_STAT3": 87,
|
||||||
"UNIT_FIELD_STAT4": 88,
|
"UNIT_FIELD_STAT4": 88,
|
||||||
"UNIT_FIELD_TARGET_HI": 7,
|
"UNIT_END": 148,
|
||||||
"UNIT_FIELD_TARGET_LO": 6,
|
"PLAYER_FLAGS": 150,
|
||||||
"UNIT_NPC_FLAGS": 82,
|
"PLAYER_BYTES": 153,
|
||||||
"UNIT_NPC_EMOTESTATE": 164
|
"PLAYER_BYTES_2": 154,
|
||||||
|
"PLAYER_XP": 634,
|
||||||
|
"PLAYER_NEXT_LEVEL_XP": 635,
|
||||||
|
"PLAYER_REST_STATE_EXPERIENCE": 1169,
|
||||||
|
"PLAYER_FIELD_COINAGE": 1170,
|
||||||
|
"PLAYER_QUEST_LOG_START": 158,
|
||||||
|
"PLAYER_FIELD_INV_SLOT_HEAD": 324,
|
||||||
|
"PLAYER_FIELD_PACK_SLOT_1": 370,
|
||||||
|
"PLAYER_FIELD_BANK_SLOT_1": 402,
|
||||||
|
"PLAYER_FIELD_BANKBAG_SLOT_1": 458,
|
||||||
|
"PLAYER_SKILL_INFO_START": 636,
|
||||||
|
"PLAYER_EXPLORED_ZONES_START": 1041,
|
||||||
|
"GAMEOBJECT_DISPLAYID": 8,
|
||||||
|
"ITEM_FIELD_STACK_COUNT": 14,
|
||||||
|
"ITEM_FIELD_DURABILITY": 60,
|
||||||
|
"ITEM_FIELD_MAXDURABILITY": 61,
|
||||||
|
"CONTAINER_FIELD_NUM_SLOTS": 64,
|
||||||
|
"CONTAINER_FIELD_SLOT_1": 66
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@
|
||||||
"SMSG_SPLINE_MOVE_SET_RUN_BACK_SPEED": "SMSG_SPLINE_SET_RUN_BACK_SPEED",
|
"SMSG_SPLINE_MOVE_SET_RUN_BACK_SPEED": "SMSG_SPLINE_SET_RUN_BACK_SPEED",
|
||||||
"SMSG_SPLINE_MOVE_SET_RUN_SPEED": "SMSG_SPLINE_SET_RUN_SPEED",
|
"SMSG_SPLINE_MOVE_SET_RUN_SPEED": "SMSG_SPLINE_SET_RUN_SPEED",
|
||||||
"SMSG_SPLINE_MOVE_SET_SWIM_SPEED": "SMSG_SPLINE_SET_SWIM_SPEED",
|
"SMSG_SPLINE_MOVE_SET_SWIM_SPEED": "SMSG_SPLINE_SET_SWIM_SPEED",
|
||||||
|
"SMSG_UPDATE_AURA_DURATION": "SMSG_EQUIPMENT_SET_SAVED",
|
||||||
"SMSG_VICTIMSTATEUPDATE_OBSOLETE": "SMSG_BATTLEFIELD_PORT_DENIED"
|
"SMSG_VICTIMSTATEUPDATE_OBSOLETE": "SMSG_BATTLEFIELD_PORT_DENIED"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ WoWee supports three World of Warcraft expansions in a unified codebase using an
|
||||||
- **Vanilla (Classic) 1.12** - Original World of Warcraft
|
- **Vanilla (Classic) 1.12** - Original World of Warcraft
|
||||||
- **The Burning Crusade (TBC) 2.4.3** - First expansion
|
- **The Burning Crusade (TBC) 2.4.3** - First expansion
|
||||||
- **Wrath of the Lich King (WotLK) 3.3.5a** - Second expansion
|
- **Wrath of the Lich King (WotLK) 3.3.5a** - Second expansion
|
||||||
- **Turtle WoW 1.17** - Custom Vanilla-based server with extended content
|
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
|
|
@ -18,9 +17,9 @@ The multi-expansion support is built on the **Expansion Profile** system:
|
||||||
- Specifies which packet parsers to use
|
- Specifies which packet parsers to use
|
||||||
|
|
||||||
2. **Packet Parsers** - Expansion-specific message handling
|
2. **Packet Parsers** - Expansion-specific message handling
|
||||||
- `packet_parsers_classic.cpp` - Vanilla 1.12 / Turtle WoW message parsing
|
- `packet_parsers_classic.cpp` - Vanilla 1.12 message parsing
|
||||||
- `packet_parsers_tbc.cpp` - TBC 2.4.3 message parsing
|
- `packet_parsers_tbc.cpp` - TBC 2.4.3 message parsing
|
||||||
- Default (WotLK 3.3.5a) parsers in `game_handler.cpp` and domain handlers
|
- `packet_parsers_wotlk.cpp` (default) - WotLK 3.3.5a message parsing
|
||||||
|
|
||||||
3. **Update Fields** - Expansion-specific entity data layout
|
3. **Update Fields** - Expansion-specific entity data layout
|
||||||
- Loaded from `update_fields.json` in expansion data directory
|
- Loaded from `update_fields.json` in expansion data directory
|
||||||
|
|
@ -79,27 +78,25 @@ WOWEE_EXPANSION=classic ./wowee # Force Classic
|
||||||
### Checking Current Expansion
|
### Checking Current Expansion
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
#include "game/game_utils.hpp"
|
#include "game/expansion_profile.hpp"
|
||||||
|
|
||||||
// Shared helpers (defined in game_utils.hpp)
|
// Global helper
|
||||||
if (isActiveExpansion("tbc")) {
|
bool isClassicLikeExpansion() {
|
||||||
|
auto profile = ExpansionProfile::getActive();
|
||||||
|
return profile && (profile->name == "Classic" || profile->name == "Vanilla");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specific check
|
||||||
|
if (GameHandler::getInstance().isActiveExpansion("tbc")) {
|
||||||
// TBC-specific code
|
// TBC-specific code
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isClassicLikeExpansion()) {
|
|
||||||
// Classic or Turtle WoW
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPreWotlk()) {
|
|
||||||
// Classic, Turtle, or TBC (not WotLK)
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Expansion-Specific Packet Parsing
|
### Expansion-Specific Packet Parsing
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
// In packet_parsers_*.cpp, implement expansion-specific logic
|
// In packet_parsers_*.cpp, implement expansion-specific logic
|
||||||
bool TbcPacketParsers::parseXxx(network::Packet& packet, XxxData& data) {
|
bool parseXxxPacket(BitStream& data, ...) {
|
||||||
// Custom logic for this expansion's packet format
|
// Custom logic for this expansion's packet format
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -124,7 +121,6 @@ bool TbcPacketParsers::parseXxx(network::Packet& packet, XxxData& data) {
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- `include/game/expansion_profile.hpp` - Expansion metadata
|
- `include/game/expansion_profile.hpp` - Expansion metadata
|
||||||
- `include/game/game_utils.hpp` - `isActiveExpansion()`, `isClassicLikeExpansion()`, `isPreWotlk()`
|
- `docs/status.md` - Current feature support by expansion
|
||||||
- `src/game/packet_parsers_classic.cpp` / `packet_parsers_tbc.cpp` - Expansion-specific parsing
|
- `src/game/packet_parsers_*.cpp` - Format-specific parsing logic
|
||||||
- `docs/status.md` - Current feature support
|
|
||||||
- `docs/` directory - Additional protocol documentation
|
- `docs/` directory - Additional protocol documentation
|
||||||
|
|
|
||||||
|
|
@ -39,24 +39,20 @@ WoWee needs game assets from your WoW installation:
|
||||||
|
|
||||||
**Using provided script (Windows)**:
|
**Using provided script (Windows)**:
|
||||||
```powershell
|
```powershell
|
||||||
.\extract_assets.ps1 "C:\Games\WoW-3.3.5a\Data"
|
.\extract_assets.ps1 -WowDirectory "C:\Program Files\World of Warcraft"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Manual extraction**:
|
**Manual extraction**:
|
||||||
1. Install [StormLib](https://github.com/ladislav-zezula/StormLib)
|
1. Install [StormLib](https://github.com/ladislav-zezula/StormLib)
|
||||||
2. Use `asset_extract` or extract manually to `./Data/`:
|
2. Extract to `./Data/`:
|
||||||
```
|
```
|
||||||
Data/
|
Data/
|
||||||
├── manifest.json # File index (generated by asset_extract)
|
├── dbc/ # DBC files
|
||||||
├── expansions/<id>/ # Per-expansion config and DB
|
├── map/ # World map data
|
||||||
├── character/ # Character textures
|
├── adt/ # Terrain chunks
|
||||||
├── creature/ # Creature models/textures
|
├── wmo/ # Building models
|
||||||
├── interface/ # UI textures and icons
|
├── m2/ # Character/creature models
|
||||||
├── item/ # Item model textures
|
└── blp/ # Textures
|
||||||
├── spell/ # Spell effect models
|
|
||||||
├── terrain/ # ADT terrain, WMO, M2 doodads
|
|
||||||
├── world/ # World map images
|
|
||||||
└── sound/ # Audio files
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3: Connect to a Server
|
### Step 3: Connect to a Server
|
||||||
|
|
@ -88,19 +84,15 @@ WoWee needs game assets from your WoW installation:
|
||||||
| Strafe Right | D |
|
| Strafe Right | D |
|
||||||
| Jump | Space |
|
| Jump | Space |
|
||||||
| Toggle Chat | Enter |
|
| Toggle Chat | Enter |
|
||||||
| Open Character Screen | C |
|
| Interact (talk to NPC, loot) | F |
|
||||||
| Open Inventory | I |
|
| Open Inventory | B |
|
||||||
| Open All Bags | B |
|
|
||||||
| Open Spellbook | P |
|
| Open Spellbook | P |
|
||||||
| Open Talents | N |
|
| Open Talent Tree | T |
|
||||||
| Open Quest Log | L |
|
| Open Quest Log | Q |
|
||||||
| Open World Map | M |
|
| Open World Map | W (when not typing) |
|
||||||
|
| Toggle Minimap | M |
|
||||||
| Toggle Nameplates | V |
|
| Toggle Nameplates | V |
|
||||||
| Toggle Raid Frames | F |
|
| Toggle Party Frames | F |
|
||||||
| Open Guild Roster | O |
|
|
||||||
| Open Dungeon Finder | J |
|
|
||||||
| Open Achievements | Y |
|
|
||||||
| Open Skills | K |
|
|
||||||
| Toggle Settings | Escape |
|
| Toggle Settings | Escape |
|
||||||
| Target Next Enemy | Tab |
|
| Target Next Enemy | Tab |
|
||||||
| Target Previous Enemy | Shift+Tab |
|
| Target Previous Enemy | Shift+Tab |
|
||||||
|
|
@ -179,7 +171,7 @@ WOWEE_EXPANSION=tbc ./wowee # Force TBC
|
||||||
|
|
||||||
### General Issues
|
### General Issues
|
||||||
- Comprehensive troubleshooting: See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
- Comprehensive troubleshooting: See [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||||
- Check `logs/wowee.log` in the working directory for errors
|
- Check logs in `~/.wowee/logs/` for errors
|
||||||
- Verify expansion matches server requirements
|
- Verify expansion matches server requirements
|
||||||
|
|
||||||
## Server Configuration
|
## Server Configuration
|
||||||
|
|
|
||||||
33
PKGBUILD
33
PKGBUILD
|
|
@ -9,27 +9,28 @@ arch=('x86_64')
|
||||||
url="https://github.com/Kelsidavis/WoWee"
|
url="https://github.com/Kelsidavis/WoWee"
|
||||||
license=('MIT')
|
license=('MIT')
|
||||||
depends=(
|
depends=(
|
||||||
'sdl2' # Windowing and event loop
|
'sdl2'
|
||||||
'vulkan-icd-loader' # Vulkan runtime (GPU driver communication)
|
'vulkan-icd-loader'
|
||||||
'openssl' # SRP6a auth protocol (key exchange + RC4 encryption)
|
'openssl'
|
||||||
'zlib' # Network packet decompression and Warden module inflate
|
'zlib'
|
||||||
'ffmpeg' # Video playback (login cinematics)
|
'ffmpeg'
|
||||||
'unicorn' # Warden anti-cheat x86 emulation (cross-platform, no Wine)
|
'unicorn'
|
||||||
'libx11' # X11 windowing support
|
'glew'
|
||||||
'stormlib' # AUR — MPQ extraction (wowee-extract-assets uses libstorm.so)
|
'libx11'
|
||||||
|
'stormlib' # AUR — required at runtime by wowee-extract-assets (libstorm.so)
|
||||||
)
|
)
|
||||||
makedepends=(
|
makedepends=(
|
||||||
'git' # Clone submodules (imgui, vk-bootstrap)
|
'git'
|
||||||
'cmake' # Build system
|
'cmake'
|
||||||
'pkgconf' # Dependency detection
|
'pkgconf'
|
||||||
'glm' # Header-only math library (vectors, matrices, quaternions)
|
'glm'
|
||||||
'vulkan-headers' # Vulkan API definitions (build-time only)
|
'vulkan-headers'
|
||||||
'shaderc' # GLSL → SPIR-V shader compilation
|
'shaderc'
|
||||||
'python' # Opcode registry generation and DBC validation scripts
|
'python'
|
||||||
)
|
)
|
||||||
provides=('wowee')
|
provides=('wowee')
|
||||||
conflicts=('wowee')
|
conflicts=('wowee')
|
||||||
source=("${pkgname}::git+https://github.com/Kelsidavis/WoWee.git#branch=master"
|
source=("${pkgname}::git+https://github.com/Kelsidavis/WoWee.git#branch=main"
|
||||||
"git+https://github.com/ocornut/imgui.git"
|
"git+https://github.com/ocornut/imgui.git"
|
||||||
"git+https://github.com/charles-lunarg/vk-bootstrap.git")
|
"git+https://github.com/charles-lunarg/vk-bootstrap.git")
|
||||||
sha256sums=('SKIP' 'SKIP' 'SKIP')
|
sha256sums=('SKIP' 'SKIP' 'SKIP')
|
||||||
|
|
|
||||||
27
README.md
27
README.md
|
|
@ -19,15 +19,13 @@ Protocol Compatible with **Vanilla (Classic) 1.12 + TBC 2.4.3 + WotLK 3.3.5a**.
|
||||||
|
|
||||||
> **Legal Disclaimer**: This is an educational/research project. It does not include any Blizzard Entertainment assets, data files, or proprietary code. World of Warcraft and all related assets are the property of Blizzard Entertainment, Inc. This project is not affiliated with or endorsed by Blizzard Entertainment. Users are responsible for supplying their own legally obtained game data files and for ensuring compliance with all applicable laws in their jurisdiction.
|
> **Legal Disclaimer**: This is an educational/research project. It does not include any Blizzard Entertainment assets, data files, or proprietary code. World of Warcraft and all related assets are the property of Blizzard Entertainment, Inc. This project is not affiliated with or endorsed by Blizzard Entertainment. Users are responsible for supplying their own legally obtained game data files and for ensuring compliance with all applicable laws in their jurisdiction.
|
||||||
|
|
||||||
## Status & Direction (2026-03-30)
|
## Status & Direction (2026-03-07)
|
||||||
|
|
||||||
- **Compatibility**: **Vanilla (Classic) 1.12 + TBC 2.4.3 + WotLK 3.3.5a** are all supported via expansion profiles and per-expansion packet parsers. All three expansions are roughly on par.
|
- **Compatibility**: **Vanilla (Classic) 1.12 + TBC 2.4.3 + WotLK 3.3.5a** are all supported via expansion profiles and per-expansion packet parsers (`src/game/packet_parsers_classic.cpp`, `src/game/packet_parsers_tbc.cpp`). All three expansions are roughly on par — no single one is significantly more complete than the others.
|
||||||
- **Tested against**: AzerothCore/ChromieCraft, TrinityCore, Mangos, and Turtle WoW (1.17).
|
- **Tested against**: AzerothCore, TrinityCore, Mangos, and Turtle WoW (1.17).
|
||||||
- **Current focus**: code quality (SOLID decomposition, documentation), rendering stability, and multi-expansion coverage.
|
- **Current focus**: instance dungeons, visual accuracy (lava/water, shadow mapping, WMO interiors), and multi-expansion coverage.
|
||||||
- **Warden**: Full module execution via Unicorn Engine CPU emulation. Decrypts (RC4→RSA→zlib), parses and relocates the PE module, executes via x86 emulation with Windows API interception. Module cache at `~/.local/share/wowee/warden_cache/`.
|
- **Warden**: Full module execution via Unicorn Engine CPU emulation. Decrypts (RC4→RSA→zlib), parses and relocates the PE module, executes via x86 emulation with Windows API interception. Module cache at `~/.local/share/wowee/warden_cache/`.
|
||||||
- **CI**: GitHub Actions builds for Linux (x86-64, ARM64), Windows (MSYS2 x86-64 + ARM64), and macOS (ARM64). Security scans via CodeQL, Semgrep, and sanitizers.
|
- **CI**: GitHub Actions builds for Linux (x86-64, ARM64), Windows (MSYS2), and macOS (ARM64). Security scans via CodeQL, Semgrep, and sanitizers.
|
||||||
- **Container builds**: Multi-platform Docker build system for Linux, macOS (arm64/x86_64 via osxcross), and Windows (LLVM-MinGW) cross-compilation.
|
|
||||||
- **Release**: v1.8.9-preview — 530+ WoW API functions, 140+ events, 664 opcode handlers.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|
@ -54,7 +52,7 @@ Protocol Compatible with **Vanilla (Classic) 1.12 + TBC 2.4.3 + WotLK 3.3.5a**.
|
||||||
- **Movement** -- WASD movement, camera orbit, spline path following, transport riding (trams, ships, zeppelins), movement ACK responses
|
- **Movement** -- WASD movement, camera orbit, spline path following, transport riding (trams, ships, zeppelins), movement ACK responses
|
||||||
- **Combat** -- Auto-attack, spell casting with cooldowns, damage calculation, death handling, spirit healer resurrection
|
- **Combat** -- Auto-attack, spell casting with cooldowns, damage calculation, death handling, spirit healer resurrection
|
||||||
- **Targeting** -- Tab-cycling with hostility filtering, click-to-target, faction-based hostility (using Faction.dbc)
|
- **Targeting** -- Tab-cycling with hostility filtering, click-to-target, faction-based hostility (using Faction.dbc)
|
||||||
- **Inventory** -- 23 equipment slots, 16 backpack slots, drag-drop, auto-equip, item tooltips with weapon damage/speed, server-synced bag sort (quality/type/stack), independent bag windows
|
- **Inventory** -- 23 equipment slots, 16 backpack slots, drag-drop, auto-equip, item tooltips with weapon damage/speed
|
||||||
- **Bank** -- Full bank support for all expansions, bag slots, drag-drop, right-click deposit (non-equippable items)
|
- **Bank** -- Full bank support for all expansions, bag slots, drag-drop, right-click deposit (non-equippable items)
|
||||||
- **Spells** -- Spellbook with specialty, general, profession, mount, and companion tabs; drag-drop to action bar; item use support
|
- **Spells** -- Spellbook with specialty, general, profession, mount, and companion tabs; drag-drop to action bar; item use support
|
||||||
- **Talents** -- Talent tree UI with proper visuals and functionality
|
- **Talents** -- Talent tree UI with proper visuals and functionality
|
||||||
|
|
@ -69,8 +67,7 @@ Protocol Compatible with **Vanilla (Classic) 1.12 + TBC 2.4.3 + WotLK 3.3.5a**.
|
||||||
- **Chat** -- Tabs/channels, emotes, chat bubbles, clickable URLs, clickable item links with tooltips
|
- **Chat** -- Tabs/channels, emotes, chat bubbles, clickable URLs, clickable item links with tooltips
|
||||||
- **Party** -- Group invites, party list, out-of-range member health via SMSG_PARTY_MEMBER_STATS
|
- **Party** -- Group invites, party list, out-of-range member health via SMSG_PARTY_MEMBER_STATS
|
||||||
- **Pets** -- Pet tracking via SMSG_PET_SPELLS, action bar (10 slots with icon/autocast tinting/tooltips), dismiss button
|
- **Pets** -- Pet tracking via SMSG_PET_SPELLS, action bar (10 slots with icon/autocast tinting/tooltips), dismiss button
|
||||||
- **Map Exploration** -- Subzone-level fog-of-war reveal, world map with continent/zone views, quest POI markers, taxi node markers, party member dots
|
- **Map Exploration** -- Subzone-level fog-of-war reveal matching retail behavior
|
||||||
- **NPC Voices** -- Race/gender-specific NPC greeting, farewell, vendor, pissed, aggro, and flee sounds for all playable races including Blood Elf and Draenei
|
|
||||||
- **Warden** -- Warden anti-cheat module execution via Unicorn Engine x86 emulation (cross-platform, no Wine)
|
- **Warden** -- Warden anti-cheat module execution via Unicorn Engine x86 emulation (cross-platform, no Wine)
|
||||||
- **UI** -- Loading screens with progress bar, settings window with graphics quality presets (LOW/MEDIUM/HIGH/ULTRA), shadow distance slider, minimap with zoom/rotation/square mode, top-right minimap mute speaker, separate bag windows with compact-empty mode (aggregate view)
|
- **UI** -- Loading screens with progress bar, settings window with graphics quality presets (LOW/MEDIUM/HIGH/ULTRA), shadow distance slider, minimap with zoom/rotation/square mode, top-right minimap mute speaker, separate bag windows with compact-empty mode (aggregate view)
|
||||||
|
|
||||||
|
|
@ -347,13 +344,3 @@ This project does not include any Blizzard Entertainment proprietary data, asset
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
MANY issues this is actively under development
|
MANY issues this is actively under development
|
||||||
|
|
||||||
## Star History
|
|
||||||
|
|
||||||
<a href="https://www.star-history.com/?repos=Kelsidavis%2FWoWee&type=date&legend=top-left">
|
|
||||||
<picture>
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=Kelsidavis/WoWee&type=date&theme=dark&legend=top-left" />
|
|
||||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=Kelsidavis/WoWee&type=date&legend=top-left" />
|
|
||||||
<img alt="Star History Chart" src="https://api.star-history.com/image?repos=Kelsidavis/WoWee&type=date&legend=top-left" />
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
|
|
|
||||||
390
TESTING.md
390
TESTING.md
|
|
@ -1,390 +0,0 @@
|
||||||
# WoWee Testing Guide
|
|
||||||
|
|
||||||
This document covers everything needed to build, run, lint, and extend the WoWee test suite.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
1. [Overview](#overview)
|
|
||||||
2. [Prerequisites](#prerequisites)
|
|
||||||
3. [Test Suite Layout](#test-suite-layout)
|
|
||||||
4. [Building the Tests](#building-the-tests)
|
|
||||||
- [Release Build (normal)](#release-build-normal)
|
|
||||||
- [Debug + ASAN/UBSan Build](#debug--asanubsan-build)
|
|
||||||
5. [Running Tests](#running-tests)
|
|
||||||
- [test.sh — the unified entry point](#testsh--the-unified-entry-point)
|
|
||||||
- [Running directly with ctest](#running-directly-with-ctest)
|
|
||||||
6. [Lint (clang-tidy)](#lint-clang-tidy)
|
|
||||||
- [Running lint](#running-lint)
|
|
||||||
- [Applying auto-fixes](#applying-auto-fixes)
|
|
||||||
- [Configuration (.clang-tidy)](#configuration-clang-tidy)
|
|
||||||
7. [ASAN / UBSan](#asan--ubsan)
|
|
||||||
8. [Adding New Tests](#adding-new-tests)
|
|
||||||
9. [CI Reference](#ci-reference)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
WoWee uses **Catch2 v3** (amalgamated) for unit testing and **clang-tidy** for static analysis. The `test.sh` script is the single entry point for both.
|
|
||||||
|
|
||||||
| Command | What it does |
|
|
||||||
|---|---|
|
|
||||||
| `./test.sh` | Runs both unit tests (Release) and lint |
|
|
||||||
| `./test.sh --test` | Runs unit tests only (Release build) |
|
|
||||||
| `./test.sh --lint` | Runs clang-tidy only |
|
|
||||||
| `./test.sh --asan` | Runs unit tests under ASAN + UBSan (Debug build) |
|
|
||||||
| `FIX=1 ./test.sh --lint` | Applies clang-tidy auto-fixes in-place |
|
|
||||||
|
|
||||||
All commands exit non-zero on any failure.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
The test suite requires the same base toolchain used to build the project. See [BUILD_INSTRUCTIONS.md](BUILD_INSTRUCTIONS.md) for platform-specific dependency installation.
|
|
||||||
|
|
||||||
### Linux (Ubuntu / Debian)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt update
|
|
||||||
sudo apt install -y \
|
|
||||||
build-essential cmake pkg-config git \
|
|
||||||
libssl-dev \
|
|
||||||
clang-tidy
|
|
||||||
```
|
|
||||||
|
|
||||||
### Linux (Arch)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo pacman -S --needed base-devel cmake pkgconf git openssl clang
|
|
||||||
```
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
|
|
||||||
```bash
|
|
||||||
brew install cmake openssl@3 llvm
|
|
||||||
# Add LLVM tools to PATH so clang-tidy is found:
|
|
||||||
export PATH="$(brew --prefix llvm)/bin:$PATH"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows (MSYS2)
|
|
||||||
|
|
||||||
Install the full toolchain as described in `BUILD_INSTRUCTIONS.md`, then add:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pacman -S --needed mingw-w64-x86_64-clang-tools-extra
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Test Suite Layout
|
|
||||||
|
|
||||||
```
|
|
||||||
tests/
|
|
||||||
CMakeLists.txt — CMake test configuration
|
|
||||||
test_packet.cpp — Network packet encode/decode
|
|
||||||
test_srp.cpp — SRP-6a authentication math (requires OpenSSL)
|
|
||||||
test_opcode_table.cpp — Opcode registry lookup
|
|
||||||
test_entity.cpp — ECS entity basics
|
|
||||||
test_dbc_loader.cpp — DBC binary file parsing
|
|
||||||
test_m2_structs.cpp — M2 model struct layout / alignment
|
|
||||||
test_blp_loader.cpp — BLP texture file parsing
|
|
||||||
test_frustum.cpp — View-frustum culling math
|
|
||||||
```
|
|
||||||
|
|
||||||
The Catch2 v3 amalgamated source lives at:
|
|
||||||
|
|
||||||
```
|
|
||||||
extern/catch2/
|
|
||||||
catch_amalgamated.hpp
|
|
||||||
catch_amalgamated.cpp
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Building the Tests
|
|
||||||
|
|
||||||
Tests are _not_ built by default. Enable them with `-DWOWEE_BUILD_TESTS=ON`.
|
|
||||||
|
|
||||||
### Release Build (normal)
|
|
||||||
|
|
||||||
> **Note:** Per project rules, always use `rebuild.sh` for a full clean build. Direct `cmake --build` is fine for test-only incremental builds.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Configure (only needed once)
|
|
||||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -DWOWEE_BUILD_TESTS=ON
|
|
||||||
|
|
||||||
# Build test targets (fast — only compiles tests and Catch2)
|
|
||||||
cmake --build build --target \
|
|
||||||
test_packet test_srp test_opcode_table test_entity \
|
|
||||||
test_dbc_loader test_m2_structs test_blp_loader test_frustum
|
|
||||||
```
|
|
||||||
|
|
||||||
Or simply run a full rebuild (builds everything including the main binary):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./rebuild.sh # ~10 minutes — see BUILD_INSTRUCTIONS.md
|
|
||||||
```
|
|
||||||
|
|
||||||
### Debug + ASAN/UBSan Build
|
|
||||||
|
|
||||||
A separate CMake build directory is used so ASAN flags do not pollute the Release binary.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cmake -B build_asan \
|
|
||||||
-DCMAKE_BUILD_TYPE=Debug \
|
|
||||||
-DWOWEE_ENABLE_ASAN=ON \
|
|
||||||
-DWOWEE_BUILD_TESTS=ON
|
|
||||||
|
|
||||||
cmake --build build_asan --target \
|
|
||||||
test_packet test_srp test_opcode_table test_entity \
|
|
||||||
test_dbc_loader test_m2_structs test_blp_loader test_frustum
|
|
||||||
```
|
|
||||||
|
|
||||||
CMake will print: `Test targets: ASAN + UBSan ENABLED` when configured correctly.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Running Tests
|
|
||||||
|
|
||||||
### test.sh — the unified entry point
|
|
||||||
|
|
||||||
`test.sh` is the recommended way to run tests and/or lint. It handles build-directory discovery, dependency checking, and exit-code aggregation across both steps.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run everything (tests + lint) — default when no flags are given
|
|
||||||
./test.sh
|
|
||||||
|
|
||||||
# Tests only (Release build)
|
|
||||||
./test.sh --test
|
|
||||||
|
|
||||||
# Tests only under ASAN+UBSan (Debug build — requires build_asan/)
|
|
||||||
./test.sh --asan
|
|
||||||
|
|
||||||
# Lint only
|
|
||||||
./test.sh --lint
|
|
||||||
|
|
||||||
# Both tests and lint explicitly
|
|
||||||
./test.sh --test --lint
|
|
||||||
|
|
||||||
# Usage summary
|
|
||||||
./test.sh --help
|
|
||||||
```
|
|
||||||
|
|
||||||
**Exit codes:**
|
|
||||||
|
|
||||||
| Outcome | Exit code |
|
|
||||||
|---|---|
|
|
||||||
| All tests passed, lint clean | `0` |
|
|
||||||
| Any test failed | `1` |
|
|
||||||
| Any lint diagnostic | `1` |
|
|
||||||
| Both test failure and lint issues | `1` |
|
|
||||||
|
|
||||||
### Running directly with ctest
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Release build
|
|
||||||
cd build
|
|
||||||
ctest --output-on-failure
|
|
||||||
|
|
||||||
# ASAN build
|
|
||||||
cd build_asan
|
|
||||||
ctest --output-on-failure
|
|
||||||
|
|
||||||
# Run one specific test suite by name
|
|
||||||
ctest --output-on-failure -R srp
|
|
||||||
|
|
||||||
# Verbose output (shows every SECTION and REQUIRE)
|
|
||||||
ctest --output-on-failure -V
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also run a test binary directly for detailed Catch2 output:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./build/bin/test_srp
|
|
||||||
./build/bin/test_srp --reporter console
|
|
||||||
./build/bin/test_srp "[authentication]" # run only tests tagged [authentication]
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Lint (clang-tidy)
|
|
||||||
|
|
||||||
The project uses clang-tidy to enforce C++20 best practices on all first-party sources under `src/`. Third-party code (anything in `extern/`) and generated files are excluded.
|
|
||||||
|
|
||||||
### Running lint
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./test.sh --lint
|
|
||||||
```
|
|
||||||
|
|
||||||
Under the hood the script:
|
|
||||||
|
|
||||||
1. Locates `clang-tidy` (tries versions 14–18, then `clang-tidy`).
|
|
||||||
2. Uses `run-clang-tidy` for parallel execution when available; falls back to sequential.
|
|
||||||
3. Reads `build/compile_commands.json` (generated by CMake) for compiler flags.
|
|
||||||
4. Feeds GCC stdlib include paths as `-isystem` extras so clang-tidy can resolve `<vector>`, `<string>`, etc. when the compile-commands were generated with GCC.
|
|
||||||
|
|
||||||
`compile_commands.json` is regenerated automatically by any CMake configure step. If you only want to update it without rebuilding:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cmake -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
|
||||||
```
|
|
||||||
|
|
||||||
### Applying auto-fixes
|
|
||||||
|
|
||||||
Some clang-tidy checks can apply fixes automatically (e.g. `modernize-*`, `readability-*`):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
FIX=1 ./test.sh --lint
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Caution:** Review the diff before committing — automatic fixes occasionally produce non-idiomatic results in complex template code.
|
|
||||||
|
|
||||||
### Configuration (.clang-tidy)
|
|
||||||
|
|
||||||
The active check set is defined in [.clang-tidy](.clang-tidy) at the repository root.
|
|
||||||
|
|
||||||
**Enabled check categories:**
|
|
||||||
|
|
||||||
| Category | What it catches |
|
|
||||||
|---|---|
|
|
||||||
| `bugprone-*` | Common bug patterns (signed overflow, misplaced `=`, etc.) |
|
|
||||||
| `clang-analyzer-*` | Deep flow-analysis: null dereferences, memory leaks, dead stores |
|
|
||||||
| `performance-*` | Unnecessary copies, inefficient STL usage |
|
|
||||||
| `modernize-*` (subset) | Pre-C++11 patterns that should use modern equivalents |
|
|
||||||
| `readability-*` (subset) | Control-flow simplification, redundant code |
|
|
||||||
|
|
||||||
**Notable suppressions** (see `.clang-tidy` for details):
|
|
||||||
|
|
||||||
| Suppressed check | Reason |
|
|
||||||
|---|---|
|
|
||||||
| `bugprone-easily-swappable-parameters` | High false-positive rate in graphics/math APIs |
|
|
||||||
| `clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling` | Intentional low-level buffer code in rendering |
|
|
||||||
| `performance-avoid-endl` | `std::endl` is used intentionally for logger flushing |
|
|
||||||
|
|
||||||
To suppress a specific warning inline, use:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
// NOLINT(bugprone-narrowing-conversions)
|
|
||||||
uint8_t byte = static_cast<uint8_t>(value); // NOLINT
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ASAN / UBSan
|
|
||||||
|
|
||||||
AddressSanitizer (ASAN) and Undefined Behaviour Sanitizer (UBSan) are applied to all test targets when `WOWEE_ENABLE_ASAN=ON`.
|
|
||||||
|
|
||||||
Both the test executables **and** the `catch2_main` static library are recompiled with:
|
|
||||||
|
|
||||||
```
|
|
||||||
-fsanitize=address,undefined -fno-omit-frame-pointer
|
|
||||||
```
|
|
||||||
|
|
||||||
This means any heap overflow, stack buffer overflow, use-after-free, null dereference, signed integer overflow, or misaligned access detected during a test will abort the process and print a human-readable report to stderr.
|
|
||||||
|
|
||||||
### Workflow
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Configure once (only needs to be re-run when CMakeLists.txt changes)
|
|
||||||
cmake -B build_asan \
|
|
||||||
-DCMAKE_BUILD_TYPE=Debug \
|
|
||||||
-DWOWEE_ENABLE_ASAN=ON \
|
|
||||||
-DWOWEE_BUILD_TESTS=ON
|
|
||||||
|
|
||||||
# 2. Build test binaries (fast incremental after the first build)
|
|
||||||
cmake --build build_asan --target test_packet test_srp # etc.
|
|
||||||
|
|
||||||
# 3. Run
|
|
||||||
./test.sh --asan
|
|
||||||
```
|
|
||||||
|
|
||||||
### Interpreting ASAN output
|
|
||||||
|
|
||||||
A failing ASAN report looks like:
|
|
||||||
|
|
||||||
```
|
|
||||||
==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000010
|
|
||||||
READ of size 4 at 0x602000000010 thread T0
|
|
||||||
#0 0x... in PacketBuffer::read_uint32 src/network/packet.cpp:42
|
|
||||||
#1 0x... in test_packet tests/test_packet.cpp:88
|
|
||||||
```
|
|
||||||
|
|
||||||
Address the issue in the source file and re-run. Do **not** suppress ASAN reports without a code fix.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Adding New Tests
|
|
||||||
|
|
||||||
1. **Create** `tests/test_<name>.cpp` with a standard Catch2 v3 structure:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
#include "catch_amalgamated.hpp"
|
|
||||||
|
|
||||||
TEST_CASE("SomeFeature does X", "[tag]") {
|
|
||||||
REQUIRE(1 + 1 == 2);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Register** the test in `tests/CMakeLists.txt` following the existing pattern:
|
|
||||||
|
|
||||||
```cmake
|
|
||||||
# ── test_<name> ──────────────────────────────────────────────
|
|
||||||
add_executable(test_<name>
|
|
||||||
test_<name>.cpp
|
|
||||||
${TEST_COMMON_SOURCES}
|
|
||||||
${CMAKE_SOURCE_DIR}/src/<module>/<file>.cpp # source under test
|
|
||||||
)
|
|
||||||
target_include_directories(test_<name> PRIVATE ${TEST_INCLUDE_DIRS})
|
|
||||||
target_include_directories(test_<name> SYSTEM PRIVATE ${TEST_SYSTEM_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(test_<name> PRIVATE catch2_main)
|
|
||||||
add_test(NAME <name> COMMAND test_<name>)
|
|
||||||
register_test_target(test_<name>) # required — enables ASAN propagation
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Build** and verify:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cmake --build build --target test_<name>
|
|
||||||
./test.sh --test
|
|
||||||
```
|
|
||||||
|
|
||||||
The `register_test_target()` macro call is **mandatory** — without it the new test will not receive ASAN/UBSan flags when `WOWEE_ENABLE_ASAN=ON`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## CI Reference
|
|
||||||
|
|
||||||
The following commands map to typical CI jobs:
|
|
||||||
|
|
||||||
| Job | Command |
|
|
||||||
|---|---|
|
|
||||||
| Unit tests (Release) | `./test.sh --test` |
|
|
||||||
| Unit tests (ASAN+UBSan) | `./test.sh --asan` |
|
|
||||||
| Lint | `./test.sh --lint` |
|
|
||||||
| Full check (tests + lint) | `./test.sh` |
|
|
||||||
|
|
||||||
**Configuring the ASAN job in CI:**
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Configure ASAN build
|
|
||||||
run: |
|
|
||||||
cmake -B build_asan \
|
|
||||||
-DCMAKE_BUILD_TYPE=Debug \
|
|
||||||
-DWOWEE_ENABLE_ASAN=ON \
|
|
||||||
-DWOWEE_BUILD_TESTS=ON
|
|
||||||
|
|
||||||
- name: Build test targets
|
|
||||||
run: |
|
|
||||||
cmake --build build_asan --target \
|
|
||||||
test_packet test_srp test_opcode_table test_entity \
|
|
||||||
test_dbc_loader test_m2_structs test_blp_loader test_frustum
|
|
||||||
|
|
||||||
- name: Run ASAN tests
|
|
||||||
run: ./test.sh --asan
|
|
||||||
```
|
|
||||||
|
|
||||||
> See [BUILD_INSTRUCTIONS.md](BUILD_INSTRUCTIONS.md) for full platform dependency installation steps required before any CI job.
|
|
||||||
|
|
@ -151,7 +151,9 @@ Graphics Preset: HIGH or ULTRA
|
||||||
## Getting Help
|
## Getting Help
|
||||||
|
|
||||||
### Check Logs
|
### Check Logs
|
||||||
Detailed logs are saved to `logs/wowee.log` in the working directory (typically `build/bin/`).
|
Detailed logs are saved to:
|
||||||
|
- **Linux/macOS**: `~/.wowee/logs/`
|
||||||
|
- **Windows**: `%APPDATA%\wowee\logs\`
|
||||||
|
|
||||||
Include relevant log entries when reporting issues.
|
Include relevant log entries when reporting issues.
|
||||||
|
|
||||||
|
|
|
||||||
38
assets/shaders/basic.frag
Normal file
38
assets/shaders/basic.frag
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec3 FragPos;
|
||||||
|
in vec3 Normal;
|
||||||
|
in vec2 TexCoord;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
uniform vec3 uLightPos;
|
||||||
|
uniform vec3 uViewPos;
|
||||||
|
uniform vec4 uColor;
|
||||||
|
uniform sampler2D uTexture;
|
||||||
|
uniform bool uUseTexture;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Ambient
|
||||||
|
vec3 ambient = 0.3 * vec3(1.0);
|
||||||
|
|
||||||
|
// Diffuse
|
||||||
|
vec3 norm = normalize(Normal);
|
||||||
|
vec3 lightDir = normalize(uLightPos - FragPos);
|
||||||
|
float diff = max(dot(norm, lightDir), 0.0);
|
||||||
|
vec3 diffuse = diff * vec3(1.0);
|
||||||
|
|
||||||
|
// Specular
|
||||||
|
vec3 viewDir = normalize(uViewPos - FragPos);
|
||||||
|
vec3 reflectDir = reflect(-lightDir, norm);
|
||||||
|
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
|
||||||
|
vec3 specular = 0.5 * spec * vec3(1.0);
|
||||||
|
|
||||||
|
vec3 result = (ambient + diffuse + specular);
|
||||||
|
|
||||||
|
if (uUseTexture) {
|
||||||
|
FragColor = texture(uTexture, TexCoord) * vec4(result, 1.0);
|
||||||
|
} else {
|
||||||
|
FragColor = uColor * vec4(result, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
assets/shaders/basic.vert
Normal file
22
assets/shaders/basic.vert
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
layout (location = 0) in vec3 aPosition;
|
||||||
|
layout (location = 1) in vec3 aNormal;
|
||||||
|
layout (location = 2) in vec2 aTexCoord;
|
||||||
|
|
||||||
|
out vec3 FragPos;
|
||||||
|
out vec3 Normal;
|
||||||
|
out vec2 TexCoord;
|
||||||
|
|
||||||
|
uniform mat4 uModel;
|
||||||
|
uniform mat4 uView;
|
||||||
|
uniform mat4 uProjection;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
FragPos = vec3(uModel * vec4(aPosition, 1.0));
|
||||||
|
// Use mat3(uModel) directly - avoids expensive inverse() per vertex
|
||||||
|
Normal = mat3(uModel) * aNormal;
|
||||||
|
TexCoord = aTexCoord;
|
||||||
|
|
||||||
|
gl_Position = uProjection * uView * vec4(FragPos, 1.0);
|
||||||
|
}
|
||||||
|
|
@ -126,14 +126,7 @@ void main() {
|
||||||
|
|
||||||
vec4 texColor = texture(uTexture, finalUV);
|
vec4 texColor = texture(uTexture, finalUV);
|
||||||
|
|
||||||
if (alphaTest != 0) {
|
if (alphaTest != 0 && texColor.a < 0.5) discard;
|
||||||
// Screen-space sharpened alpha for alpha-to-coverage anti-aliasing.
|
|
||||||
// Rescales alpha so the 0.5 cutoff maps to exactly the texel boundary,
|
|
||||||
// giving smooth edges when MSAA + alpha-to-coverage is active.
|
|
||||||
float aGrad = fwidth(texColor.a);
|
|
||||||
texColor.a = clamp((texColor.a - 0.5) / max(aGrad, 0.001) * 0.5 + 0.5, 0.0, 1.0);
|
|
||||||
if (texColor.a < 1.0 / 255.0) discard;
|
|
||||||
}
|
|
||||||
if (colorKeyBlack != 0) {
|
if (colorKeyBlack != 0) {
|
||||||
float lum = dot(texColor.rgb, vec3(0.299, 0.587, 0.114));
|
float lum = dot(texColor.rgb, vec3(0.299, 0.587, 0.114));
|
||||||
float ck = smoothstep(0.12, 0.30, lum);
|
float ck = smoothstep(0.12, 0.30, lum);
|
||||||
|
|
@ -176,7 +169,7 @@ void main() {
|
||||||
if (proj.x >= 0.0 && proj.x <= 1.0 &&
|
if (proj.x >= 0.0 && proj.x <= 1.0 &&
|
||||||
proj.y >= 0.0 && proj.y <= 1.0 &&
|
proj.y >= 0.0 && proj.y <= 1.0 &&
|
||||||
proj.z >= 0.0 && proj.z <= 1.0) {
|
proj.z >= 0.0 && proj.z <= 1.0) {
|
||||||
float bias = max(0.0005 * (1.0 - abs(dot(norm, ldir))), 0.00005);
|
float bias = max(0.0005 * (1.0 - dot(norm, ldir)), 0.00005);
|
||||||
shadow = sampleShadowPCF(uShadowMap, vec3(proj.xy, proj.z - bias));
|
shadow = sampleShadowPCF(uShadowMap, vec3(proj.xy, proj.z - bias));
|
||||||
}
|
}
|
||||||
shadow = mix(1.0, shadow, shadowParams.y);
|
shadow = mix(1.0, shadow, shadowParams.y);
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -10,7 +10,9 @@ layout(push_constant) uniform PushConstants {
|
||||||
} pc;
|
} pc;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 tc = TexCoord;
|
// Undo the vertex shader Y flip (postprocess.vert flips for Vulkan overlay,
|
||||||
|
// but we need standard UV coords for texture sampling)
|
||||||
|
vec2 tc = vec2(TexCoord.x, 1.0 - TexCoord.y);
|
||||||
|
|
||||||
vec2 texelSize = pc.params.xy;
|
vec2 texelSize = pc.params.xy;
|
||||||
float sharpness = pc.params.z;
|
float sharpness = pc.params.z;
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -21,7 +21,9 @@ vec3 fsrFetch(vec2 p, vec2 off) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 tc = TexCoord;
|
// Undo the vertex shader Y flip (postprocess.vert flips for Vulkan overlay,
|
||||||
|
// but we need standard UV coords for texture sampling)
|
||||||
|
vec2 tc = vec2(TexCoord.x, 1.0 - TexCoord.y);
|
||||||
|
|
||||||
// Map output pixel to input space
|
// Map output pixel to input space
|
||||||
vec2 pp = tc * fsr.con2.xy; // output pixel position
|
vec2 pp = tc * fsr.con2.xy; // output pixel position
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,155 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// FXAA 3.11 — Fast Approximate Anti-Aliasing post-process pass.
|
|
||||||
// Reads the resolved scene color and outputs a smoothed result.
|
|
||||||
// Push constant: rcpFrame = vec2(1/width, 1/height), sharpness (0=off, 2=max), desaturate (1=ghost grayscale).
|
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform sampler2D uScene;
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 TexCoord;
|
|
||||||
layout(location = 0) out vec4 outColor;
|
|
||||||
|
|
||||||
layout(push_constant) uniform PC {
|
|
||||||
vec2 rcpFrame;
|
|
||||||
float sharpness; // 0 = no sharpen, 2 = max (matches FSR2 RCAS range)
|
|
||||||
float desaturate; // 1 = full grayscale (ghost mode), 0 = normal color
|
|
||||||
} pc;
|
|
||||||
|
|
||||||
// Quality tuning
|
|
||||||
#define FXAA_EDGE_THRESHOLD (1.0/8.0) // minimum edge contrast to process
|
|
||||||
#define FXAA_EDGE_THRESHOLD_MIN (1.0/24.0) // ignore very dark regions
|
|
||||||
#define FXAA_SEARCH_STEPS 12
|
|
||||||
#define FXAA_SEARCH_THRESHOLD (1.0/4.0)
|
|
||||||
#define FXAA_SUBPIX 0.75
|
|
||||||
#define FXAA_SUBPIX_TRIM (1.0/4.0)
|
|
||||||
#define FXAA_SUBPIX_TRIM_SCALE (1.0/(1.0 - FXAA_SUBPIX_TRIM))
|
|
||||||
#define FXAA_SUBPIX_CAP (3.0/4.0)
|
|
||||||
|
|
||||||
float luma(vec3 c) {
|
|
||||||
return dot(c, vec3(0.299, 0.587, 0.114));
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec2 uv = TexCoord;
|
|
||||||
vec2 rcp = pc.rcpFrame;
|
|
||||||
|
|
||||||
// --- Centre and cardinal neighbours ---
|
|
||||||
vec3 rgbM = texture(uScene, uv).rgb;
|
|
||||||
vec3 rgbN = texture(uScene, uv + vec2( 0.0, -1.0) * rcp).rgb;
|
|
||||||
vec3 rgbS = texture(uScene, uv + vec2( 0.0, 1.0) * rcp).rgb;
|
|
||||||
vec3 rgbE = texture(uScene, uv + vec2( 1.0, 0.0) * rcp).rgb;
|
|
||||||
vec3 rgbW = texture(uScene, uv + vec2(-1.0, 0.0) * rcp).rgb;
|
|
||||||
|
|
||||||
float lumaN = luma(rgbN);
|
|
||||||
float lumaS = luma(rgbS);
|
|
||||||
float lumaE = luma(rgbE);
|
|
||||||
float lumaW = luma(rgbW);
|
|
||||||
float lumaM = luma(rgbM);
|
|
||||||
|
|
||||||
float lumaMin = min(lumaM, min(min(lumaN, lumaS), min(lumaE, lumaW)));
|
|
||||||
float lumaMax = max(lumaM, max(max(lumaN, lumaS), max(lumaE, lumaW)));
|
|
||||||
float range = lumaMax - lumaMin;
|
|
||||||
|
|
||||||
// Early exit on smooth regions
|
|
||||||
if (range < max(FXAA_EDGE_THRESHOLD_MIN, lumaMax * FXAA_EDGE_THRESHOLD)) {
|
|
||||||
outColor = vec4(rgbM, 1.0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Diagonal neighbours ---
|
|
||||||
vec3 rgbNW = texture(uScene, uv + vec2(-1.0, -1.0) * rcp).rgb;
|
|
||||||
vec3 rgbNE = texture(uScene, uv + vec2( 1.0, -1.0) * rcp).rgb;
|
|
||||||
vec3 rgbSW = texture(uScene, uv + vec2(-1.0, 1.0) * rcp).rgb;
|
|
||||||
vec3 rgbSE = texture(uScene, uv + vec2( 1.0, 1.0) * rcp).rgb;
|
|
||||||
|
|
||||||
float lumaNW = luma(rgbNW);
|
|
||||||
float lumaNE = luma(rgbNE);
|
|
||||||
float lumaSW = luma(rgbSW);
|
|
||||||
float lumaSE = luma(rgbSE);
|
|
||||||
|
|
||||||
// --- Sub-pixel blend factor ---
|
|
||||||
float lumaL = (lumaN + lumaS + lumaE + lumaW) * 0.25;
|
|
||||||
float rangeL = abs(lumaL - lumaM);
|
|
||||||
float blendL = max(0.0, (rangeL / range) - FXAA_SUBPIX_TRIM) * FXAA_SUBPIX_TRIM_SCALE;
|
|
||||||
blendL = min(FXAA_SUBPIX_CAP, blendL) * FXAA_SUBPIX;
|
|
||||||
|
|
||||||
// --- Edge orientation (horizontal vs. vertical) ---
|
|
||||||
float edgeHorz =
|
|
||||||
abs(-2.0*lumaW + lumaNW + lumaSW)
|
|
||||||
+ 2.0*abs(-2.0*lumaM + lumaN + lumaS)
|
|
||||||
+ abs(-2.0*lumaE + lumaNE + lumaSE);
|
|
||||||
float edgeVert =
|
|
||||||
abs(-2.0*lumaS + lumaSW + lumaSE)
|
|
||||||
+ 2.0*abs(-2.0*lumaM + lumaW + lumaE)
|
|
||||||
+ abs(-2.0*lumaN + lumaNW + lumaNE);
|
|
||||||
|
|
||||||
bool horzSpan = (edgeHorz >= edgeVert);
|
|
||||||
float lengthSign = horzSpan ? rcp.y : rcp.x;
|
|
||||||
|
|
||||||
float luma1 = horzSpan ? lumaN : lumaW;
|
|
||||||
float luma2 = horzSpan ? lumaS : lumaE;
|
|
||||||
float grad1 = abs(luma1 - lumaM);
|
|
||||||
float grad2 = abs(luma2 - lumaM);
|
|
||||||
lengthSign = (grad1 >= grad2) ? -lengthSign : lengthSign;
|
|
||||||
|
|
||||||
// --- Edge search ---
|
|
||||||
vec2 posB = uv;
|
|
||||||
vec2 offNP = horzSpan ? vec2(rcp.x, 0.0) : vec2(0.0, rcp.y);
|
|
||||||
if (!horzSpan) posB.x += lengthSign * 0.5;
|
|
||||||
if ( horzSpan) posB.y += lengthSign * 0.5;
|
|
||||||
|
|
||||||
float lumaMLSS = lumaM - (luma1 + luma2) * 0.5;
|
|
||||||
float gradientScaled = max(grad1, grad2) * 0.25;
|
|
||||||
|
|
||||||
vec2 posN = posB - offNP;
|
|
||||||
vec2 posP = posB + offNP;
|
|
||||||
bool done1 = false, done2 = false;
|
|
||||||
float lumaEnd1 = 0.0, lumaEnd2 = 0.0;
|
|
||||||
|
|
||||||
for (int i = 0; i < FXAA_SEARCH_STEPS; ++i) {
|
|
||||||
if (!done1) lumaEnd1 = luma(texture(uScene, posN).rgb) - lumaMLSS;
|
|
||||||
if (!done2) lumaEnd2 = luma(texture(uScene, posP).rgb) - lumaMLSS;
|
|
||||||
done1 = done1 || (abs(lumaEnd1) >= gradientScaled * FXAA_SEARCH_THRESHOLD);
|
|
||||||
done2 = done2 || (abs(lumaEnd2) >= gradientScaled * FXAA_SEARCH_THRESHOLD);
|
|
||||||
if (done1 && done2) break;
|
|
||||||
if (!done1) posN -= offNP;
|
|
||||||
if (!done2) posP += offNP;
|
|
||||||
}
|
|
||||||
|
|
||||||
float dstN = horzSpan ? (uv.x - posN.x) : (uv.y - posN.y);
|
|
||||||
float dstP = horzSpan ? (posP.x - uv.x) : (posP.y - uv.y);
|
|
||||||
bool dirN = (dstN < dstP);
|
|
||||||
float lumaEndFinal = dirN ? lumaEnd1 : lumaEnd2;
|
|
||||||
|
|
||||||
float spanLength = dstN + dstP;
|
|
||||||
float pixelOffset = (dirN ? dstN : dstP) / spanLength;
|
|
||||||
bool goodSpan = ((lumaEndFinal < 0.0) != (lumaMLSS < 0.0));
|
|
||||||
float pixelOffsetFinal = max(goodSpan ? pixelOffset : 0.0, blendL);
|
|
||||||
|
|
||||||
vec2 finalUV = uv;
|
|
||||||
if ( horzSpan) finalUV.y += pixelOffsetFinal * lengthSign;
|
|
||||||
if (!horzSpan) finalUV.x += pixelOffsetFinal * lengthSign;
|
|
||||||
|
|
||||||
vec3 fxaaResult = texture(uScene, finalUV).rgb;
|
|
||||||
|
|
||||||
// Post-FXAA contrast-adaptive sharpening (unsharp mask).
|
|
||||||
// Counteracts FXAA's sub-pixel blur when sharpness > 0.
|
|
||||||
if (pc.sharpness > 0.0) {
|
|
||||||
vec2 r = pc.rcpFrame;
|
|
||||||
vec3 blur = (texture(uScene, uv + vec2(-r.x, 0)).rgb
|
|
||||||
+ texture(uScene, uv + vec2( r.x, 0)).rgb
|
|
||||||
+ texture(uScene, uv + vec2(0, -r.y)).rgb
|
|
||||||
+ texture(uScene, uv + vec2(0, r.y)).rgb) * 0.25;
|
|
||||||
// scale sharpness from [0,2] to a modest [0, 0.3] boost factor
|
|
||||||
float s = pc.sharpness * 0.15;
|
|
||||||
fxaaResult = clamp(fxaaResult + s * (fxaaResult - blur), 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ghost mode: desaturate to grayscale (with a slight cool blue tint).
|
|
||||||
if (pc.desaturate > 0.5) {
|
|
||||||
float gray = dot(fxaaResult, vec3(0.299, 0.587, 0.114));
|
|
||||||
fxaaResult = mix(fxaaResult, vec3(gray, gray, gray * 1.05), pc.desaturate);
|
|
||||||
}
|
|
||||||
|
|
||||||
outColor = vec4(fxaaResult, 1.0);
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
|
@ -1,57 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// Hierarchical-Z depth pyramid builder.
|
|
||||||
// Builds successive mip levels from the scene depth buffer.
|
|
||||||
// Each 2×2 block is reduced to its MAXIMUM depth (farthest/largest value).
|
|
||||||
// This is conservative for occlusion: an object is only culled when its nearest
|
|
||||||
// depth exceeds the farthest occluder depth in the pyramid region.
|
|
||||||
//
|
|
||||||
// Two modes controlled by push constant:
|
|
||||||
// mipLevel == 0: Sample from the source depth texture (mip 0 of the full-res depth).
|
|
||||||
// mipLevel > 0: Sample from the previous HiZ mip level.
|
|
||||||
|
|
||||||
layout(local_size_x = 8, local_size_y = 8) in;
|
|
||||||
|
|
||||||
// Source depth texture (full-resolution scene depth, or previous mip via same image)
|
|
||||||
layout(set = 0, binding = 0) uniform sampler2D srcDepth;
|
|
||||||
|
|
||||||
// Destination mip level (written as storage image)
|
|
||||||
layout(r32f, set = 0, binding = 1) uniform writeonly image2D dstMip;
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
|
||||||
ivec2 dstSize; // Width and height of the destination mip level
|
|
||||||
int mipLevel; // Current mip level being built (0 = from scene depth)
|
|
||||||
};
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
|
|
||||||
if (pos.x >= dstSize.x || pos.y >= dstSize.y) return;
|
|
||||||
|
|
||||||
// Each output texel covers a 2×2 block of the source.
|
|
||||||
// Use texelFetch for precise texel access (no filtering).
|
|
||||||
ivec2 srcPos = pos * 2;
|
|
||||||
|
|
||||||
float d00, d10, d01, d11;
|
|
||||||
|
|
||||||
if (mipLevel == 0) {
|
|
||||||
// Sample from full-res scene depth (sampler2D, lod 0)
|
|
||||||
d00 = texelFetch(srcDepth, srcPos + ivec2(0, 0), 0).r;
|
|
||||||
d10 = texelFetch(srcDepth, srcPos + ivec2(1, 0), 0).r;
|
|
||||||
d01 = texelFetch(srcDepth, srcPos + ivec2(0, 1), 0).r;
|
|
||||||
d11 = texelFetch(srcDepth, srcPos + ivec2(1, 1), 0).r;
|
|
||||||
} else {
|
|
||||||
// Sample from previous HiZ mip level (mipLevel - 1)
|
|
||||||
d00 = texelFetch(srcDepth, srcPos + ivec2(0, 0), mipLevel - 1).r;
|
|
||||||
d10 = texelFetch(srcDepth, srcPos + ivec2(1, 0), mipLevel - 1).r;
|
|
||||||
d01 = texelFetch(srcDepth, srcPos + ivec2(0, 1), mipLevel - 1).r;
|
|
||||||
d11 = texelFetch(srcDepth, srcPos + ivec2(1, 1), mipLevel - 1).r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conservative maximum (standard depth buffer: 0=near, 1=far).
|
|
||||||
// We store the farthest (largest) depth in each 2×2 block.
|
|
||||||
// An object is occluded only when its nearest depth > the farthest occluder
|
|
||||||
// depth in the covered screen region — guaranteeing it's behind EVERYTHING.
|
|
||||||
float maxDepth = max(max(d00, d10), max(d01, d11));
|
|
||||||
|
|
||||||
imageStore(dstMip, pos, vec4(maxDepth));
|
|
||||||
}
|
|
||||||
|
|
@ -13,29 +13,19 @@ layout(set = 0, binding = 0) uniform PerFrame {
|
||||||
vec4 shadowParams;
|
vec4 shadowParams;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Per-draw push constants (batch-level data only)
|
|
||||||
layout(push_constant) uniform Push {
|
layout(push_constant) uniform Push {
|
||||||
int texCoordSet; // UV set index (0 or 1)
|
mat4 model;
|
||||||
int isFoliage; // Foliage wind animation flag
|
vec2 uvOffset;
|
||||||
int instanceDataOffset; // Base index into InstanceSSBO for this draw group
|
int texCoordSet;
|
||||||
|
int useBones;
|
||||||
|
int isFoliage;
|
||||||
|
float fadeAlpha;
|
||||||
} push;
|
} push;
|
||||||
|
|
||||||
layout(set = 2, binding = 0) readonly buffer BoneSSBO {
|
layout(set = 2, binding = 0) readonly buffer BoneSSBO {
|
||||||
mat4 bones[];
|
mat4 bones[];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Per-instance data read via gl_InstanceIndex (GPU instancing)
|
|
||||||
struct InstanceData {
|
|
||||||
mat4 model;
|
|
||||||
vec2 uvOffset;
|
|
||||||
float fadeAlpha;
|
|
||||||
int useBones;
|
|
||||||
int boneBase;
|
|
||||||
};
|
|
||||||
layout(set = 3, binding = 0) readonly buffer InstanceSSBO {
|
|
||||||
InstanceData instanceData[];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 aPos;
|
layout(location = 0) in vec3 aPos;
|
||||||
layout(location = 1) in vec3 aNormal;
|
layout(location = 1) in vec3 aNormal;
|
||||||
layout(location = 2) in vec2 aTexCoord;
|
layout(location = 2) in vec2 aTexCoord;
|
||||||
|
|
@ -51,23 +41,15 @@ layout(location = 4) out float ModelHeight;
|
||||||
layout(location = 5) out float vFadeAlpha;
|
layout(location = 5) out float vFadeAlpha;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// Fetch per-instance data from SSBO
|
|
||||||
int instIdx = push.instanceDataOffset + gl_InstanceIndex;
|
|
||||||
mat4 model = instanceData[instIdx].model;
|
|
||||||
vec2 uvOff = instanceData[instIdx].uvOffset;
|
|
||||||
float fade = instanceData[instIdx].fadeAlpha;
|
|
||||||
int uBones = instanceData[instIdx].useBones;
|
|
||||||
int bBase = instanceData[instIdx].boneBase;
|
|
||||||
|
|
||||||
vec4 pos = vec4(aPos, 1.0);
|
vec4 pos = vec4(aPos, 1.0);
|
||||||
vec4 norm = vec4(aNormal, 0.0);
|
vec4 norm = vec4(aNormal, 0.0);
|
||||||
|
|
||||||
if (uBones != 0) {
|
if (push.useBones != 0) {
|
||||||
ivec4 bi = ivec4(aBoneIndicesF);
|
ivec4 bi = ivec4(aBoneIndicesF);
|
||||||
mat4 skinMat = bones[bBase + bi.x] * aBoneWeights.x
|
mat4 skinMat = bones[bi.x] * aBoneWeights.x
|
||||||
+ bones[bBase + bi.y] * aBoneWeights.y
|
+ bones[bi.y] * aBoneWeights.y
|
||||||
+ bones[bBase + bi.z] * aBoneWeights.z
|
+ bones[bi.z] * aBoneWeights.z
|
||||||
+ bones[bBase + bi.w] * aBoneWeights.w;
|
+ bones[bi.w] * aBoneWeights.w;
|
||||||
pos = skinMat * pos;
|
pos = skinMat * pos;
|
||||||
norm = skinMat * norm;
|
norm = skinMat * norm;
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +57,7 @@ void main() {
|
||||||
// Wind animation for foliage
|
// Wind animation for foliage
|
||||||
if (push.isFoliage != 0) {
|
if (push.isFoliage != 0) {
|
||||||
float windTime = fogParams.z;
|
float windTime = fogParams.z;
|
||||||
vec3 worldRef = model[3].xyz;
|
vec3 worldRef = push.model[3].xyz;
|
||||||
float heightFactor = clamp(pos.z / 20.0, 0.0, 1.0);
|
float heightFactor = clamp(pos.z / 20.0, 0.0, 1.0);
|
||||||
heightFactor *= heightFactor; // quadratic — base stays grounded
|
heightFactor *= heightFactor; // quadratic — base stays grounded
|
||||||
|
|
||||||
|
|
@ -98,15 +80,15 @@ void main() {
|
||||||
pos.y += trunkSwayY + branchSwayY + leafFlutterY;
|
pos.y += trunkSwayY + branchSwayY + leafFlutterY;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 worldPos = model * pos;
|
vec4 worldPos = push.model * pos;
|
||||||
FragPos = worldPos.xyz;
|
FragPos = worldPos.xyz;
|
||||||
Normal = mat3(model) * norm.xyz;
|
Normal = mat3(push.model) * norm.xyz;
|
||||||
|
|
||||||
TexCoord = (push.texCoordSet == 1 ? aTexCoord2 : aTexCoord) + uvOff;
|
TexCoord = (push.texCoordSet == 1 ? aTexCoord2 : aTexCoord) + push.uvOffset;
|
||||||
|
|
||||||
InstanceOrigin = model[3].xyz;
|
InstanceOrigin = push.model[3].xyz;
|
||||||
ModelHeight = pos.z;
|
ModelHeight = pos.z;
|
||||||
vFadeAlpha = fade;
|
vFadeAlpha = push.fadeAlpha;
|
||||||
|
|
||||||
gl_Position = projection * view * worldPos;
|
gl_Position = projection * view * worldPos;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1,76 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// GPU Frustum Culling for M2 doodads
|
|
||||||
// Each compute thread tests one M2 instance against 6 frustum planes.
|
|
||||||
// Input: per-instance bounding sphere + flags.
|
|
||||||
// Output: uint visibility array (1 = visible, 0 = culled).
|
|
||||||
|
|
||||||
layout(local_size_x = 64) in;
|
|
||||||
|
|
||||||
// Per-instance cull data (uploaded from CPU each frame)
|
|
||||||
struct CullInstance {
|
|
||||||
vec4 sphere; // xyz = world position, w = padded radius
|
|
||||||
float effectiveMaxDistSq; // adaptive distance cull threshold
|
|
||||||
uint flags; // bit 0 = valid, bit 1 = smoke, bit 2 = invisibleTrap
|
|
||||||
float _pad0;
|
|
||||||
float _pad1;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(std140, set = 0, binding = 0) uniform CullUniforms {
|
|
||||||
vec4 frustumPlanes[6]; // xyz = normal, w = distance
|
|
||||||
vec4 cameraPos; // xyz = camera position, w = maxPossibleDistSq
|
|
||||||
uint instanceCount;
|
|
||||||
uint _pad0;
|
|
||||||
uint _pad1;
|
|
||||||
uint _pad2;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(std430, set = 0, binding = 1) readonly buffer CullInput {
|
|
||||||
CullInstance cullInstances[];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(std430, set = 0, binding = 2) writeonly buffer CullOutput {
|
|
||||||
uint visibility[];
|
|
||||||
};
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
uint id = gl_GlobalInvocationID.x;
|
|
||||||
if (id >= instanceCount) return;
|
|
||||||
|
|
||||||
CullInstance inst = cullInstances[id];
|
|
||||||
|
|
||||||
// Flag check: must be valid, not smoke, not invisible trap
|
|
||||||
uint f = inst.flags;
|
|
||||||
if ((f & 1u) == 0u || (f & 6u) != 0u) {
|
|
||||||
visibility[id] = 0u;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Early distance rejection (loose upper bound)
|
|
||||||
vec3 toCam = inst.sphere.xyz - cameraPos.xyz;
|
|
||||||
float distSq = dot(toCam, toCam);
|
|
||||||
if (distSq > cameraPos.w) {
|
|
||||||
visibility[id] = 0u;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accurate per-instance distance cull
|
|
||||||
if (distSq > inst.effectiveMaxDistSq) {
|
|
||||||
visibility[id] = 0u;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frustum cull: sphere vs 6 planes
|
|
||||||
float radius = inst.sphere.w;
|
|
||||||
if (radius > 0.0) {
|
|
||||||
for (int i = 0; i < 6; i++) {
|
|
||||||
float d = dot(frustumPlanes[i].xyz, inst.sphere.xyz) + frustumPlanes[i].w;
|
|
||||||
if (d < -radius) {
|
|
||||||
visibility[id] = 0u;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visibility[id] = 1u;
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
|
@ -1,184 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// GPU Frustum + HiZ Occlusion Culling for M2 doodads (Phase 6.3).
|
|
||||||
//
|
|
||||||
// Two-level culling:
|
|
||||||
// 1. Frustum — current-frame planes from viewProj.
|
|
||||||
// 2. HiZ occlusion — projects bounding sphere into the PREVIOUS frame's
|
|
||||||
// screen space via prevViewProj and samples the Hierarchical-Z pyramid
|
|
||||||
// (built from said previous depth). Conservative safeguards:
|
|
||||||
// • Only objects that were visible last frame get the HiZ test.
|
|
||||||
// • AABB must be fully inside the screen (no border sampling).
|
|
||||||
// • Bounding sphere is inflated by 50 % for the HiZ AABB.
|
|
||||||
// • A depth bias is applied before the occlusion comparison.
|
|
||||||
// • Nearest depth is projected via prevViewProj from sphere center
|
|
||||||
// (avoids toCam mismatch between current and previous cameras).
|
|
||||||
//
|
|
||||||
// Falls back gracefully: if hizEnabled == 0, behaves identically to frustum-only.
|
|
||||||
|
|
||||||
layout(local_size_x = 64) in;
|
|
||||||
|
|
||||||
struct CullInstance {
|
|
||||||
vec4 sphere; // xyz = world position, w = padded radius
|
|
||||||
float effectiveMaxDistSq;
|
|
||||||
uint flags; // bit 0 = valid, bit 1 = smoke, bit 2 = invisibleTrap,
|
|
||||||
// bit 3 = previouslyVisible
|
|
||||||
float _pad0;
|
|
||||||
float _pad1;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(std140, set = 0, binding = 0) uniform CullUniforms {
|
|
||||||
vec4 frustumPlanes[6];
|
|
||||||
vec4 cameraPos; // xyz = camera position, w = maxPossibleDistSq
|
|
||||||
uint instanceCount;
|
|
||||||
uint hizEnabled;
|
|
||||||
uint hizMipLevels;
|
|
||||||
uint _pad2;
|
|
||||||
vec4 hizParams; // x = pyramidWidth, y = pyramidHeight, z = nearPlane, w = unused
|
|
||||||
mat4 viewProj; // current frame view-projection
|
|
||||||
mat4 prevViewProj; // PREVIOUS frame's view-projection for HiZ reprojection
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(std430, set = 0, binding = 1) readonly buffer CullInput {
|
|
||||||
CullInstance cullInstances[];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(std430, set = 0, binding = 2) buffer CullOutput {
|
|
||||||
uint visibility[];
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(set = 1, binding = 0) uniform sampler2D hizPyramid;
|
|
||||||
|
|
||||||
// Screen-edge margin — skip HiZ if the AABB touches this border.
|
|
||||||
// Depth data at screen edges is from unrelated geometry → false culls.
|
|
||||||
const float SCREEN_EDGE_MARGIN = 0.02;
|
|
||||||
|
|
||||||
// Sphere inflation factor for HiZ screen AABB (50 % larger → very conservative).
|
|
||||||
const float HIZ_SPHERE_INFLATE = 1.5;
|
|
||||||
|
|
||||||
// Depth bias — push nearest depth closer to camera so only objects
|
|
||||||
// significantly behind occluders are culled.
|
|
||||||
const float HIZ_DEPTH_BIAS = 0.02;
|
|
||||||
|
|
||||||
// Minimum screen-space size (pixels) for HiZ to engage.
|
|
||||||
const float HIZ_MIN_SCREEN_PX = 6.0;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
uint id = gl_GlobalInvocationID.x;
|
|
||||||
if (id >= instanceCount) return;
|
|
||||||
|
|
||||||
CullInstance inst = cullInstances[id];
|
|
||||||
|
|
||||||
// Flag check: must be valid, not smoke, not invisible trap
|
|
||||||
uint f = inst.flags;
|
|
||||||
if ((f & 1u) == 0u || (f & 6u) != 0u) {
|
|
||||||
visibility[id] = 0u;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Early distance rejection (loose upper bound)
|
|
||||||
vec3 toCam = inst.sphere.xyz - cameraPos.xyz;
|
|
||||||
float distSq = dot(toCam, toCam);
|
|
||||||
if (distSq > cameraPos.w) {
|
|
||||||
visibility[id] = 0u;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accurate per-instance distance cull
|
|
||||||
if (distSq > inst.effectiveMaxDistSq) {
|
|
||||||
visibility[id] = 0u;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frustum cull: sphere vs 6 planes (current frame)
|
|
||||||
float radius = inst.sphere.w;
|
|
||||||
if (radius > 0.0) {
|
|
||||||
for (int i = 0; i < 6; i++) {
|
|
||||||
float d = dot(frustumPlanes[i].xyz, inst.sphere.xyz) + frustumPlanes[i].w;
|
|
||||||
if (d < -radius) {
|
|
||||||
visibility[id] = 0u;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- HiZ Occlusion Test ---
|
|
||||||
// Skip for objects not rendered last frame (bit 3 = previouslyVisible).
|
|
||||||
bool previouslyVisible = (f & 8u) != 0u;
|
|
||||||
|
|
||||||
if (hizEnabled != 0u && radius > 0.0 && previouslyVisible) {
|
|
||||||
// Inflate sphere for conservative screen-space AABB
|
|
||||||
float hizRadius = radius * HIZ_SPHERE_INFLATE;
|
|
||||||
|
|
||||||
// Project sphere center into previous frame's clip space
|
|
||||||
vec4 clipCenter = prevViewProj * vec4(inst.sphere.xyz, 1.0);
|
|
||||||
if (clipCenter.w > 0.0) {
|
|
||||||
vec3 ndc = clipCenter.xyz / clipCenter.w;
|
|
||||||
|
|
||||||
// --- Correct sphere → screen AABB using VP row-vector lengths ---
|
|
||||||
// The maximum screen-space extent of a world-space sphere is
|
|
||||||
// maxDeltaNdcX = R * ‖row_x(VP)‖ / w
|
|
||||||
// where row_x = (VP[0][0], VP[1][0], VP[2][0]) maps world XYZ
|
|
||||||
// offsets to clip-X. Using only the diagonal element (VP[0][0])
|
|
||||||
// underestimates the footprint when the camera is rotated,
|
|
||||||
// causing false culls at certain view angles.
|
|
||||||
float rowLenX = length(vec3(prevViewProj[0][0],
|
|
||||||
prevViewProj[1][0],
|
|
||||||
prevViewProj[2][0]));
|
|
||||||
float rowLenY = length(vec3(prevViewProj[0][1],
|
|
||||||
prevViewProj[1][1],
|
|
||||||
prevViewProj[2][1]));
|
|
||||||
float projRadX = hizRadius * rowLenX / clipCenter.w;
|
|
||||||
float projRadY = hizRadius * rowLenY / clipCenter.w;
|
|
||||||
float projRad = max(projRadX, projRadY);
|
|
||||||
|
|
||||||
vec2 uvCenter = ndc.xy * 0.5 + 0.5;
|
|
||||||
float uvRad = projRad * 0.5;
|
|
||||||
vec2 uvMin = uvCenter - uvRad;
|
|
||||||
vec2 uvMax = uvCenter + uvRad;
|
|
||||||
|
|
||||||
// **Screen-edge guard**: skip if AABB extends outside safe area.
|
|
||||||
// Depth data at borders is from unrelated geometry.
|
|
||||||
if (uvMin.x >= SCREEN_EDGE_MARGIN && uvMin.y >= SCREEN_EDGE_MARGIN &&
|
|
||||||
uvMax.x <= (1.0 - SCREEN_EDGE_MARGIN) && uvMax.y <= (1.0 - SCREEN_EDGE_MARGIN) &&
|
|
||||||
uvMax.x > uvMin.x && uvMax.y > uvMin.y)
|
|
||||||
{
|
|
||||||
float aabbW = (uvMax.x - uvMin.x) * hizParams.x;
|
|
||||||
float aabbH = (uvMax.y - uvMin.y) * hizParams.y;
|
|
||||||
float screenSize = max(aabbW, aabbH);
|
|
||||||
|
|
||||||
if (screenSize >= HIZ_MIN_SCREEN_PX) {
|
|
||||||
// Mip level: +1 for conservatism (coarser = bigger depth footprint)
|
|
||||||
float mipLevel = ceil(log2(max(screenSize, 1.0))) + 1.0;
|
|
||||||
mipLevel = clamp(mipLevel, 0.0, float(hizMipLevels - 1u));
|
|
||||||
|
|
||||||
// Sample HiZ at 4 corners — take MAX (farthest occluder)
|
|
||||||
float pz0 = textureLod(hizPyramid, uvMin, mipLevel).r;
|
|
||||||
float pz1 = textureLod(hizPyramid, vec2(uvMax.x, uvMin.y), mipLevel).r;
|
|
||||||
float pz2 = textureLod(hizPyramid, vec2(uvMin.x, uvMax.y), mipLevel).r;
|
|
||||||
float pz3 = textureLod(hizPyramid, uvMax, mipLevel).r;
|
|
||||||
float pyramidDepth = max(max(pz0, pz1), max(pz2, pz3));
|
|
||||||
|
|
||||||
// Nearest depth: project sphere center's NDC-Z then subtract
|
|
||||||
// the sphere's depth range. The depth span uses the Z-row
|
|
||||||
// length of VP (same Cauchy-Schwarz reasoning as X/Y), giving
|
|
||||||
// the correct NDC-Z extent regardless of camera orientation.
|
|
||||||
float rowLenZ = length(vec3(prevViewProj[0][2],
|
|
||||||
prevViewProj[1][2],
|
|
||||||
prevViewProj[2][2]));
|
|
||||||
float depthSpan = hizRadius * rowLenZ / clipCenter.w;
|
|
||||||
float centerDepth = ndc.z;
|
|
||||||
float nearestDepth = centerDepth - depthSpan - HIZ_DEPTH_BIAS;
|
|
||||||
|
|
||||||
if (nearestDepth > pyramidDepth && pyramidDepth < 1.0) {
|
|
||||||
visibility[id] = 0u;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// fallthrough: conservatively visible
|
|
||||||
}
|
|
||||||
|
|
||||||
visibility[id] = 1u;
|
|
||||||
}
|
|
||||||
|
|
@ -25,9 +25,6 @@ void main() {
|
||||||
if (lum < 0.05) discard;
|
if (lum < 0.05) discard;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Soft circular falloff for point-sprite edges.
|
float edge = smoothstep(0.5, 0.4, length(p - 0.5));
|
||||||
float edge = 1.0 - smoothstep(0.4, 0.5, length(p - 0.5));
|
outColor = texColor * vColor * vec4(vec3(1.0), edge);
|
||||||
float alpha = texColor.a * vColor.a * edge;
|
|
||||||
vec3 rgb = texColor.rgb * vColor.rgb * alpha;
|
|
||||||
outColor = vec4(rgb, alpha);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// M2 ribbon emitter fragment shader.
|
|
||||||
// Samples the ribbon texture, multiplied by vertex color and alpha.
|
|
||||||
// Uses additive blending (pipeline-level) for magic/spell trails.
|
|
||||||
|
|
||||||
layout(set = 1, binding = 0) uniform sampler2D uTexture;
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 vColor;
|
|
||||||
layout(location = 1) in float vAlpha;
|
|
||||||
layout(location = 2) in vec2 vUV;
|
|
||||||
layout(location = 3) in float vFogFactor;
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 outColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 tex = texture(uTexture, vUV);
|
|
||||||
// For additive ribbons alpha comes from texture luminance; multiply by vertex alpha.
|
|
||||||
float a = tex.a * vAlpha;
|
|
||||||
if (a < 0.01) discard;
|
|
||||||
vec3 rgb = tex.rgb * vColor;
|
|
||||||
// Ribbons fade slightly with fog (additive blend attenuated toward black = invisible in fog).
|
|
||||||
rgb *= vFogFactor;
|
|
||||||
outColor = vec4(rgb, a);
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
|
@ -1,43 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
// M2 ribbon emitter vertex shader.
|
|
||||||
// Ribbon geometry is generated CPU-side as a triangle strip.
|
|
||||||
// Vertex format: pos(3) + color(3) + alpha(1) + uv(2) = 9 floats.
|
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform PerFrame {
|
|
||||||
mat4 view;
|
|
||||||
mat4 projection;
|
|
||||||
mat4 lightSpaceMatrix;
|
|
||||||
vec4 lightDir;
|
|
||||||
vec4 lightColor;
|
|
||||||
vec4 ambientColor;
|
|
||||||
vec4 viewPos;
|
|
||||||
vec4 fogColor;
|
|
||||||
vec4 fogParams;
|
|
||||||
vec4 shadowParams;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 aPos;
|
|
||||||
layout(location = 1) in vec3 aColor;
|
|
||||||
layout(location = 2) in float aAlpha;
|
|
||||||
layout(location = 3) in vec2 aUV;
|
|
||||||
|
|
||||||
layout(location = 0) out vec3 vColor;
|
|
||||||
layout(location = 1) out float vAlpha;
|
|
||||||
layout(location = 2) out vec2 vUV;
|
|
||||||
layout(location = 3) out float vFogFactor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 worldPos = vec4(aPos, 1.0);
|
|
||||||
vec4 viewPos4 = view * worldPos;
|
|
||||||
gl_Position = projection * viewPos4;
|
|
||||||
|
|
||||||
float dist = length(viewPos4.xyz);
|
|
||||||
float fogStart = fogParams.x;
|
|
||||||
float fogEnd = fogParams.y;
|
|
||||||
vFogFactor = clamp((fogEnd - dist) / max(fogEnd - fogStart, 0.001), 0.0, 1.0);
|
|
||||||
|
|
||||||
vColor = aColor;
|
|
||||||
vAlpha = aAlpha;
|
|
||||||
vUV = aUV;
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
|
@ -40,27 +40,23 @@ void main() {
|
||||||
float cs = cos(push.rotation);
|
float cs = cos(push.rotation);
|
||||||
float sn = sin(push.rotation);
|
float sn = sin(push.rotation);
|
||||||
vec2 rotated = vec2(center.x * cs - center.y * sn, center.x * sn + center.y * cs);
|
vec2 rotated = vec2(center.x * cs - center.y * sn, center.x * sn + center.y * cs);
|
||||||
vec2 mapUV = push.playerUV + vec2(rotated.x, rotated.y) * push.zoomRadius * 2.0;
|
vec2 mapUV = push.playerUV + vec2(-rotated.x, rotated.y) * push.zoomRadius * 2.0;
|
||||||
|
|
||||||
vec4 mapColor = texture(uComposite, mapUV);
|
vec4 mapColor = texture(uComposite, mapUV);
|
||||||
|
|
||||||
// Single player direction indicator (center arrow) rendered in-shader.
|
// Player arrow
|
||||||
vec2 local = center; // [-0.5, 0.5] around minimap center
|
float acs = cos(push.arrowRotation);
|
||||||
float ac = cos(push.arrowRotation);
|
float asn = sin(push.arrowRotation);
|
||||||
float as = sin(push.arrowRotation);
|
vec2 ac = center;
|
||||||
// TexCoord Y grows downward on screen; use negative Y so 0-angle points North (up).
|
vec2 arrowPos = vec2(-(ac.x * acs - ac.y * asn), ac.x * asn + ac.y * acs);
|
||||||
vec2 tip = vec2(0.0, -0.09);
|
|
||||||
vec2 left = vec2(-0.045, 0.02);
|
vec2 tip = vec2(0.0, -0.04);
|
||||||
vec2 right = vec2( 0.045, 0.02);
|
vec2 left = vec2(-0.02, 0.02);
|
||||||
mat2 rot = mat2(ac, -as, as, ac);
|
vec2 right = vec2(0.02, 0.02);
|
||||||
tip = rot * tip;
|
|
||||||
left = rot * left;
|
if (pointInTriangle(arrowPos, tip, left, right)) {
|
||||||
right = rot * right;
|
mapColor = vec4(1.0, 0.8, 0.0, 1.0);
|
||||||
if (pointInTriangle(local, tip, left, right)) {
|
|
||||||
mapColor.rgb = vec3(1.0, 0.86, 0.05);
|
|
||||||
}
|
}
|
||||||
float centerDot = smoothstep(0.016, 0.0, length(local));
|
|
||||||
mapColor.rgb = mix(mapColor.rgb, vec3(1.0), centerDot * 0.95);
|
|
||||||
|
|
||||||
// Dark border ring
|
// Dark border ring
|
||||||
float border = smoothstep(0.48, 0.5, dist);
|
float border = smoothstep(0.48, 0.5, dist);
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -6,7 +6,5 @@ void main() {
|
||||||
// Fullscreen triangle trick: 3 vertices, no vertex buffer
|
// Fullscreen triangle trick: 3 vertices, no vertex buffer
|
||||||
TexCoord = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
|
TexCoord = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
|
||||||
gl_Position = vec4(TexCoord * 2.0 - 1.0, 0.0, 1.0);
|
gl_Position = vec4(TexCoord * 2.0 - 1.0, 0.0, 1.0);
|
||||||
// No Y-flip: scene textures use Vulkan convention (v=0 at top),
|
TexCoord.y = 1.0 - TexCoord.y; // flip Y for Vulkan
|
||||||
// and NDC y=-1 already maps to framebuffer top, so the triangle
|
|
||||||
// naturally samples the correct row without any inversion.
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
146
assets/shaders/terrain.frag
Normal file
146
assets/shaders/terrain.frag
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
in vec3 FragPos;
|
||||||
|
in vec3 Normal;
|
||||||
|
in vec2 TexCoord;
|
||||||
|
in vec2 LayerUV;
|
||||||
|
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
// Texture layers (up to 4)
|
||||||
|
uniform sampler2D uBaseTexture;
|
||||||
|
uniform sampler2D uLayer1Texture;
|
||||||
|
uniform sampler2D uLayer2Texture;
|
||||||
|
uniform sampler2D uLayer3Texture;
|
||||||
|
|
||||||
|
// Alpha maps for blending
|
||||||
|
uniform sampler2D uLayer1Alpha;
|
||||||
|
uniform sampler2D uLayer2Alpha;
|
||||||
|
uniform sampler2D uLayer3Alpha;
|
||||||
|
|
||||||
|
// Layer control
|
||||||
|
uniform int uLayerCount;
|
||||||
|
uniform bool uHasLayer1;
|
||||||
|
uniform bool uHasLayer2;
|
||||||
|
uniform bool uHasLayer3;
|
||||||
|
|
||||||
|
// Lighting
|
||||||
|
uniform vec3 uLightDir;
|
||||||
|
uniform vec3 uLightColor;
|
||||||
|
uniform vec3 uAmbientColor;
|
||||||
|
|
||||||
|
// Camera
|
||||||
|
uniform vec3 uViewPos;
|
||||||
|
|
||||||
|
// Fog
|
||||||
|
uniform vec3 uFogColor;
|
||||||
|
uniform float uFogStart;
|
||||||
|
uniform float uFogEnd;
|
||||||
|
|
||||||
|
// Shadow mapping
|
||||||
|
uniform sampler2DShadow uShadowMap;
|
||||||
|
uniform mat4 uLightSpaceMatrix;
|
||||||
|
uniform bool uShadowEnabled;
|
||||||
|
uniform float uShadowStrength;
|
||||||
|
|
||||||
|
float calcShadow() {
|
||||||
|
vec4 lsPos = uLightSpaceMatrix * vec4(FragPos, 1.0);
|
||||||
|
vec3 proj = lsPos.xyz / lsPos.w * 0.5 + 0.5;
|
||||||
|
if (proj.z > 1.0) return 1.0;
|
||||||
|
float edgeDist = max(abs(proj.x - 0.5), abs(proj.y - 0.5));
|
||||||
|
float coverageFade = 1.0 - smoothstep(0.40, 0.49, edgeDist);
|
||||||
|
vec3 norm = normalize(Normal);
|
||||||
|
vec3 lightDir = normalize(-uLightDir);
|
||||||
|
float bias = max(0.005 * (1.0 - dot(norm, lightDir)), 0.001);
|
||||||
|
// 5-tap PCF tuned for slightly sharper detail while keeping stability.
|
||||||
|
vec2 texel = vec2(1.0 / 2048.0);
|
||||||
|
float ref = proj.z - bias;
|
||||||
|
vec2 off = texel * 0.7;
|
||||||
|
float shadow = 0.0;
|
||||||
|
shadow += texture(uShadowMap, vec3(proj.xy, ref)) * 0.55;
|
||||||
|
shadow += texture(uShadowMap, vec3(proj.xy + vec2(off.x, 0.0), ref)) * 0.1125;
|
||||||
|
shadow += texture(uShadowMap, vec3(proj.xy - vec2(off.x, 0.0), ref)) * 0.1125;
|
||||||
|
shadow += texture(uShadowMap, vec3(proj.xy + vec2(0.0, off.y), ref)) * 0.1125;
|
||||||
|
shadow += texture(uShadowMap, vec3(proj.xy - vec2(0.0, off.y), ref)) * 0.1125;
|
||||||
|
return mix(1.0, shadow, coverageFade);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sampleAlpha(sampler2D tex, vec2 uv) {
|
||||||
|
// Slight blur near alpha-map borders to hide seams between chunks.
|
||||||
|
vec2 edge = min(uv, 1.0 - uv);
|
||||||
|
float border = min(edge.x, edge.y);
|
||||||
|
float doBlur = step(border, 2.0 / 64.0); // within ~2 texels of edge
|
||||||
|
if (doBlur < 0.5) {
|
||||||
|
return texture(tex, uv).r;
|
||||||
|
}
|
||||||
|
vec2 texel = vec2(1.0 / 64.0);
|
||||||
|
float a = 0.0;
|
||||||
|
a += texture(tex, uv + vec2(-texel.x, 0.0)).r;
|
||||||
|
a += texture(tex, uv + vec2(texel.x, 0.0)).r;
|
||||||
|
a += texture(tex, uv + vec2(0.0, -texel.y)).r;
|
||||||
|
a += texture(tex, uv + vec2(0.0, texel.y)).r;
|
||||||
|
return a * 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Sample base texture
|
||||||
|
vec4 baseColor = texture(uBaseTexture, TexCoord);
|
||||||
|
vec4 finalColor = baseColor;
|
||||||
|
|
||||||
|
// Apply texture layers with alpha blending
|
||||||
|
// TexCoord = tiling UVs for texture sampling (repeats across chunk)
|
||||||
|
// LayerUV = 0-1 per-chunk UVs for alpha map sampling
|
||||||
|
float a1 = uHasLayer1 ? sampleAlpha(uLayer1Alpha, LayerUV) : 0.0;
|
||||||
|
float a2 = uHasLayer2 ? sampleAlpha(uLayer2Alpha, LayerUV) : 0.0;
|
||||||
|
float a3 = uHasLayer3 ? sampleAlpha(uLayer3Alpha, LayerUV) : 0.0;
|
||||||
|
|
||||||
|
// Normalize weights to reduce quilting seams at chunk borders.
|
||||||
|
float w0 = 1.0;
|
||||||
|
float w1 = a1;
|
||||||
|
float w2 = a2;
|
||||||
|
float w3 = a3;
|
||||||
|
float sum = w0 + w1 + w2 + w3;
|
||||||
|
if (sum > 0.0) {
|
||||||
|
w0 /= sum; w1 /= sum; w2 /= sum; w3 /= sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
finalColor = baseColor * w0;
|
||||||
|
if (uHasLayer1) {
|
||||||
|
vec4 layer1Color = texture(uLayer1Texture, TexCoord);
|
||||||
|
finalColor += layer1Color * w1;
|
||||||
|
}
|
||||||
|
if (uHasLayer2) {
|
||||||
|
vec4 layer2Color = texture(uLayer2Texture, TexCoord);
|
||||||
|
finalColor += layer2Color * w2;
|
||||||
|
}
|
||||||
|
if (uHasLayer3) {
|
||||||
|
vec4 layer3Color = texture(uLayer3Texture, TexCoord);
|
||||||
|
finalColor += layer3Color * w3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize normal
|
||||||
|
vec3 norm = normalize(Normal);
|
||||||
|
vec3 lightDir = normalize(-uLightDir);
|
||||||
|
|
||||||
|
// Ambient lighting
|
||||||
|
vec3 ambient = uAmbientColor * finalColor.rgb;
|
||||||
|
|
||||||
|
// Diffuse lighting (two-sided for terrain hills)
|
||||||
|
float diff = abs(dot(norm, lightDir));
|
||||||
|
diff = max(diff, 0.2); // Minimum light to prevent completely dark faces
|
||||||
|
vec3 diffuse = diff * uLightColor * finalColor.rgb;
|
||||||
|
|
||||||
|
// Shadow
|
||||||
|
float shadow = uShadowEnabled ? calcShadow() : 1.0;
|
||||||
|
shadow = mix(1.0, shadow, clamp(uShadowStrength, 0.0, 1.0));
|
||||||
|
|
||||||
|
// Combine lighting (terrain is purely diffuse — no specular on ground)
|
||||||
|
vec3 result = ambient + shadow * diffuse;
|
||||||
|
|
||||||
|
// Apply fog
|
||||||
|
float distance = length(uViewPos - FragPos);
|
||||||
|
float fogFactor = clamp((uFogEnd - distance) / (uFogEnd - uFogStart), 0.0, 1.0);
|
||||||
|
result = mix(uFogColor, result, fogFactor);
|
||||||
|
|
||||||
|
FragColor = vec4(result, 1.0);
|
||||||
|
}
|
||||||
|
|
@ -50,21 +50,19 @@ float sampleShadowPCF(sampler2DShadow smap, vec3 coords) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float sampleAlpha(sampler2D tex, vec2 uv) {
|
float sampleAlpha(sampler2D tex, vec2 uv) {
|
||||||
// Smooth 5-tap box near chunk edges to hide alpha-map seams;
|
|
||||||
// blends gradually to avoid a visible ring at the transition.
|
|
||||||
vec2 edge = min(uv, 1.0 - uv);
|
vec2 edge = min(uv, 1.0 - uv);
|
||||||
float border = min(edge.x, edge.y);
|
float border = min(edge.x, edge.y);
|
||||||
float blurWeight = 1.0 - smoothstep(0.5 / 64.0, 3.0 / 64.0, border);
|
float doBlur = step(border, 2.0 / 64.0);
|
||||||
float center = texture(tex, uv).r;
|
if (doBlur < 0.5) {
|
||||||
if (blurWeight < 0.001) return center;
|
return texture(tex, uv).r;
|
||||||
|
}
|
||||||
vec2 texel = vec2(1.0 / 64.0);
|
vec2 texel = vec2(1.0 / 64.0);
|
||||||
float avg = center;
|
float a = 0.0;
|
||||||
avg += texture(tex, uv + vec2(-texel.x, 0.0)).r;
|
a += texture(tex, uv + vec2(-texel.x, 0.0)).r;
|
||||||
avg += texture(tex, uv + vec2( texel.x, 0.0)).r;
|
a += texture(tex, uv + vec2(texel.x, 0.0)).r;
|
||||||
avg += texture(tex, uv + vec2(0.0, -texel.y)).r;
|
a += texture(tex, uv + vec2(0.0, -texel.y)).r;
|
||||||
avg += texture(tex, uv + vec2(0.0, texel.y)).r;
|
a += texture(tex, uv + vec2(0.0, texel.y)).r;
|
||||||
avg *= 0.2;
|
return a * 0.25;
|
||||||
return mix(center, avg, blurWeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|
@ -89,12 +87,9 @@ void main() {
|
||||||
vec3 norm = normalize(Normal);
|
vec3 norm = normalize(Normal);
|
||||||
|
|
||||||
// Derivative-based normal mapping: perturb vertex normal using texture detail.
|
// Derivative-based normal mapping: perturb vertex normal using texture detail.
|
||||||
// Fade out with distance and near chunk edges (dFdx/dFdy are invalid across
|
// Fade out with distance — looks noisy/harsh beyond ~100 units.
|
||||||
// chunk draw-call boundaries, producing visible seams if not faded).
|
|
||||||
float fragDist = length(viewPos.xyz - FragPos);
|
float fragDist = length(viewPos.xyz - FragPos);
|
||||||
float bumpFade = 1.0 - smoothstep(50.0, 125.0, fragDist);
|
float bumpFade = 1.0 - smoothstep(50.0, 125.0, fragDist);
|
||||||
float edgeDist = min(min(LayerUV.x, 1.0 - LayerUV.x), min(LayerUV.y, 1.0 - LayerUV.y));
|
|
||||||
bumpFade *= smoothstep(0.0, 0.06, edgeDist);
|
|
||||||
if (bumpFade > 0.001) {
|
if (bumpFade > 0.001) {
|
||||||
float lum = dot(finalColor.rgb, vec3(0.299, 0.587, 0.114));
|
float lum = dot(finalColor.rgb, vec3(0.299, 0.587, 0.114));
|
||||||
float dLdx = dFdx(lum);
|
float dLdx = dFdx(lum);
|
||||||
|
|
@ -121,8 +116,8 @@ void main() {
|
||||||
vec4 lsPos = lightSpaceMatrix * vec4(biasedPos, 1.0);
|
vec4 lsPos = lightSpaceMatrix * vec4(biasedPos, 1.0);
|
||||||
vec3 proj = lsPos.xyz / lsPos.w;
|
vec3 proj = lsPos.xyz / lsPos.w;
|
||||||
proj.xy = proj.xy * 0.5 + 0.5;
|
proj.xy = proj.xy * 0.5 + 0.5;
|
||||||
if (proj.x >= 0.0 && proj.x <= 1.0 && proj.y >= 0.0 && proj.y <= 1.0 && proj.z >= 0.0 && proj.z <= 1.0) {
|
if (proj.x >= 0.0 && proj.x <= 1.0 && proj.y >= 0.0 && proj.y <= 1.0 && proj.z <= 1.0) {
|
||||||
float bias = max(0.0005 * (1.0 - abs(dot(norm, ldir))), 0.00005);
|
float bias = 0.0002;
|
||||||
shadow = sampleShadowPCF(uShadowMap, vec3(proj.xy, proj.z - bias));
|
shadow = sampleShadowPCF(uShadowMap, vec3(proj.xy, proj.z - bias));
|
||||||
shadow = mix(1.0, shadow, shadowParams.y);
|
shadow = mix(1.0, shadow, shadowParams.y);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
assets/shaders/terrain.vert
Normal file
28
assets/shaders/terrain.vert
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#version 330 core
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 aPosition;
|
||||||
|
layout(location = 1) in vec3 aNormal;
|
||||||
|
layout(location = 2) in vec2 aTexCoord;
|
||||||
|
layout(location = 3) in vec2 aLayerUV;
|
||||||
|
|
||||||
|
out vec3 FragPos;
|
||||||
|
out vec3 Normal;
|
||||||
|
out vec2 TexCoord;
|
||||||
|
out vec2 LayerUV;
|
||||||
|
|
||||||
|
uniform mat4 uModel;
|
||||||
|
uniform mat4 uView;
|
||||||
|
uniform mat4 uProjection;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 worldPos = uModel * vec4(aPosition, 1.0);
|
||||||
|
FragPos = worldPos.xyz;
|
||||||
|
|
||||||
|
// Terrain uses identity model matrix, so normal passes through directly
|
||||||
|
Normal = aNormal;
|
||||||
|
|
||||||
|
TexCoord = aTexCoord;
|
||||||
|
LayerUV = aLayerUV;
|
||||||
|
|
||||||
|
gl_Position = uProjection * uView * worldPos;
|
||||||
|
}
|
||||||
|
|
@ -47,16 +47,12 @@ layout(location = 0) out vec4 outColor;
|
||||||
// Dual-scroll detail normals (multi-octave ripple overlay)
|
// Dual-scroll detail normals (multi-octave ripple overlay)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
vec3 dualScrollWaveNormal(vec2 p, float time) {
|
vec3 dualScrollWaveNormal(vec2 p, float time) {
|
||||||
// Three wave octaves at different angles, frequencies, and speeds.
|
vec2 d1 = normalize(vec2(0.86, 0.51));
|
||||||
// Directions are non-axis-aligned to prevent visible tiling patterns.
|
vec2 d2 = normalize(vec2(-0.47, 0.88));
|
||||||
// Frequency increases and amplitude decreases per octave (standard
|
vec2 d3 = normalize(vec2(0.32, -0.95));
|
||||||
// multi-octave noise layering for natural water appearance).
|
float f1 = 0.19, f2 = 0.43, f3 = 0.72;
|
||||||
vec2 d1 = normalize(vec2(0.86, 0.51)); // ~30° from +X
|
float s1 = 0.95, s2 = 1.73, s3 = 2.40;
|
||||||
vec2 d2 = normalize(vec2(-0.47, 0.88)); // ~118° (opposing cross-wave)
|
float a1 = 0.22, a2 = 0.10, a3 = 0.05;
|
||||||
vec2 d3 = normalize(vec2(0.32, -0.95)); // ~-71° (third axis for variety)
|
|
||||||
float f1 = 0.19, f2 = 0.43, f3 = 0.72; // spatial frequency (higher = tighter ripples)
|
|
||||||
float s1 = 0.95, s2 = 1.73, s3 = 2.40; // scroll speed (higher octaves move faster)
|
|
||||||
float a1 = 0.22, a2 = 0.10, a3 = 0.05; // amplitude (decreasing for natural falloff)
|
|
||||||
|
|
||||||
vec2 p1 = p + d1 * (time * s1 * 4.0);
|
vec2 p1 = p + d1 * (time * s1 * 4.0);
|
||||||
vec2 p2 = p + d2 * (time * s2 * 4.0);
|
vec2 p2 = p + d2 * (time * s2 * 4.0);
|
||||||
|
|
|
||||||
|
|
@ -163,9 +163,8 @@ void main() {
|
||||||
|
|
||||||
vec3 result;
|
vec3 result;
|
||||||
|
|
||||||
// Sample shadow map for all groups. Interior groups receive attenuated
|
// Sample shadow map for all WMO groups (interior groups with 0x2000 flag
|
||||||
// shadow (30%) so they get subtle light/shadow variation without the full
|
// include covered outdoor areas like archways/streets that should receive shadows)
|
||||||
// outdoor darkening that makes them look wrong.
|
|
||||||
float shadow = 1.0;
|
float shadow = 1.0;
|
||||||
if (shadowParams.x > 0.5) {
|
if (shadowParams.x > 0.5) {
|
||||||
vec3 ldir = normalize(-lightDir.xyz);
|
vec3 ldir = normalize(-lightDir.xyz);
|
||||||
|
|
@ -177,7 +176,7 @@ void main() {
|
||||||
if (proj.x >= 0.0 && proj.x <= 1.0 &&
|
if (proj.x >= 0.0 && proj.x <= 1.0 &&
|
||||||
proj.y >= 0.0 && proj.y <= 1.0 &&
|
proj.y >= 0.0 && proj.y <= 1.0 &&
|
||||||
proj.z >= 0.0 && proj.z <= 1.0) {
|
proj.z >= 0.0 && proj.z <= 1.0) {
|
||||||
float bias = max(0.0005 * (1.0 - abs(dot(norm, ldir))), 0.00005);
|
float bias = max(0.0005 * (1.0 - dot(norm, ldir)), 0.00005);
|
||||||
shadow = sampleShadowPCF(uShadowMap, vec3(proj.xy, proj.z - bias));
|
shadow = sampleShadowPCF(uShadowMap, vec3(proj.xy, proj.z - bias));
|
||||||
}
|
}
|
||||||
shadow = mix(1.0, shadow, shadowParams.y);
|
shadow = mix(1.0, shadow, shadowParams.y);
|
||||||
|
|
@ -186,19 +185,17 @@ void main() {
|
||||||
if (isLava != 0) {
|
if (isLava != 0) {
|
||||||
// Lava is self-luminous — bright emissive, no shadows
|
// Lava is self-luminous — bright emissive, no shadows
|
||||||
result = texColor.rgb * 1.5;
|
result = texColor.rgb * 1.5;
|
||||||
|
} else if (unlit != 0) {
|
||||||
|
result = texColor.rgb * shadow;
|
||||||
} else if (isInterior != 0) {
|
} else if (isInterior != 0) {
|
||||||
// WMO interior: vertex colors (MOCV) are pre-baked lighting from the artist.
|
// WMO interior: vertex colors (MOCV) are pre-baked lighting from the artist.
|
||||||
// The MOHD ambient color floors the vertex colors so dark spots don't go
|
// The MOHD ambient color tints/floors the vertex colors so dark spots don't
|
||||||
// completely black. Full shadow strength is applied but clamped so
|
// go completely black, matching the WoW client's interior shading.
|
||||||
// interiors never go darker than a minimum brightness.
|
|
||||||
vec3 wmoAmbient = vec3(wmoAmbientR, wmoAmbientG, wmoAmbientB);
|
vec3 wmoAmbient = vec3(wmoAmbientR, wmoAmbientG, wmoAmbientB);
|
||||||
wmoAmbient = max(wmoAmbient, vec3(0.35));
|
// Clamp ambient to at least 0.3 to avoid total darkness when MOHD color is zero
|
||||||
|
wmoAmbient = max(wmoAmbient, vec3(0.3));
|
||||||
vec3 mocv = max(VertColor.rgb, wmoAmbient);
|
vec3 mocv = max(VertColor.rgb, wmoAmbient);
|
||||||
float clampedShadow = max(shadow, 0.45);
|
result = texColor.rgb * mocv * shadow;
|
||||||
result = texColor.rgb * mocv * clampedShadow;
|
|
||||||
} else if (unlit != 0) {
|
|
||||||
// Outdoor unlit surface — still receives directional shadows
|
|
||||||
result = texColor.rgb * shadow;
|
|
||||||
} else {
|
} else {
|
||||||
vec3 ldir = normalize(-lightDir.xyz);
|
vec3 ldir = normalize(-lightDir.xyz);
|
||||||
float diff = max(dot(norm, ldir), 0.0);
|
float diff = max(dot(norm, ldir), 0.0);
|
||||||
|
|
|
||||||
BIN
assets/shaders/wmo.frag.spv
Normal file
BIN
assets/shaders/wmo.frag.spv
Normal file
Binary file not shown.
|
|
@ -1,16 +0,0 @@
|
||||||
#version 450
|
|
||||||
|
|
||||||
layout(set = 0, binding = 0) uniform sampler2D uTileTexture;
|
|
||||||
|
|
||||||
layout(push_constant) uniform PushConstants {
|
|
||||||
layout(offset = 16) vec4 tintColor;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout(location = 0) in vec2 TexCoord;
|
|
||||||
|
|
||||||
layout(location = 0) out vec4 outColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
vec4 texel = texture(uTileTexture, TexCoord);
|
|
||||||
outColor = texel * tintColor;
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
# HD Texture Assets
|
|
||||||
|
|
||||||
**Source**: TurtleHD Texture Pack (Turtle WoW)
|
|
||||||
**Imported**: 2026-01-27
|
|
||||||
**Total Files**: 298 BLP textures
|
|
||||||
**Total Size**: 10MB
|
|
||||||
|
|
||||||
## Directory Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
textures/
|
|
||||||
├── character/
|
|
||||||
│ └── human/ # 274 human male textures
|
|
||||||
├── creature/ # 15 creature textures
|
|
||||||
├── item/ # (reserved for future)
|
|
||||||
└── world/
|
|
||||||
├── generic/ # 1 generic world texture
|
|
||||||
└── stormwind/ # 8 Stormwind building textures
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
These HD BLP textures are ready for integration with:
|
|
||||||
- **WMO Renderer**: Building texture mapping
|
|
||||||
- **Character Renderer**: M2 model skin/face textures
|
|
||||||
- **Creature Renderer**: NPC texture application
|
|
||||||
|
|
||||||
## Integration Status
|
|
||||||
|
|
||||||
Textures are loaded via the BLP pipeline and applied to WMO/M2 renderers.
|
|
||||||
HD texture overrides (e.g. TurtleHD packs) can be placed as PNG files
|
|
||||||
alongside the original BLP paths — the asset manager checks for `.png`
|
|
||||||
overrides before loading the `.blp` version.
|
|
||||||
|
|
@ -1,283 +0,0 @@
|
||||||
# Container Build Flow
|
|
||||||
|
|
||||||
Comprehensive documentation of the Docker-based build pipeline for each target platform.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Architecture Overview
|
|
||||||
|
|
||||||
Each platform follows the same two-phase pattern:
|
|
||||||
|
|
||||||
1. **Image Build** (one-time, cached by Docker) — installs compilers, toolchains, and pre-builds vcpkg dependencies.
|
|
||||||
2. **Container Run** (each build) — copies source into the container, runs CMake configure + build, outputs artifacts to the host.
|
|
||||||
|
|
||||||
```
|
|
||||||
Host Docker
|
|
||||||
─────────────────────────────────────────────────────────────
|
|
||||||
run-{platform}.sh/.ps1
|
|
||||||
│
|
|
||||||
├─ docker build builder-{platform}.Dockerfile
|
|
||||||
│ (cached after first run) ├─ install compilers
|
|
||||||
│ ├─ install vcpkg + packages
|
|
||||||
│ └─ COPY build-{platform}.sh
|
|
||||||
│
|
|
||||||
└─ docker run build-{platform}.sh (entrypoint)
|
|
||||||
├─ bind /src (readonly) ├─ tar copy source → /wowee-build-src
|
|
||||||
└─ bind /out (writable) ├─ git clone FidelityFX SDKs
|
|
||||||
├─ cmake -S . -B /out
|
|
||||||
├─ cmake --build /out
|
|
||||||
└─ artifacts appear in /out
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Linux Build Flow
|
|
||||||
|
|
||||||
**Image:** `wowee-builder-linux`
|
|
||||||
**Dockerfile:** `builder-linux.Dockerfile`
|
|
||||||
**Toolchain:** GCC + Ninja (native amd64)
|
|
||||||
**Base:** Ubuntu 24.04
|
|
||||||
|
|
||||||
### Docker Image Build Steps
|
|
||||||
|
|
||||||
| Step | What | Why |
|
|
||||||
|------|------|-----|
|
|
||||||
| 1 | `apt-get install` cmake, ninja-build, build-essential, pkg-config, git, python3 | Core build tools |
|
|
||||||
| 2 | `apt-get install` glslang-tools, spirv-tools | Vulkan shader compilation |
|
|
||||||
| 3 | `apt-get install` libsdl2-dev, libglew-dev, libglm-dev, libssl-dev, zlib1g-dev | Runtime dependencies (system packages) |
|
|
||||||
| 4 | `apt-get install` libavformat-dev, libavcodec-dev, libswscale-dev, libavutil-dev | FFmpeg libraries |
|
|
||||||
| 5 | `apt-get install` libvulkan-dev, vulkan-tools | Vulkan SDK |
|
|
||||||
| 6 | `apt-get install` libstorm-dev, libunicorn-dev | MPQ archive + CPU emulation |
|
|
||||||
| 7 | COPY `build-linux.sh` → `/build-platform.sh` | Container entrypoint |
|
|
||||||
|
|
||||||
### Container Run Steps (build-linux.sh)
|
|
||||||
|
|
||||||
```
|
|
||||||
1. tar copy /src → /wowee-build-src (excludes build/, .git/, large Data/ dirs)
|
|
||||||
2. git clone FidelityFX-FSR2 (if missing)
|
|
||||||
3. git clone FidelityFX-SDK (if missing)
|
|
||||||
4. cmake configure:
|
|
||||||
-G Ninja
|
|
||||||
-DCMAKE_BUILD_TYPE=Release
|
|
||||||
-DCMAKE_C_COMPILER=gcc
|
|
||||||
-DCMAKE_CXX_COMPILER=g++
|
|
||||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON
|
|
||||||
5. cmake --build (parallel)
|
|
||||||
6. Create Data symlink: build/linux/bin/Data → ../../../Data
|
|
||||||
```
|
|
||||||
|
|
||||||
### Output
|
|
||||||
- `build/linux/bin/wowee` — ELF 64-bit x86-64 executable
|
|
||||||
- `build/linux/bin/Data` — symlink to project Data/ directory
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## macOS Build Flow
|
|
||||||
|
|
||||||
**Image:** `wowee-builder-macos`
|
|
||||||
**Dockerfile:** `builder-macos.Dockerfile` (multi-stage)
|
|
||||||
**Toolchain:** osxcross (Clang 18 + Apple ld64)
|
|
||||||
**Base:** Ubuntu 24.04
|
|
||||||
**Targets:** arm64-apple-darwin24.5 (default), x86_64-apple-darwin24.5
|
|
||||||
|
|
||||||
### Docker Image Build — Stage 1: SDK Fetcher
|
|
||||||
|
|
||||||
The macOS SDK is fetched automatically from Apple's public software update catalog.
|
|
||||||
No manual download required.
|
|
||||||
|
|
||||||
| Step | What | Why |
|
|
||||||
|------|------|-----|
|
|
||||||
| 1 | `FROM ubuntu:24.04 AS sdk-fetcher` | Lightweight stage for SDK download |
|
|
||||||
| 2 | `apt-get install` ca-certificates, python3, cpio, tar, gzip, xz-utils | SDK extraction tools |
|
|
||||||
| 3 | COPY `macos/sdk-fetcher.py` → `/opt/sdk-fetcher.py` | Python script that scrapes Apple's SUCATALOG |
|
|
||||||
| 4 | `python3 /opt/sdk-fetcher.py /opt/sdk` | Downloads, extracts, and packages MacOSX15.5.sdk.tar.gz |
|
|
||||||
|
|
||||||
**SDK Fetcher internals** (`macos/sdk-fetcher.py`):
|
|
||||||
1. Queries Apple SUCATALOG URLs for the latest macOS package
|
|
||||||
2. Downloads the `CLTools_macOSNMOS_SDK.pkg` package
|
|
||||||
3. Extracts the XAR archive (using `bsdtar` or pure-Python fallback)
|
|
||||||
4. Decompresses the PBZX payload stream
|
|
||||||
5. Extracts via `cpio` to get the SDK directory
|
|
||||||
6. Packages as `MacOSX<version>.sdk.tar.gz`
|
|
||||||
|
|
||||||
### Docker Image Build — Stage 2: Builder
|
|
||||||
|
|
||||||
| Step | What | Why |
|
|
||||||
|------|------|-----|
|
|
||||||
| 1 | `FROM ubuntu:24.04 AS builder` | Full build environment |
|
|
||||||
| 2 | `apt-get install` cmake, ninja-build, git, python3, curl, wget, xz-utils, zip, unzip, tar, make, patch, libssl-dev, zlib1g-dev, pkg-config, libbz2-dev, libxml2-dev, uuid-dev | Build tools + osxcross build deps |
|
|
||||||
| 3 | Install Clang 18 from LLVM apt repo (`llvm-toolchain-jammy-18`) | Cross-compiler backend |
|
|
||||||
| 4 | Symlink clang-18 → clang, clang++-18 → clang++, etc. | osxcross expects unversioned names |
|
|
||||||
| 5 | `git clone osxcross` → `/opt/osxcross` | Apple cross-compile toolchain wrapper |
|
|
||||||
| 6 | `COPY --from=sdk-fetcher /opt/sdk/ → /opt/osxcross/tarballs/` | SDK from stage 1 |
|
|
||||||
| 7 | `UNATTENDED=1 ./build.sh` | Builds osxcross (LLVM wrappers + cctools + ld64) |
|
|
||||||
| 8 | Create unprefixed symlinks (install_name_tool, otool, lipo, codesign) | vcpkg/CMake need these without arch prefix |
|
|
||||||
| 9 | COPY `macos/osxcross-toolchain.cmake` → `/opt/osxcross-toolchain.cmake` | Auto-detecting CMake toolchain |
|
|
||||||
| 10 | COPY `macos/triplets/` → `/opt/vcpkg-triplets/` | vcpkg cross-compile triplet definitions |
|
|
||||||
| 11 | `apt-get install` file, nasm | Mach-O detection + ffmpeg x86 asm |
|
|
||||||
| 12 | Bootstrap vcpkg → `/opt/vcpkg` | Package manager |
|
|
||||||
| 13 | `vcpkg install` sdl2, openssl, glew, glm, zlib, ffmpeg `--triplet arm64-osx-cross` | arm64 dependencies |
|
|
||||||
| 14 | `vcpkg install` same packages `--triplet x64-osx-cross` | x86_64 dependencies |
|
|
||||||
| 15 | `apt-get install` libvulkan-dev, glslang-tools | Vulkan headers (for compilation, not runtime) |
|
|
||||||
| 16 | COPY `build-macos.sh` → `/build-platform.sh` | Container entrypoint |
|
|
||||||
|
|
||||||
### Custom Toolchain Files
|
|
||||||
|
|
||||||
**`macos/osxcross-toolchain.cmake`** — Auto-detecting CMake toolchain:
|
|
||||||
- Detects SDK path via `file(GLOB)` in `/opt/osxcross/target/SDK/MacOSX*.sdk`
|
|
||||||
- Detects darwin version from compiler binary names (e.g., `arm64-apple-darwin24.5-clang`)
|
|
||||||
- Picks architecture from `CMAKE_OSX_ARCHITECTURES`
|
|
||||||
- Sets `CMAKE_C_COMPILER`, `CMAKE_CXX_COMPILER`, `CMAKE_AR`, `CMAKE_RANLIB`, `CMAKE_STRIP`
|
|
||||||
|
|
||||||
**`macos/triplets/arm64-osx-cross.cmake`**:
|
|
||||||
```cmake
|
|
||||||
set(VCPKG_TARGET_ARCHITECTURE arm64)
|
|
||||||
set(VCPKG_LIBRARY_LINKAGE static)
|
|
||||||
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
|
|
||||||
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE /opt/osxcross-toolchain.cmake)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Container Run Steps (build-macos.sh)
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Determine arch from MACOS_ARCH env (default: arm64)
|
|
||||||
2. Pick vcpkg triplet: arm64-osx-cross or x64-osx-cross
|
|
||||||
3. Auto-detect darwin target from osxcross binaries
|
|
||||||
4. tar copy /src → /wowee-build-src
|
|
||||||
5. git clone FidelityFX-FSR2 + FidelityFX-SDK (if missing)
|
|
||||||
6. cmake configure:
|
|
||||||
-G Ninja
|
|
||||||
-DCMAKE_BUILD_TYPE=Release
|
|
||||||
-DCMAKE_SYSTEM_NAME=Darwin
|
|
||||||
-DCMAKE_OSX_ARCHITECTURES=${ARCH}
|
|
||||||
-DCMAKE_C_COMPILER=osxcross clang
|
|
||||||
-DCMAKE_CXX_COMPILER=osxcross clang++
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE=vcpkg.cmake
|
|
||||||
-DVCPKG_TARGET_TRIPLET=arm64-osx-cross
|
|
||||||
-DVCPKG_OVERLAY_TRIPLETS=/opt/vcpkg-triplets
|
|
||||||
7. cmake --build (parallel)
|
|
||||||
```
|
|
||||||
|
|
||||||
### CMakeLists.txt Integration
|
|
||||||
|
|
||||||
The main CMakeLists.txt has a macOS cross-compile branch that:
|
|
||||||
- Finds Vulkan headers via vcpkg (`VulkanHeaders` package) instead of the Vulkan SDK
|
|
||||||
- Adds `-undefined dynamic_lookup` linker flag for Vulkan loader symbols (resolved at runtime via MoltenVK)
|
|
||||||
|
|
||||||
### Output
|
|
||||||
- `build/macos/bin/wowee` — Mach-O 64-bit arm64 (or x86_64) executable (~40 MB)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Windows Build Flow
|
|
||||||
|
|
||||||
**Image:** `wowee-builder-windows`
|
|
||||||
**Dockerfile:** `builder-windows.Dockerfile`
|
|
||||||
**Toolchain:** LLVM-MinGW (Clang + LLD) targeting x86_64-w64-mingw32-ucrt
|
|
||||||
**Base:** Ubuntu 24.04
|
|
||||||
|
|
||||||
### Docker Image Build Steps
|
|
||||||
|
|
||||||
| Step | What | Why |
|
|
||||||
|------|------|-----|
|
|
||||||
| 1 | `apt-get install` ca-certificates, build-essential, cmake, ninja-build, git, python3, curl, zip, unzip, tar, xz-utils, pkg-config, nasm, libssl-dev, zlib1g-dev | Build tools |
|
|
||||||
| 2 | Download + extract LLVM-MinGW (v20240619 ucrt) → `/opt/llvm-mingw` | Clang/LLD cross-compiler for Windows |
|
|
||||||
| 3 | Add `/opt/llvm-mingw/bin` to PATH | Makes `x86_64-w64-mingw32-clang` etc. available |
|
|
||||||
| 4 | Bootstrap vcpkg → `/opt/vcpkg` | Package manager |
|
|
||||||
| 5 | `vcpkg install` sdl2, openssl, glew, glm, zlib, ffmpeg `--triplet x64-mingw-static` | Static Windows dependencies |
|
|
||||||
| 6 | `apt-get install` libvulkan-dev, glslang-tools | Vulkan headers + shader tools |
|
|
||||||
| 7 | Create no-op `powershell.exe` stub | vcpkg MinGW post-build hook needs it |
|
|
||||||
| 8 | COPY `build-windows.sh` → `/build-platform.sh` | Container entrypoint |
|
|
||||||
|
|
||||||
### Container Run Steps (build-windows.sh)
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Set up no-op powershell.exe (if not already present)
|
|
||||||
2. tar copy /src → /wowee-build-src
|
|
||||||
3. git clone FidelityFX-FSR2 + FidelityFX-SDK (if missing)
|
|
||||||
4. Generate Vulkan import library:
|
|
||||||
a. Extract vk* symbols from vulkan_core.h
|
|
||||||
b. Create vulkan-1.def file
|
|
||||||
c. Run dlltool to create libvulkan-1.a
|
|
||||||
5. Lock PKG_CONFIG_LIBDIR to vcpkg packages only
|
|
||||||
6. cmake configure:
|
|
||||||
-G Ninja
|
|
||||||
-DCMAKE_BUILD_TYPE=Release
|
|
||||||
-DCMAKE_SYSTEM_NAME=Windows
|
|
||||||
-DCMAKE_C_COMPILER=x86_64-w64-mingw32-clang
|
|
||||||
-DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-clang++
|
|
||||||
-DCMAKE_RC_COMPILER=x86_64-w64-mingw32-windres
|
|
||||||
-DCMAKE_EXE_LINKER_FLAGS=-fuse-ld=lld
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE=vcpkg.cmake
|
|
||||||
-DVCPKG_TARGET_TRIPLET=x64-mingw-static
|
|
||||||
-DVCPKG_APPLOCAL_DEPS=OFF
|
|
||||||
7. cmake --build (parallel)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Vulkan Import Library Generation
|
|
||||||
|
|
||||||
Windows applications link against `vulkan-1.dll` (the Khronos Vulkan loader). Since the LLVM-MinGW toolchain doesn't ship a Vulkan import library, the build script generates one:
|
|
||||||
|
|
||||||
1. Parses `vulkan_core.h` for `VKAPI_CALL vk*` function names
|
|
||||||
2. Creates a `.def` file mapping symbols to `vulkan-1.dll`
|
|
||||||
3. Uses `dlltool` to produce `libvulkan-1.a` (PE import library)
|
|
||||||
|
|
||||||
This allows the linker to resolve Vulkan symbols at build time, while deferring actual loading to the runtime DLL.
|
|
||||||
|
|
||||||
### Output
|
|
||||||
- `build/windows/bin/wowee.exe` — PE32+ x86-64 executable (~135 MB)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Shared Patterns
|
|
||||||
|
|
||||||
### Source Tree Copy
|
|
||||||
|
|
||||||
All three platforms use the same tar-based copy with exclusions:
|
|
||||||
```bash
|
|
||||||
tar -C /src \
|
|
||||||
--exclude='./build' --exclude='./logs' --exclude='./cache' \
|
|
||||||
--exclude='./container' --exclude='./.git' \
|
|
||||||
--exclude='./Data/character' --exclude='./Data/creature' \
|
|
||||||
--exclude='./Data/db' --exclude='./Data/environment' \
|
|
||||||
--exclude='./Data/interface' --exclude='./Data/item' \
|
|
||||||
--exclude='./Data/misc' --exclude='./Data/sound' \
|
|
||||||
--exclude='./Data/spell' --exclude='./Data/terrain' \
|
|
||||||
--exclude='./Data/world' \
|
|
||||||
-cf - . | tar -C /wowee-build-src -xf -
|
|
||||||
```
|
|
||||||
|
|
||||||
**Kept:** `Data/opcodes/`, `Data/expansions/` (small, needed at build time for configuration).
|
|
||||||
**Excluded:** Large game asset directories (character, creature, environment, etc.) not needed for compilation.
|
|
||||||
|
|
||||||
### FidelityFX SDK Fetch
|
|
||||||
|
|
||||||
All platforms clone the same two repos at build time:
|
|
||||||
1. **FidelityFX-FSR2** — FSR 2.0 upscaling
|
|
||||||
2. **FidelityFX-SDK** — FSR 3.0 frame generation (repo URL/ref configurable via env vars)
|
|
||||||
|
|
||||||
### .dockerignore
|
|
||||||
|
|
||||||
The `.dockerignore` at the project root minimizes the Docker build context by excluding:
|
|
||||||
- `build/`, `cache/`, `logs/`, `.git/`
|
|
||||||
- Large external dirs (`extern/FidelityFX-*`)
|
|
||||||
- IDE files, documentation, host-only scripts
|
|
||||||
- SDK tarballs (`*.tar.xz`, `*.tar.gz`, etc.)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Timing Estimates
|
|
||||||
|
|
||||||
These are approximate times on a 4-core machine with 16 GB RAM:
|
|
||||||
|
|
||||||
| Phase | Linux | macOS | Windows |
|
|
||||||
|-------|-------|-------|---------|
|
|
||||||
| Docker image build (first time) | ~5 min | ~25 min | ~15 min |
|
|
||||||
| Docker image build (cached) | seconds | seconds | seconds |
|
|
||||||
| Source copy + SDK fetch | ~10 sec | ~10 sec | ~10 sec |
|
|
||||||
| CMake configure | ~20 sec | ~30 sec | ~30 sec |
|
|
||||||
| Compilation | ~8 min | ~8 min | ~8 min |
|
|
||||||
| **Total (first build)** | **~14 min** | **~34 min** | **~24 min** |
|
|
||||||
| **Total (subsequent)** | **~9 min** | **~9 min** | **~9 min** |
|
|
||||||
|
|
||||||
macOS image is slowest because osxcross builds a subset of LLVM + cctools, and vcpkg packages are compiled for two architectures (arm64 + x64).
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
# Container Builds
|
|
||||||
|
|
||||||
Build WoWee for **Linux**, **macOS**, or **Windows** with a single command.
|
|
||||||
All builds run inside Docker — no toolchains to install on your host.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- [Docker](https://docs.docker.com/get-docker/) (Docker Desktop on Windows/macOS, or Docker Engine on Linux)
|
|
||||||
- ~20 GB free disk space (toolchains + vcpkg packages are cached in the Docker image)
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
Run **from the project root directory**.
|
|
||||||
|
|
||||||
### Linux (native amd64)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Bash / Linux / macOS terminal
|
|
||||||
./container/run-linux.sh
|
|
||||||
```
|
|
||||||
```powershell
|
|
||||||
# PowerShell (Windows)
|
|
||||||
.\container\run-linux.ps1
|
|
||||||
```
|
|
||||||
|
|
||||||
Output: `build/linux/bin/wowee`
|
|
||||||
|
|
||||||
### macOS (cross-compile, arm64 default)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./container/run-macos.sh
|
|
||||||
```
|
|
||||||
```powershell
|
|
||||||
.\container\run-macos.ps1
|
|
||||||
```
|
|
||||||
|
|
||||||
Output: `build/macos/bin/wowee`
|
|
||||||
|
|
||||||
For Intel (x86_64):
|
|
||||||
```bash
|
|
||||||
MACOS_ARCH=x86_64 ./container/run-macos.sh
|
|
||||||
```
|
|
||||||
```powershell
|
|
||||||
.\container\run-macos.ps1 -Arch x86_64
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows (cross-compile, x86_64)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./container/run-windows.sh
|
|
||||||
```
|
|
||||||
```powershell
|
|
||||||
.\container\run-windows.ps1
|
|
||||||
```
|
|
||||||
|
|
||||||
Output: `build/windows/bin/wowee.exe`
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
| Option | Bash | PowerShell | Description |
|
|
||||||
|--------|------|------------|-------------|
|
|
||||||
| Rebuild image | `--rebuild-image` | `-RebuildImage` | Force a fresh Docker image build |
|
|
||||||
| macOS arch | `MACOS_ARCH=x86_64` | `-Arch x86_64` | Build for Intel instead of Apple Silicon |
|
|
||||||
| FidelityFX SDK repo | `WOWEE_FFX_SDK_REPO=<url>` | `$env:WOWEE_FFX_SDK_REPO="<url>"` | Custom FidelityFX SDK git URL |
|
|
||||||
| FidelityFX SDK ref | `WOWEE_FFX_SDK_REF=<ref>` | `$env:WOWEE_FFX_SDK_REF="<ref>"` | Custom FidelityFX SDK git ref/tag |
|
|
||||||
|
|
||||||
## Docker Image Caching
|
|
||||||
|
|
||||||
The first build takes longer because Docker builds the toolchain image (installing compilers, vcpkg packages, etc.). Subsequent builds reuse the cached image and only run the compilation step.
|
|
||||||
|
|
||||||
To force a full image rebuild:
|
|
||||||
```bash
|
|
||||||
./container/run-linux.sh --rebuild-image
|
|
||||||
```
|
|
||||||
|
|
||||||
## Output Locations
|
|
||||||
|
|
||||||
| Target | Binary | Size |
|
|
||||||
|--------|--------|------|
|
|
||||||
| Linux | `build/linux/bin/wowee` | ~135 MB |
|
|
||||||
| macOS | `build/macos/bin/wowee` | ~40 MB |
|
|
||||||
| Windows | `build/windows/bin/wowee.exe` | ~135 MB |
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
container/
|
|
||||||
├── run-linux.sh / .ps1 # Host launchers (bash / PowerShell)
|
|
||||||
├── run-macos.sh / .ps1
|
|
||||||
├── run-windows.sh / .ps1
|
|
||||||
├── build-linux.sh # Container entrypoints (run inside Docker)
|
|
||||||
├── build-macos.sh
|
|
||||||
├── build-windows.sh
|
|
||||||
├── builder-linux.Dockerfile # Docker image definitions
|
|
||||||
├── builder-macos.Dockerfile
|
|
||||||
├── builder-windows.Dockerfile
|
|
||||||
├── macos/
|
|
||||||
│ ├── sdk-fetcher.py # Auto-fetches macOS SDK from Apple's catalog
|
|
||||||
│ ├── osxcross-toolchain.cmake # CMake toolchain for osxcross
|
|
||||||
│ └── triplets/ # vcpkg cross-compile triplets
|
|
||||||
│ ├── arm64-osx-cross.cmake
|
|
||||||
│ └── x64-osx-cross.cmake
|
|
||||||
├── README.md # This file
|
|
||||||
└── FLOW.md # Detailed build flow documentation
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
**"docker is not installed or not in PATH"**
|
|
||||||
Install Docker and ensure the `docker` command is available in your terminal.
|
|
||||||
|
|
||||||
**Build fails on first run**
|
|
||||||
Some vcpkg packages (ffmpeg, SDL2) take a while to compile. Ensure you have enough RAM (4 GB+) and disk space.
|
|
||||||
|
|
||||||
**macOS build: "could not find osxcross compiler"**
|
|
||||||
The Docker image may not have built correctly. Run with `--rebuild-image` to rebuild from scratch.
|
|
||||||
|
|
||||||
**Windows build: linker errors about vulkan-1.dll**
|
|
||||||
The build script auto-generates a Vulkan import library. If this fails, ensure the Docker image has `libvulkan-dev` installed (it should, by default).
|
|
||||||
19
container/build-in-container.sh
Executable file
19
container/build-in-container.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
|
||||||
|
podman build \
|
||||||
|
-f "${SCRIPT_DIR}/builder-ubuntu.Dockerfile" \
|
||||||
|
-t wowee-builder-ubuntu
|
||||||
|
|
||||||
|
BUILD_DIR="$(mktemp --tmpdir -d wowee.XXXXX \
|
||||||
|
--suffix=".$(cd "${PROJECT_ROOT}"; git rev-parse --short HEAD)")"
|
||||||
|
podman run \
|
||||||
|
--mount "type=bind,src=${PROJECT_ROOT},dst=/WoWee-src,ro=true" \
|
||||||
|
--mount "type=bind,src=${BUILD_DIR},dst=/build" \
|
||||||
|
localhost/wowee-builder-ubuntu \
|
||||||
|
./build-wowee.sh
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# Linux amd64 build entrypoint — runs INSIDE the linux container.
|
|
||||||
# Bind-mounts:
|
|
||||||
# /src (ro) — project source
|
|
||||||
# /out (rw) — host ./build/linux
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SRC=/src
|
|
||||||
OUT=/out
|
|
||||||
NPROC=$(nproc)
|
|
||||||
|
|
||||||
echo "==> [linux] Copying source tree..."
|
|
||||||
mkdir -p /wowee-build-src
|
|
||||||
tar -C "${SRC}" \
|
|
||||||
--exclude='./build' --exclude='./logs' --exclude='./cache' \
|
|
||||||
--exclude='./container' --exclude='./.git' \
|
|
||||||
--exclude='./Data/character' --exclude='./Data/creature' \
|
|
||||||
--exclude='./Data/db' --exclude='./Data/environment' \
|
|
||||||
--exclude='./Data/interface' --exclude='./Data/item' \
|
|
||||||
--exclude='./Data/misc' --exclude='./Data/sound' \
|
|
||||||
--exclude='./Data/spell' --exclude='./Data/terrain' \
|
|
||||||
--exclude='./Data/world' \
|
|
||||||
-cf - . | tar -C /wowee-build-src -xf -
|
|
||||||
|
|
||||||
cd /wowee-build-src
|
|
||||||
|
|
||||||
echo "==> [linux] Fetching external SDKs (if needed)..."
|
|
||||||
if [ ! -f extern/FidelityFX-FSR2/src/ffx-fsr2-api/ffx_fsr2.h ]; then
|
|
||||||
git clone --depth 1 \
|
|
||||||
https://github.com/GPUOpen-Effects/FidelityFX-FSR2.git \
|
|
||||||
extern/FidelityFX-FSR2 || echo "Warning: FSR2 clone failed — continuing without FSR2"
|
|
||||||
fi
|
|
||||||
|
|
||||||
SDK_REPO="${WOWEE_FFX_SDK_REPO:-https://github.com/Kelsidavis/FidelityFX-SDK.git}"
|
|
||||||
SDK_REF="${WOWEE_FFX_SDK_REF:-main}"
|
|
||||||
if [ ! -f "extern/FidelityFX-SDK/sdk/include/FidelityFX/host/ffx_frameinterpolation.h" ]; then
|
|
||||||
git clone --depth 1 --branch "${SDK_REF}" "${SDK_REPO}" extern/FidelityFX-SDK \
|
|
||||||
|| echo "Warning: FidelityFX-SDK clone failed — continuing without FSR3"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "==> [linux] Configuring with CMake (Release, Ninja, amd64)..."
|
|
||||||
cmake -S . -B "${OUT}" \
|
|
||||||
-G Ninja \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCMAKE_C_COMPILER=gcc \
|
|
||||||
-DCMAKE_CXX_COMPILER=g++ \
|
|
||||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON
|
|
||||||
|
|
||||||
echo "==> [linux] Building with ${NPROC} cores..."
|
|
||||||
cmake --build "${OUT}" --parallel "${NPROC}"
|
|
||||||
|
|
||||||
echo "==> [linux] Creating Data symlink..."
|
|
||||||
mkdir -p "${OUT}/bin"
|
|
||||||
if [ ! -e "${OUT}/bin/Data" ]; then
|
|
||||||
# Relative symlink so it resolves correctly on the host:
|
|
||||||
# build/linux/bin/Data -> ../../../Data (project root)
|
|
||||||
ln -s ../../../Data "${OUT}/bin/Data"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "==> [linux] Build complete. Artifacts in: ./build/linux/"
|
|
||||||
echo " Binary: ./build/linux/bin/wowee"
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# macOS cross-compile entrypoint — runs INSIDE the macos container.
|
|
||||||
# Toolchain: osxcross + Apple Clang, target: arm64-apple-darwin (default) or
|
|
||||||
# x86_64-apple-darwin when MACOS_ARCH=x86_64.
|
|
||||||
# Bind-mounts:
|
|
||||||
# /src (ro) — project source
|
|
||||||
# /out (rw) — host ./build/macos
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SRC=/src
|
|
||||||
OUT=/out
|
|
||||||
NPROC=$(nproc)
|
|
||||||
|
|
||||||
# Arch selection: arm64 (Apple Silicon) is the default primary target.
|
|
||||||
ARCH="${MACOS_ARCH:-arm64}"
|
|
||||||
case "${ARCH}" in
|
|
||||||
arm64) VCPKG_TRIPLET=arm64-osx-cross ;;
|
|
||||||
x86_64) VCPKG_TRIPLET=x64-osx-cross ;;
|
|
||||||
*) echo "ERROR: unsupported MACOS_ARCH '${ARCH}'. Use arm64 or x86_64." ; exit 1 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Auto-detect darwin target from osxcross binaries (e.g. arm64-apple-darwin24.5).
|
|
||||||
OSXCROSS_BIN=/opt/osxcross/target/bin
|
|
||||||
TARGET=$(basename "$(ls "${OSXCROSS_BIN}/${ARCH}-apple-darwin"*-clang 2>/dev/null | head -1)" | sed 's/-clang$//')
|
|
||||||
if [[ -z "${TARGET}" ]]; then
|
|
||||||
echo "ERROR: could not find osxcross ${ARCH} compiler in ${OSXCROSS_BIN}" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "==> Detected osxcross target: ${TARGET}"
|
|
||||||
|
|
||||||
echo "==> [macos/${ARCH}] Copying source tree..."
|
|
||||||
mkdir -p /wowee-build-src
|
|
||||||
tar -C "${SRC}" \
|
|
||||||
--exclude='./build' --exclude='./logs' --exclude='./cache' \
|
|
||||||
--exclude='./container' --exclude='./.git' \
|
|
||||||
--exclude='./Data/character' --exclude='./Data/creature' \
|
|
||||||
--exclude='./Data/db' --exclude='./Data/environment' \
|
|
||||||
--exclude='./Data/interface' --exclude='./Data/item' \
|
|
||||||
--exclude='./Data/misc' --exclude='./Data/sound' \
|
|
||||||
--exclude='./Data/spell' --exclude='./Data/terrain' \
|
|
||||||
--exclude='./Data/world' \
|
|
||||||
-cf - . | tar -C /wowee-build-src -xf -
|
|
||||||
|
|
||||||
cd /wowee-build-src
|
|
||||||
|
|
||||||
echo "==> [macos/${ARCH}] Fetching external SDKs (if needed)..."
|
|
||||||
if [ ! -f extern/FidelityFX-FSR2/src/ffx-fsr2-api/ffx_fsr2.h ]; then
|
|
||||||
git clone --depth 1 \
|
|
||||||
https://github.com/GPUOpen-Effects/FidelityFX-FSR2.git \
|
|
||||||
extern/FidelityFX-FSR2 || echo "Warning: FSR2 clone failed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
SDK_REPO="${WOWEE_FFX_SDK_REPO:-https://github.com/Kelsidavis/FidelityFX-SDK.git}"
|
|
||||||
SDK_REF="${WOWEE_FFX_SDK_REF:-main}"
|
|
||||||
if [ ! -f "extern/FidelityFX-SDK/sdk/include/FidelityFX/host/ffx_frameinterpolation.h" ]; then
|
|
||||||
git clone --depth 1 --branch "${SDK_REF}" "${SDK_REPO}" extern/FidelityFX-SDK \
|
|
||||||
|| echo "Warning: FidelityFX-SDK clone failed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "==> [macos/${ARCH}] Configuring with CMake (Release, Ninja, osxcross ${TARGET})..."
|
|
||||||
cmake -S . -B "${OUT}" \
|
|
||||||
-G Ninja \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCMAKE_SYSTEM_NAME=Darwin \
|
|
||||||
-DCMAKE_OSX_ARCHITECTURES="${ARCH}" \
|
|
||||||
-DCMAKE_OSX_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET:-13.0}" \
|
|
||||||
-DCMAKE_C_COMPILER="${OSXCROSS_BIN}/${TARGET}-clang" \
|
|
||||||
-DCMAKE_CXX_COMPILER="${OSXCROSS_BIN}/${TARGET}-clang++" \
|
|
||||||
-DCMAKE_AR="${OSXCROSS_BIN}/${TARGET}-ar" \
|
|
||||||
-DCMAKE_RANLIB="${OSXCROSS_BIN}/${TARGET}-ranlib" \
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" \
|
|
||||||
-DVCPKG_TARGET_TRIPLET="${VCPKG_TRIPLET}" \
|
|
||||||
-DVCPKG_OVERLAY_TRIPLETS=/opt/vcpkg-triplets \
|
|
||||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF \
|
|
||||||
-DWOWEE_ENABLE_ASAN=OFF
|
|
||||||
|
|
||||||
echo "==> [macos/${ARCH}] Building with ${NPROC} cores..."
|
|
||||||
cmake --build "${OUT}" --parallel "${NPROC}"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "==> [macos/${ARCH}] Build complete. Artifacts in: ./build/macos/"
|
|
||||||
echo " Binary: ./build/macos/bin/wowee"
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# Windows cross-compile entrypoint — runs INSIDE the windows container.
|
|
||||||
# Toolchain: LLVM-MinGW (Clang + LLD), target: x86_64-w64-mingw32-ucrt
|
|
||||||
# Bind-mounts:
|
|
||||||
# /src (ro) — project source
|
|
||||||
# /out (rw) — host ./build/windows
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SRC=/src
|
|
||||||
OUT=/out
|
|
||||||
NPROC=$(nproc)
|
|
||||||
TARGET=x86_64-w64-mingw32
|
|
||||||
|
|
||||||
# vcpkg's MinGW applocal hook always appends a powershell.exe post-build step to
|
|
||||||
# copy DLLs next to each binary, even when VCPKG_APPLOCAL_DEPS=OFF. For the
|
|
||||||
# x64-mingw-static triplet the bin/ dir is empty (no DLLs) so the script does
|
|
||||||
# nothing — but it still needs to exit 0. Provide a no-op stub if the real
|
|
||||||
# PowerShell isn't available.
|
|
||||||
if ! command -v powershell.exe &>/dev/null; then
|
|
||||||
printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/powershell.exe
|
|
||||||
chmod +x /usr/local/bin/powershell.exe
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "==> [windows] Copying source tree..."
|
|
||||||
mkdir -p /wowee-build-src
|
|
||||||
tar -C "${SRC}" \
|
|
||||||
--exclude='./build' --exclude='./logs' --exclude='./cache' \
|
|
||||||
--exclude='./container' --exclude='./.git' \
|
|
||||||
--exclude='./Data/character' --exclude='./Data/creature' \
|
|
||||||
--exclude='./Data/db' --exclude='./Data/environment' \
|
|
||||||
--exclude='./Data/interface' --exclude='./Data/item' \
|
|
||||||
--exclude='./Data/misc' --exclude='./Data/sound' \
|
|
||||||
--exclude='./Data/spell' --exclude='./Data/terrain' \
|
|
||||||
--exclude='./Data/world' \
|
|
||||||
-cf - . | tar -C /wowee-build-src -xf -
|
|
||||||
|
|
||||||
|
|
||||||
cd /wowee-build-src
|
|
||||||
|
|
||||||
echo "==> [windows] Fetching external SDKs (if needed)..."
|
|
||||||
if [ ! -f extern/FidelityFX-FSR2/src/ffx-fsr2-api/ffx_fsr2.h ]; then
|
|
||||||
git clone --depth 1 \
|
|
||||||
https://github.com/GPUOpen-Effects/FidelityFX-FSR2.git \
|
|
||||||
extern/FidelityFX-FSR2 || echo "Warning: FSR2 clone failed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
SDK_REPO="${WOWEE_FFX_SDK_REPO:-https://github.com/Kelsidavis/FidelityFX-SDK.git}"
|
|
||||||
SDK_REF="${WOWEE_FFX_SDK_REF:-main}"
|
|
||||||
if [ ! -f "extern/FidelityFX-SDK/sdk/include/FidelityFX/host/ffx_frameinterpolation.h" ]; then
|
|
||||||
git clone --depth 1 --branch "${SDK_REF}" "${SDK_REPO}" extern/FidelityFX-SDK \
|
|
||||||
|| echo "Warning: FidelityFX-SDK clone failed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "==> [windows] Generating Vulkan import library for cross-compile..."
|
|
||||||
# Windows applications link against vulkan-1.dll (the Khronos Vulkan loader).
|
|
||||||
# The cross-compile toolchain only ships Vulkan *headers* (via vcpkg), not the
|
|
||||||
# import library. Generate a minimal libvulkan-1.a from the header prototypes
|
|
||||||
# so the linker can resolve vk* symbols → vulkan-1.dll at runtime.
|
|
||||||
# We use the host libvulkan-dev header for function name extraction — the Vulkan
|
|
||||||
# API prototypes are platform-independent.
|
|
||||||
VULKAN_IMP_DIR="${OUT}/vulkan-import"
|
|
||||||
if [ ! -f "${VULKAN_IMP_DIR}/libvulkan-1.a" ]; then
|
|
||||||
mkdir -p "${VULKAN_IMP_DIR}"
|
|
||||||
# Try vcpkg-installed header first (available on incremental builds),
|
|
||||||
# then fall back to the host libvulkan-dev header (always present in the image).
|
|
||||||
VK_HEADER="${OUT}/vcpkg_installed/x64-mingw-static/include/vulkan/vulkan_core.h"
|
|
||||||
if [ ! -f "${VK_HEADER}" ]; then
|
|
||||||
VK_HEADER="/usr/include/vulkan/vulkan_core.h"
|
|
||||||
fi
|
|
||||||
{
|
|
||||||
echo "LIBRARY vulkan-1.dll"
|
|
||||||
echo "EXPORTS"
|
|
||||||
grep -oP 'VKAPI_ATTR \S+ VKAPI_CALL \K(vk\w+)' "${VK_HEADER}" | sort -u | sed 's/^/ /'
|
|
||||||
} > "${VULKAN_IMP_DIR}/vulkan-1.def"
|
|
||||||
"${TARGET}-dlltool" -d "${VULKAN_IMP_DIR}/vulkan-1.def" \
|
|
||||||
-l "${VULKAN_IMP_DIR}/libvulkan-1.a" -m i386:x86-64
|
|
||||||
echo " Generated $(wc -l < "${VULKAN_IMP_DIR}/vulkan-1.def") export entries"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "==> [windows] Configuring with CMake (Release, Ninja, LLVM-MinGW cross)..."
|
|
||||||
# Lock pkg-config to the cross-compiled vcpkg packages only.
|
|
||||||
# Without this, CMake's Vulkan pkg-config fallback finds the *Linux* libvulkan-dev
|
|
||||||
# and injects /usr/include into every MinGW compile command, which then fails
|
|
||||||
# because the glibc-specific bits/libc-header-start.h is not in the MinGW sysroot.
|
|
||||||
export PKG_CONFIG_LIBDIR="${OUT}/vcpkg_installed/x64-mingw-static/lib/pkgconfig"
|
|
||||||
export PKG_CONFIG_PATH=""
|
|
||||||
cmake -S . -B "${OUT}" \
|
|
||||||
-G Ninja \
|
|
||||||
-DCMAKE_BUILD_TYPE=Release \
|
|
||||||
-DCMAKE_SYSTEM_NAME=Windows \
|
|
||||||
-DCMAKE_C_COMPILER="${TARGET}-clang" \
|
|
||||||
-DCMAKE_CXX_COMPILER="${TARGET}-clang++" \
|
|
||||||
-DCMAKE_RC_COMPILER="${TARGET}-windres" \
|
|
||||||
-DCMAKE_AR="/opt/llvm-mingw/bin/llvm-ar" \
|
|
||||||
-DCMAKE_RANLIB="/opt/llvm-mingw/bin/llvm-ranlib" \
|
|
||||||
-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" \
|
|
||||||
-DCMAKE_SHARED_LINKER_FLAGS="-fuse-ld=lld" \
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE="${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" \
|
|
||||||
-DVCPKG_TARGET_TRIPLET=x64-mingw-static \
|
|
||||||
-DVCPKG_APPLOCAL_DEPS=OFF \
|
|
||||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF \
|
|
||||||
-DWOWEE_ENABLE_ASAN=OFF
|
|
||||||
|
|
||||||
echo "==> [windows] Building with ${NPROC} cores..."
|
|
||||||
cmake --build "${OUT}" --parallel "${NPROC}"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "==> [windows] Build complete. Artifacts in: ./build/windows/"
|
|
||||||
echo " Binary: ./build/windows/bin/wowee.exe"
|
|
||||||
14
container/build-wowee.sh
Executable file
14
container/build-wowee.sh
Executable file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
cp -r /WoWee-src /WoWee
|
||||||
|
|
||||||
|
pushd /WoWee
|
||||||
|
./build.sh
|
||||||
|
popd
|
||||||
|
|
||||||
|
pushd /WoWee/build
|
||||||
|
cmake --install . --prefix=/build
|
||||||
|
popd
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
FROM ubuntu:24.04
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
cmake \
|
|
||||||
ninja-build \
|
|
||||||
build-essential \
|
|
||||||
pkg-config \
|
|
||||||
git \
|
|
||||||
python3 \
|
|
||||||
glslang-tools \
|
|
||||||
spirv-tools \
|
|
||||||
libsdl2-dev \
|
|
||||||
libglew-dev \
|
|
||||||
libglm-dev \
|
|
||||||
libssl-dev \
|
|
||||||
zlib1g-dev \
|
|
||||||
libavformat-dev \
|
|
||||||
libavcodec-dev \
|
|
||||||
libswscale-dev \
|
|
||||||
libavutil-dev \
|
|
||||||
libvulkan-dev \
|
|
||||||
vulkan-tools \
|
|
||||||
libstorm-dev \
|
|
||||||
libunicorn-dev && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
COPY build-linux.sh /build-platform.sh
|
|
||||||
RUN chmod +x /build-platform.sh
|
|
||||||
|
|
||||||
ENTRYPOINT ["/build-platform.sh"]
|
|
||||||
|
|
@ -1,142 +0,0 @@
|
||||||
FROM ubuntu:24.04 AS sdk-fetcher
|
|
||||||
|
|
||||||
# Stage 1: Fetch macOS SDK from Apple's public software update catalog.
|
|
||||||
# This avoids requiring the user to supply the SDK tarball manually.
|
|
||||||
# The SDK is downloaded, extracted, and packaged as a .tar.gz.
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
ca-certificates \
|
|
||||||
python3 \
|
|
||||||
python3-defusedxml \
|
|
||||||
cpio \
|
|
||||||
tar \
|
|
||||||
gzip \
|
|
||||||
xz-utils && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
COPY macos/sdk-fetcher.py /opt/sdk-fetcher.py
|
|
||||||
RUN python3 /opt/sdk-fetcher.py /opt/sdk
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
FROM ubuntu:24.04 AS builder
|
|
||||||
|
|
||||||
# Stage 2: macOS cross-compile image using osxcross + Clang 18.
|
|
||||||
#
|
|
||||||
# Target triplets (auto-detected from osxcross):
|
|
||||||
# arm64-apple-darwinNN (Apple Silicon)
|
|
||||||
# x86_64-apple-darwinNN (Intel)
|
|
||||||
# Default: arm64. Override with MACOS_ARCH=x86_64 env var at run time.
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
ca-certificates \
|
|
||||||
cmake \
|
|
||||||
ninja-build \
|
|
||||||
git \
|
|
||||||
python3 \
|
|
||||||
curl \
|
|
||||||
wget \
|
|
||||||
xz-utils \
|
|
||||||
zip \
|
|
||||||
unzip \
|
|
||||||
tar \
|
|
||||||
make \
|
|
||||||
patch \
|
|
||||||
libssl-dev \
|
|
||||||
zlib1g-dev \
|
|
||||||
pkg-config \
|
|
||||||
libbz2-dev \
|
|
||||||
libxml2-dev \
|
|
||||||
libz-dev \
|
|
||||||
liblzma-dev \
|
|
||||||
uuid-dev \
|
|
||||||
python3-lxml \
|
|
||||||
gnupg \
|
|
||||||
software-properties-common && \
|
|
||||||
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \
|
|
||||||
echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-18 main" > /etc/apt/sources.list.d/llvm-18.list && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
clang-18 \
|
|
||||||
lld-18 \
|
|
||||||
llvm-18 && \
|
|
||||||
ln -sf /usr/bin/clang-18 /usr/bin/clang && \
|
|
||||||
ln -sf /usr/bin/clang++-18 /usr/bin/clang++ && \
|
|
||||||
ln -sf /usr/bin/lld-18 /usr/bin/lld && \
|
|
||||||
ln -sf /usr/bin/ld.lld-18 /usr/bin/ld.lld && \
|
|
||||||
ln -sf /usr/bin/llvm-ar-18 /usr/bin/llvm-ar && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Build osxcross with SDK from stage 1
|
|
||||||
RUN git clone --depth 1 https://github.com/tpoechtrager/osxcross.git /opt/osxcross
|
|
||||||
|
|
||||||
COPY --from=sdk-fetcher /opt/sdk/ /opt/osxcross/tarballs/
|
|
||||||
|
|
||||||
ENV MACOSX_DEPLOYMENT_TARGET=13.0
|
|
||||||
RUN cd /opt/osxcross && \
|
|
||||||
UNATTENDED=1 ./build.sh && \
|
|
||||||
rm -rf /opt/osxcross/build /opt/osxcross/tarballs
|
|
||||||
|
|
||||||
ENV PATH="/opt/osxcross/target/bin:${PATH}"
|
|
||||||
ENV OSXCROSS_TARGET_DIR="/opt/osxcross/target"
|
|
||||||
ENV MACOSX_DEPLOYMENT_TARGET=13.0
|
|
||||||
|
|
||||||
# Create unprefixed symlinks for macOS tools that vcpkg/CMake expect
|
|
||||||
RUN cd /opt/osxcross/target/bin && \
|
|
||||||
for tool in install_name_tool otool lipo codesign; do \
|
|
||||||
src="$(ls *-apple-darwin*-"${tool}" 2>/dev/null | head -1)"; \
|
|
||||||
if [ -n "$src" ]; then \
|
|
||||||
ln -sf "$src" "$tool"; \
|
|
||||||
fi; \
|
|
||||||
done
|
|
||||||
|
|
||||||
# Custom osxcross toolchain + vcpkg triplets
|
|
||||||
COPY macos/osxcross-toolchain.cmake /opt/osxcross-toolchain.cmake
|
|
||||||
COPY macos/triplets/ /opt/vcpkg-triplets/
|
|
||||||
|
|
||||||
# Extra tools needed by vcpkg's Mach-O rpath fixup and ffmpeg x86 asm
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends file nasm && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# vcpkg — macOS cross triplets (arm64-osx-cross / x64-osx-cross)
|
|
||||||
ENV VCPKG_ROOT=/opt/vcpkg
|
|
||||||
RUN git clone --depth 1 https://github.com/microsoft/vcpkg.git "${VCPKG_ROOT}" && \
|
|
||||||
"${VCPKG_ROOT}/bootstrap-vcpkg.sh" -disableMetrics
|
|
||||||
|
|
||||||
# Pre-install deps for both arches; the launcher script picks the right one at run time.
|
|
||||||
RUN "${VCPKG_ROOT}/vcpkg" install \
|
|
||||||
sdl2[vulkan] \
|
|
||||||
openssl \
|
|
||||||
glew \
|
|
||||||
glm \
|
|
||||||
zlib \
|
|
||||||
ffmpeg \
|
|
||||||
--triplet arm64-osx-cross \
|
|
||||||
--overlay-triplets=/opt/vcpkg-triplets
|
|
||||||
|
|
||||||
RUN "${VCPKG_ROOT}/vcpkg" install \
|
|
||||||
sdl2[vulkan] \
|
|
||||||
openssl \
|
|
||||||
glew \
|
|
||||||
glm \
|
|
||||||
zlib \
|
|
||||||
ffmpeg \
|
|
||||||
--triplet x64-osx-cross \
|
|
||||||
--overlay-triplets=/opt/vcpkg-triplets
|
|
||||||
|
|
||||||
# Vulkan SDK headers (MoltenVK is the runtime — headers only needed to compile)
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends libvulkan-dev glslang-tools && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
COPY build-macos.sh /build-platform.sh
|
|
||||||
RUN chmod +x /build-platform.sh
|
|
||||||
|
|
||||||
ENTRYPOINT ["/build-platform.sh"]
|
|
||||||
25
container/builder-ubuntu.Dockerfile
Normal file
25
container/builder-ubuntu.Dockerfile
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt install -y \
|
||||||
|
cmake \
|
||||||
|
build-essential \
|
||||||
|
pkg-config \
|
||||||
|
git \
|
||||||
|
libsdl2-dev \
|
||||||
|
libglew-dev \
|
||||||
|
libglm-dev \
|
||||||
|
libssl-dev \
|
||||||
|
zlib1g-dev \
|
||||||
|
libavformat-dev \
|
||||||
|
libavcodec-dev \
|
||||||
|
libswscale-dev \
|
||||||
|
libavutil-dev \
|
||||||
|
libvulkan-dev \
|
||||||
|
vulkan-tools \
|
||||||
|
libstorm-dev && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY build-wowee.sh /
|
||||||
|
|
||||||
|
ENTRYPOINT ./build-wowee.sh
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
FROM ubuntu:24.04
|
|
||||||
|
|
||||||
# Windows cross-compile using LLVM-MinGW — best-in-class Clang/LLD toolchain
|
|
||||||
# targeting x86_64-w64-mingw32. Produces native .exe/.dll without MSVC or Wine.
|
|
||||||
# LLVM-MinGW ships: clang, clang++, lld, libc++ / libunwind headers, winpthreads.
|
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
ENV LLVM_MINGW_VERSION=20240619
|
|
||||||
ENV LLVM_MINGW_URL=https://github.com/mstorsjo/llvm-mingw/releases/download/${LLVM_MINGW_VERSION}/llvm-mingw-${LLVM_MINGW_VERSION}-ucrt-ubuntu-20.04-x86_64.tar.xz
|
|
||||||
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends \
|
|
||||||
ca-certificates \
|
|
||||||
build-essential \
|
|
||||||
cmake \
|
|
||||||
ninja-build \
|
|
||||||
git \
|
|
||||||
python3 \
|
|
||||||
curl \
|
|
||||||
zip \
|
|
||||||
unzip \
|
|
||||||
tar \
|
|
||||||
xz-utils \
|
|
||||||
pkg-config \
|
|
||||||
nasm \
|
|
||||||
libssl-dev \
|
|
||||||
zlib1g-dev && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install LLVM-MinGW toolchain
|
|
||||||
RUN curl -fsSL "${LLVM_MINGW_URL}" -o /tmp/llvm-mingw.tar.xz && \
|
|
||||||
tar -xf /tmp/llvm-mingw.tar.xz -C /opt && \
|
|
||||||
mv /opt/llvm-mingw-${LLVM_MINGW_VERSION}-ucrt-ubuntu-20.04-x86_64 /opt/llvm-mingw && \
|
|
||||||
rm /tmp/llvm-mingw.tar.xz
|
|
||||||
|
|
||||||
ENV PATH="/opt/llvm-mingw/bin:${PATH}"
|
|
||||||
|
|
||||||
# Windows dependencies via vcpkg (static, x64-mingw-static triplet)
|
|
||||||
ENV VCPKG_ROOT=/opt/vcpkg
|
|
||||||
RUN git clone --depth 1 https://github.com/microsoft/vcpkg.git "${VCPKG_ROOT}" && \
|
|
||||||
"${VCPKG_ROOT}/bootstrap-vcpkg.sh" -disableMetrics
|
|
||||||
|
|
||||||
ENV VCPKG_DEFAULT_TRIPLET=x64-mingw-static
|
|
||||||
RUN "${VCPKG_ROOT}/vcpkg" install \
|
|
||||||
sdl2[vulkan] \
|
|
||||||
openssl \
|
|
||||||
glew \
|
|
||||||
glm \
|
|
||||||
zlib \
|
|
||||||
ffmpeg \
|
|
||||||
--triplet x64-mingw-static
|
|
||||||
|
|
||||||
# Vulkan SDK headers (loader is linked statically via SDL2's vulkan surface)
|
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y --no-install-recommends libvulkan-dev glslang-tools && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Provide a no-op powershell.exe so vcpkg's MinGW applocal post-build hook
|
|
||||||
# exits cleanly. The x64-mingw-static triplet is fully static (no DLLs to
|
|
||||||
# copy), so the script has nothing to do — it just needs to not fail.
|
|
||||||
RUN printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/powershell.exe && \
|
|
||||||
chmod +x /usr/local/bin/powershell.exe
|
|
||||||
|
|
||||||
COPY build-windows.sh /build-platform.sh
|
|
||||||
RUN chmod +x /build-platform.sh
|
|
||||||
|
|
||||||
ENTRYPOINT ["/build-platform.sh"]
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
# osxcross CMake toolchain file for cross-compiling to macOS from Linux.
|
|
||||||
# Used by vcpkg triplets and the WoWee build.
|
|
||||||
# Auto-detects SDK, darwin version, and arch from the osxcross installation
|
|
||||||
# and the VCPKG_OSX_ARCHITECTURES / CMAKE_OSX_ARCHITECTURES setting.
|
|
||||||
|
|
||||||
set(CMAKE_SYSTEM_NAME Darwin)
|
|
||||||
|
|
||||||
# ── osxcross paths ──────────────────────────────────────────────────
|
|
||||||
set(_target_dir "/opt/osxcross/target")
|
|
||||||
if(DEFINED ENV{OSXCROSS_TARGET_DIR})
|
|
||||||
set(_target_dir "$ENV{OSXCROSS_TARGET_DIR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Auto-detect SDK (pick the newest if several are present)
|
|
||||||
file(GLOB _sdk_dirs "${_target_dir}/SDK/MacOSX*.sdk")
|
|
||||||
list(SORT _sdk_dirs)
|
|
||||||
list(GET _sdk_dirs -1 _sdk_dir)
|
|
||||||
set(CMAKE_OSX_SYSROOT "${_sdk_dir}" CACHE PATH "" FORCE)
|
|
||||||
|
|
||||||
# Deployment target
|
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "13.0" CACHE STRING "" FORCE)
|
|
||||||
if(DEFINED ENV{MACOSX_DEPLOYMENT_TARGET})
|
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "$ENV{MACOSX_DEPLOYMENT_TARGET}" CACHE STRING "" FORCE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ── auto-detect darwin version from compiler names ──────────────────
|
|
||||||
file(GLOB _darwin_compilers "${_target_dir}/bin/*-apple-darwin*-clang")
|
|
||||||
list(GET _darwin_compilers 0 _first_compiler)
|
|
||||||
get_filename_component(_compiler_name "${_first_compiler}" NAME)
|
|
||||||
string(REGEX MATCH "apple-darwin[0-9.]+" _darwin_part "${_compiler_name}")
|
|
||||||
|
|
||||||
# ── pick architecture ───────────────────────────────────────────────
|
|
||||||
# CMAKE_OSX_ARCHITECTURES is set by vcpkg from VCPKG_OSX_ARCHITECTURES
|
|
||||||
if(CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
|
|
||||||
set(_arch "arm64")
|
|
||||||
elseif(CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64")
|
|
||||||
set(_arch "x86_64")
|
|
||||||
elseif(DEFINED ENV{OSXCROSS_ARCH})
|
|
||||||
set(_arch "$ENV{OSXCROSS_ARCH}")
|
|
||||||
else()
|
|
||||||
set(_arch "arm64")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(_host "${_arch}-${_darwin_part}")
|
|
||||||
set(CMAKE_SYSTEM_PROCESSOR "${_arch}" CACHE STRING "" FORCE)
|
|
||||||
|
|
||||||
# ── compilers ───────────────────────────────────────────────────────
|
|
||||||
set(CMAKE_C_COMPILER "${_target_dir}/bin/${_host}-clang" CACHE FILEPATH "" FORCE)
|
|
||||||
set(CMAKE_CXX_COMPILER "${_target_dir}/bin/${_host}-clang++" CACHE FILEPATH "" FORCE)
|
|
||||||
|
|
||||||
# ── tools ───────────────────────────────────────────────────────────
|
|
||||||
set(CMAKE_AR "${_target_dir}/bin/${_host}-ar" CACHE FILEPATH "" FORCE)
|
|
||||||
set(CMAKE_RANLIB "${_target_dir}/bin/${_host}-ranlib" CACHE FILEPATH "" FORCE)
|
|
||||||
set(CMAKE_STRIP "${_target_dir}/bin/${_host}-strip" CACHE FILEPATH "" FORCE)
|
|
||||||
set(CMAKE_INSTALL_NAME_TOOL "${_target_dir}/bin/${_host}-install_name_tool" CACHE FILEPATH "" FORCE)
|
|
||||||
|
|
||||||
# ── search paths ────────────────────────────────────────────────────
|
|
||||||
set(CMAKE_FIND_ROOT_PATH "${_sdk_dir}" "${_target_dir}")
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
|
|
||||||
|
|
@ -1,380 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""Download and extract macOS SDK from Apple's Command Line Tools package.
|
|
||||||
|
|
||||||
Apple publishes Command Line Tools (CLT) packages via their publicly
|
|
||||||
accessible software update catalog. This script downloads the latest CLT,
|
|
||||||
extracts just the macOS SDK, and packages it as a .tar.gz tarball suitable
|
|
||||||
for osxcross.
|
|
||||||
|
|
||||||
No Apple ID or paid developer account required.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
python3 sdk-fetcher.py [output_dir]
|
|
||||||
|
|
||||||
The script prints the absolute path of the resulting tarball to stdout.
|
|
||||||
All progress / status messages go to stderr.
|
|
||||||
If a cached SDK tarball already exists in output_dir, it is reused.
|
|
||||||
|
|
||||||
Dependencies: python3 (>= 3.6), cpio, tar, gzip
|
|
||||||
Optional: bsdtar (libarchive-tools) or xar -- faster XAR extraction.
|
|
||||||
Falls back to a pure-Python XAR parser when neither is available.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import glob
|
|
||||||
import gzip
|
|
||||||
import lzma
|
|
||||||
import os
|
|
||||||
import plistlib
|
|
||||||
import re
|
|
||||||
import shutil
|
|
||||||
import struct
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import tempfile
|
|
||||||
import urllib.request
|
|
||||||
import zlib
|
|
||||||
|
|
||||||
try:
|
|
||||||
import defusedxml.ElementTree as ET
|
|
||||||
except ImportError as exc:
|
|
||||||
raise ImportError(
|
|
||||||
"defusedxml is required: pip install defusedxml"
|
|
||||||
) from exc
|
|
||||||
|
|
||||||
# -- Configuration -----------------------------------------------------------
|
|
||||||
|
|
||||||
CATALOG_URLS = [
|
|
||||||
# Try newest catalog first; first successful fetch wins.
|
|
||||||
"https://swscan.apple.com/content/catalogs/others/"
|
|
||||||
"index-16-15-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-"
|
|
||||||
"mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz",
|
|
||||||
|
|
||||||
"https://swscan.apple.com/content/catalogs/others/"
|
|
||||||
"index-15-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-"
|
|
||||||
"mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz",
|
|
||||||
|
|
||||||
"https://swscan.apple.com/content/catalogs/others/"
|
|
||||||
"index-14-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-"
|
|
||||||
"mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz",
|
|
||||||
]
|
|
||||||
|
|
||||||
USER_AGENT = "Software%20Update"
|
|
||||||
|
|
||||||
|
|
||||||
# -- Helpers -----------------------------------------------------------------
|
|
||||||
|
|
||||||
def _validate_url(url):
|
|
||||||
"""Reject non-HTTPS URLs to prevent file:// and other scheme attacks."""
|
|
||||||
if not url.startswith("https://"):
|
|
||||||
raise ValueError(f"Refusing non-HTTPS URL: {url}")
|
|
||||||
|
|
||||||
|
|
||||||
def log(msg):
|
|
||||||
print(msg, file=sys.stderr, flush=True)
|
|
||||||
|
|
||||||
|
|
||||||
# -- 1) Catalog & URL discovery ----------------------------------------------
|
|
||||||
|
|
||||||
def find_sdk_pkg_url():
|
|
||||||
"""Search Apple catalogs for the latest CLTools_macOSNMOS_SDK.pkg URL."""
|
|
||||||
for cat_url in CATALOG_URLS:
|
|
||||||
short = cat_url.split("/index-")[1][:25] + "..."
|
|
||||||
log(f" Trying catalog: {short}")
|
|
||||||
try:
|
|
||||||
_validate_url(cat_url)
|
|
||||||
req = urllib.request.Request(cat_url, headers={"User-Agent": USER_AGENT})
|
|
||||||
with urllib.request.urlopen(req, timeout=60) as resp: # nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected
|
|
||||||
raw = gzip.decompress(resp.read())
|
|
||||||
catalog = plistlib.loads(raw)
|
|
||||||
except Exception as exc:
|
|
||||||
log(f" -> fetch failed: {exc}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
products = catalog.get("Products", {})
|
|
||||||
candidates = []
|
|
||||||
for pid, product in products.items():
|
|
||||||
post_date = str(product.get("PostDate", ""))
|
|
||||||
for pkg in product.get("Packages", []):
|
|
||||||
url = pkg.get("URL", "")
|
|
||||||
size = pkg.get("Size", 0)
|
|
||||||
if "CLTools_macOSNMOS_SDK" in url and url.endswith(".pkg"):
|
|
||||||
candidates.append((post_date, url, size, pid))
|
|
||||||
|
|
||||||
if not candidates:
|
|
||||||
log(f" -> no CLTools SDK packages in this catalog, trying next...")
|
|
||||||
continue
|
|
||||||
|
|
||||||
candidates.sort(reverse=True)
|
|
||||||
_date, url, size, pid = candidates[0]
|
|
||||||
log(f"==> Found: CLTools_macOSNMOS_SDK (product {pid}, {size // 1048576} MB)")
|
|
||||||
return url
|
|
||||||
|
|
||||||
log("ERROR: No CLTools SDK packages found in any Apple catalog.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
# -- 2) Download -------------------------------------------------------------
|
|
||||||
|
|
||||||
def download(url, dest):
|
|
||||||
"""Download *url* to *dest* with a basic progress indicator."""
|
|
||||||
_validate_url(url)
|
|
||||||
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
|
|
||||||
with urllib.request.urlopen(req, timeout=600) as resp: # nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected
|
|
||||||
total = int(resp.headers.get("Content-Length", 0))
|
|
||||||
done = 0
|
|
||||||
with open(dest, "wb") as f:
|
|
||||||
while True:
|
|
||||||
chunk = resp.read(1 << 20)
|
|
||||||
if not chunk:
|
|
||||||
break
|
|
||||||
f.write(chunk)
|
|
||||||
done += len(chunk)
|
|
||||||
if total:
|
|
||||||
pct = done * 100 // total
|
|
||||||
log(f"\r {done // 1048576} / {total // 1048576} MB ({pct}%)")
|
|
||||||
log("")
|
|
||||||
|
|
||||||
|
|
||||||
# -- 3) XAR extraction -------------------------------------------------------
|
|
||||||
|
|
||||||
def extract_xar(pkg_path, dest_dir):
|
|
||||||
"""Extract a XAR (.pkg) archive -- external tool or pure-Python fallback."""
|
|
||||||
for tool in ("bsdtar", "xar"):
|
|
||||||
if shutil.which(tool):
|
|
||||||
log(f"==> Extracting .pkg with {tool}...")
|
|
||||||
r = subprocess.run([tool, "-xf", pkg_path, "-C", dest_dir],
|
|
||||||
capture_output=True)
|
|
||||||
if r.returncode == 0:
|
|
||||||
return
|
|
||||||
log(f" {tool} exited {r.returncode}, trying next method...")
|
|
||||||
|
|
||||||
log("==> Extracting .pkg with built-in Python XAR parser...")
|
|
||||||
_extract_xar_python(pkg_path, dest_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def _extract_xar_python(pkg_path, dest_dir):
|
|
||||||
"""Pure-Python XAR extractor (no external dependencies)."""
|
|
||||||
with open(pkg_path, "rb") as f:
|
|
||||||
raw = f.read(28)
|
|
||||||
if len(raw) < 28:
|
|
||||||
raise ValueError("File too small to be a valid XAR archive")
|
|
||||||
magic, hdr_size, _ver, toc_clen, _toc_ulen, _ck = struct.unpack(
|
|
||||||
">4sHHQQI", raw,
|
|
||||||
)
|
|
||||||
if magic != b"xar!":
|
|
||||||
raise ValueError(f"Not a XAR file (magic: {magic!r})")
|
|
||||||
|
|
||||||
f.seek(hdr_size)
|
|
||||||
toc_xml = zlib.decompress(f.read(toc_clen))
|
|
||||||
heap_off = hdr_size + toc_clen
|
|
||||||
|
|
||||||
root = ET.fromstring(toc_xml)
|
|
||||||
toc = root.find("toc")
|
|
||||||
if toc is None:
|
|
||||||
raise ValueError("Malformed XAR: no <toc> element")
|
|
||||||
|
|
||||||
def _walk(elem, base):
|
|
||||||
for fe in elem.findall("file"):
|
|
||||||
name = fe.findtext("name", "")
|
|
||||||
ftype = fe.findtext("type", "file")
|
|
||||||
path = os.path.join(base, name)
|
|
||||||
|
|
||||||
if ftype == "directory":
|
|
||||||
os.makedirs(path, exist_ok=True)
|
|
||||||
_walk(fe, path)
|
|
||||||
continue
|
|
||||||
|
|
||||||
de = fe.find("data")
|
|
||||||
if de is None:
|
|
||||||
continue
|
|
||||||
offset = int(de.findtext("offset", "0"))
|
|
||||||
size = int(de.findtext("size", "0"))
|
|
||||||
enc_el = de.find("encoding")
|
|
||||||
enc = enc_el.get("style", "") if enc_el is not None else ""
|
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
||||||
f.seek(heap_off + offset)
|
|
||||||
|
|
||||||
if "gzip" in enc:
|
|
||||||
with open(path, "wb") as out:
|
|
||||||
out.write(zlib.decompress(f.read(size), 15 + 32))
|
|
||||||
elif "bzip2" in enc:
|
|
||||||
import bz2
|
|
||||||
with open(path, "wb") as out:
|
|
||||||
out.write(bz2.decompress(f.read(size)))
|
|
||||||
else:
|
|
||||||
with open(path, "wb") as out:
|
|
||||||
rem = size
|
|
||||||
while rem > 0:
|
|
||||||
blk = f.read(min(rem, 1 << 20))
|
|
||||||
if not blk:
|
|
||||||
break
|
|
||||||
out.write(blk)
|
|
||||||
rem -= len(blk)
|
|
||||||
|
|
||||||
_walk(toc, dest_dir)
|
|
||||||
|
|
||||||
|
|
||||||
# -- 4) Payload extraction (pbzx / gzip cpio) --------------------------------
|
|
||||||
|
|
||||||
def _pbzx_stream(path):
|
|
||||||
"""Yield decompressed chunks from a pbzx-compressed file."""
|
|
||||||
with open(path, "rb") as f:
|
|
||||||
if f.read(4) != b"pbzx":
|
|
||||||
raise ValueError("Not a pbzx file")
|
|
||||||
f.read(8)
|
|
||||||
while True:
|
|
||||||
hdr = f.read(16)
|
|
||||||
if len(hdr) < 16:
|
|
||||||
break
|
|
||||||
_usize, csize = struct.unpack(">QQ", hdr)
|
|
||||||
data = f.read(csize)
|
|
||||||
if len(data) < csize:
|
|
||||||
break
|
|
||||||
if csize == _usize:
|
|
||||||
yield data
|
|
||||||
else:
|
|
||||||
yield lzma.decompress(data)
|
|
||||||
|
|
||||||
|
|
||||||
def _gzip_stream(path):
|
|
||||||
"""Yield decompressed chunks from a gzip file."""
|
|
||||||
with gzip.open(path, "rb") as f:
|
|
||||||
while True:
|
|
||||||
chunk = f.read(1 << 20)
|
|
||||||
if not chunk:
|
|
||||||
break
|
|
||||||
yield chunk
|
|
||||||
|
|
||||||
|
|
||||||
def _raw_stream(path):
|
|
||||||
"""Yield raw 1 MiB chunks (last resort)."""
|
|
||||||
with open(path, "rb") as f:
|
|
||||||
while True:
|
|
||||||
chunk = f.read(1 << 20)
|
|
||||||
if not chunk:
|
|
||||||
break
|
|
||||||
yield chunk
|
|
||||||
|
|
||||||
|
|
||||||
def extract_payload(payload_path, out_dir):
|
|
||||||
"""Decompress a CLT Payload (pbzx or gzip cpio) into *out_dir*."""
|
|
||||||
with open(payload_path, "rb") as pf:
|
|
||||||
magic = pf.read(4)
|
|
||||||
|
|
||||||
if magic == b"pbzx":
|
|
||||||
log(" Payload format: pbzx (LZMA chunks)")
|
|
||||||
stream = _pbzx_stream(payload_path)
|
|
||||||
elif magic[:2] == b"\x1f\x8b":
|
|
||||||
log(" Payload format: gzip")
|
|
||||||
stream = _gzip_stream(payload_path)
|
|
||||||
else:
|
|
||||||
log(f" Payload format: unknown (magic: {magic.hex()}), trying raw cpio...")
|
|
||||||
stream = _raw_stream(payload_path)
|
|
||||||
|
|
||||||
proc = subprocess.Popen(
|
|
||||||
["cpio", "-id", "--quiet"],
|
|
||||||
stdin=subprocess.PIPE,
|
|
||||||
cwd=out_dir,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
)
|
|
||||||
for chunk in stream:
|
|
||||||
try:
|
|
||||||
proc.stdin.write(chunk)
|
|
||||||
except BrokenPipeError:
|
|
||||||
break
|
|
||||||
proc.stdin.close()
|
|
||||||
proc.wait()
|
|
||||||
|
|
||||||
|
|
||||||
# -- Main --------------------------------------------------------------------
|
|
||||||
|
|
||||||
def main():
|
|
||||||
output_dir = os.path.abspath(sys.argv[1]) if len(sys.argv) > 1 else os.getcwd()
|
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
|
||||||
|
|
||||||
# Re-use a previously fetched SDK if present.
|
|
||||||
cached = glob.glob(os.path.join(output_dir, "MacOSX*.sdk.tar.*"))
|
|
||||||
if cached:
|
|
||||||
cached.sort()
|
|
||||||
result = os.path.realpath(cached[-1])
|
|
||||||
log(f"==> Using cached SDK: {os.path.basename(result)}")
|
|
||||||
print(result)
|
|
||||||
return
|
|
||||||
|
|
||||||
work = tempfile.mkdtemp(prefix="fetch-macos-sdk-")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 1 -- Locate SDK package URL from Apple's catalog
|
|
||||||
log("==> Searching Apple software-update catalogs...")
|
|
||||||
sdk_url = find_sdk_pkg_url()
|
|
||||||
|
|
||||||
# 2 -- Download (just the SDK component, ~55 MB)
|
|
||||||
pkg = os.path.join(work, "sdk.pkg")
|
|
||||||
log("==> Downloading CLTools SDK package...")
|
|
||||||
download(sdk_url, pkg)
|
|
||||||
|
|
||||||
# 3 -- Extract the flat .pkg (XAR format) to get the Payload
|
|
||||||
pkg_dir = os.path.join(work, "pkg")
|
|
||||||
os.makedirs(pkg_dir)
|
|
||||||
extract_xar(pkg, pkg_dir)
|
|
||||||
os.unlink(pkg)
|
|
||||||
|
|
||||||
# 4 -- Locate the Payload file
|
|
||||||
log("==> Locating SDK payload...")
|
|
||||||
sdk_payload = None
|
|
||||||
for dirpath, _dirs, files in os.walk(pkg_dir):
|
|
||||||
if "Payload" in files:
|
|
||||||
sdk_payload = os.path.join(dirpath, "Payload")
|
|
||||||
log(f" Found: {os.path.relpath(sdk_payload, pkg_dir)}")
|
|
||||||
break
|
|
||||||
|
|
||||||
if sdk_payload is None:
|
|
||||||
log("ERROR: No Payload found in extracted package")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# 5 -- Decompress Payload -> cpio -> filesystem
|
|
||||||
sdk_root = os.path.join(work, "sdk")
|
|
||||||
os.makedirs(sdk_root)
|
|
||||||
log("==> Extracting SDK from payload (this may take a minute)...")
|
|
||||||
extract_payload(sdk_payload, sdk_root)
|
|
||||||
shutil.rmtree(pkg_dir)
|
|
||||||
|
|
||||||
# 6 -- Find MacOSX*.sdk directory
|
|
||||||
sdk_found = None
|
|
||||||
for dirpath, dirs, _files in os.walk(sdk_root):
|
|
||||||
for d in dirs:
|
|
||||||
if re.match(r"MacOSX\d+(\.\d+)?\.sdk$", d):
|
|
||||||
sdk_found = os.path.join(dirpath, d)
|
|
||||||
break
|
|
||||||
if sdk_found:
|
|
||||||
break
|
|
||||||
|
|
||||||
if not sdk_found:
|
|
||||||
log("ERROR: MacOSX*.sdk directory not found. Extracted contents:")
|
|
||||||
for dp, ds, fs in os.walk(sdk_root):
|
|
||||||
depth = dp.replace(sdk_root, "").count(os.sep)
|
|
||||||
if depth < 4:
|
|
||||||
log(f" {' ' * depth}{os.path.basename(dp)}/")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
sdk_name = os.path.basename(sdk_found)
|
|
||||||
log(f"==> Found: {sdk_name}")
|
|
||||||
|
|
||||||
# 7 -- Package as .tar.gz
|
|
||||||
tarball = os.path.join(output_dir, f"{sdk_name}.tar.gz")
|
|
||||||
log(f"==> Packaging: {sdk_name}.tar.gz ...")
|
|
||||||
subprocess.run(
|
|
||||||
["tar", "-czf", tarball, "-C", os.path.dirname(sdk_found), sdk_name],
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
log(f"==> macOS SDK ready: {tarball}")
|
|
||||||
print(tarball)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
shutil.rmtree(work, ignore_errors=True)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
set(VCPKG_TARGET_ARCHITECTURE arm64)
|
|
||||||
set(VCPKG_CRT_LINKAGE dynamic)
|
|
||||||
set(VCPKG_LIBRARY_LINKAGE static)
|
|
||||||
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
|
|
||||||
|
|
||||||
set(VCPKG_OSX_ARCHITECTURES arm64)
|
|
||||||
set(VCPKG_OSX_DEPLOYMENT_TARGET 13.0)
|
|
||||||
|
|
||||||
# osxcross toolchain
|
|
||||||
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE /opt/osxcross-toolchain.cmake)
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
set(VCPKG_TARGET_ARCHITECTURE x64)
|
|
||||||
set(VCPKG_CRT_LINKAGE dynamic)
|
|
||||||
set(VCPKG_LIBRARY_LINKAGE static)
|
|
||||||
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
|
|
||||||
|
|
||||||
set(VCPKG_OSX_ARCHITECTURES x86_64)
|
|
||||||
set(VCPKG_OSX_DEPLOYMENT_TARGET 13.0)
|
|
||||||
|
|
||||||
# osxcross toolchain
|
|
||||||
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE /opt/osxcross-toolchain.cmake)
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
# run-linux.ps1 — Build WoWee for Linux (amd64) inside a Docker container.
|
|
||||||
#
|
|
||||||
# Usage (run from project root):
|
|
||||||
# .\container\run-linux.ps1 [-RebuildImage]
|
|
||||||
#
|
|
||||||
# Environment variables:
|
|
||||||
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
|
|
||||||
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
|
|
||||||
|
|
||||||
param(
|
|
||||||
[switch]$RebuildImage
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
|
||||||
$ProjectRoot = (Resolve-Path "$ScriptDir\..").Path
|
|
||||||
|
|
||||||
$ImageName = "wowee-builder-linux"
|
|
||||||
$BuildOutput = "$ProjectRoot\build\linux"
|
|
||||||
|
|
||||||
# Verify Docker is available
|
|
||||||
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
|
|
||||||
Write-Error "docker is not installed or not in PATH."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build the image (skip if already present and -RebuildImage not given)
|
|
||||||
$imageExists = docker image inspect $ImageName 2>$null
|
|
||||||
if ($RebuildImage -or -not $imageExists) {
|
|
||||||
Write-Host "==> Building Docker image: $ImageName"
|
|
||||||
docker build `
|
|
||||||
-f "$ScriptDir\builder-linux.Dockerfile" `
|
|
||||||
-t $ImageName `
|
|
||||||
"$ScriptDir"
|
|
||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
||||||
} else {
|
|
||||||
Write-Host "==> Using existing Docker image: $ImageName"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create output directory on the host
|
|
||||||
New-Item -ItemType Directory -Force -Path $BuildOutput | Out-Null
|
|
||||||
|
|
||||||
Write-Host "==> Starting Linux build (output: $BuildOutput)"
|
|
||||||
|
|
||||||
$dockerArgs = @(
|
|
||||||
"run", "--rm",
|
|
||||||
"--mount", "type=bind,src=$ProjectRoot,dst=/src,readonly",
|
|
||||||
"--mount", "type=bind,src=$BuildOutput,dst=/out"
|
|
||||||
)
|
|
||||||
|
|
||||||
if ($env:WOWEE_FFX_SDK_REPO) {
|
|
||||||
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REPO=$env:WOWEE_FFX_SDK_REPO")
|
|
||||||
}
|
|
||||||
if ($env:WOWEE_FFX_SDK_REF) {
|
|
||||||
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REF=$env:WOWEE_FFX_SDK_REF")
|
|
||||||
}
|
|
||||||
|
|
||||||
$dockerArgs += $ImageName
|
|
||||||
|
|
||||||
& docker @dockerArgs
|
|
||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
||||||
|
|
||||||
Write-Host "==> Linux build complete. Artifacts in: $BuildOutput"
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# run-linux.sh — Build WoWee for Linux (amd64) inside a Docker container.
|
|
||||||
#
|
|
||||||
# Usage (run from project root):
|
|
||||||
# ./container/run-linux.sh [--rebuild-image]
|
|
||||||
#
|
|
||||||
# Environment variables:
|
|
||||||
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
|
|
||||||
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
|
|
||||||
# REBUILD_IMAGE — Set to 1 to force a fresh docker build (same as --rebuild-image)
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
||||||
|
|
||||||
IMAGE_NAME="wowee-builder-linux"
|
|
||||||
BUILD_OUTPUT="${PROJECT_ROOT}/build/linux"
|
|
||||||
|
|
||||||
# Parse arguments
|
|
||||||
REBUILD_IMAGE="${REBUILD_IMAGE:-0}"
|
|
||||||
for arg in "$@"; do
|
|
||||||
case "$arg" in
|
|
||||||
--rebuild-image) REBUILD_IMAGE=1 ;;
|
|
||||||
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Verify Docker is available
|
|
||||||
if ! command -v docker &>/dev/null; then
|
|
||||||
echo "Error: docker is not installed or not in PATH." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build the image (skip if already present and --rebuild-image not given)
|
|
||||||
if [[ "$REBUILD_IMAGE" == "1" ]] || ! docker image inspect "$IMAGE_NAME" &>/dev/null; then
|
|
||||||
echo "==> Building Docker image: ${IMAGE_NAME}"
|
|
||||||
docker build \
|
|
||||||
-f "${SCRIPT_DIR}/builder-linux.Dockerfile" \
|
|
||||||
-t "$IMAGE_NAME" \
|
|
||||||
"${SCRIPT_DIR}"
|
|
||||||
else
|
|
||||||
echo "==> Using existing Docker image: ${IMAGE_NAME}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create output directory on the host
|
|
||||||
mkdir -p "$BUILD_OUTPUT"
|
|
||||||
|
|
||||||
echo "==> Starting Linux build (output: ${BUILD_OUTPUT})"
|
|
||||||
|
|
||||||
docker run --rm \
|
|
||||||
--mount "type=bind,src=${PROJECT_ROOT},dst=/src,readonly" \
|
|
||||||
--mount "type=bind,src=${BUILD_OUTPUT},dst=/out" \
|
|
||||||
${WOWEE_FFX_SDK_REPO:+--env "WOWEE_FFX_SDK_REPO=${WOWEE_FFX_SDK_REPO}"} \
|
|
||||||
${WOWEE_FFX_SDK_REF:+--env "WOWEE_FFX_SDK_REF=${WOWEE_FFX_SDK_REF}"} \
|
|
||||||
"$IMAGE_NAME"
|
|
||||||
|
|
||||||
echo "==> Linux build complete. Artifacts in: ${BUILD_OUTPUT}"
|
|
||||||
|
|
@ -1,71 +0,0 @@
|
||||||
# run-macos.ps1 — Cross-compile WoWee for macOS (arm64 or x86_64) inside a Docker container.
|
|
||||||
#
|
|
||||||
# Usage (run from project root):
|
|
||||||
# .\container\run-macos.ps1 [-RebuildImage] [-Arch arm64|x86_64]
|
|
||||||
#
|
|
||||||
# The macOS SDK is fetched automatically inside the Docker build from Apple's
|
|
||||||
# public software update catalog. No manual SDK download required.
|
|
||||||
#
|
|
||||||
# Environment variables:
|
|
||||||
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
|
|
||||||
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
|
|
||||||
|
|
||||||
param(
|
|
||||||
[switch]$RebuildImage,
|
|
||||||
[ValidateSet("arm64", "x86_64")]
|
|
||||||
[string]$Arch = "arm64"
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
|
||||||
$ProjectRoot = (Resolve-Path "$ScriptDir\..").Path
|
|
||||||
|
|
||||||
$ImageName = "wowee-builder-macos"
|
|
||||||
$BuildOutput = "$ProjectRoot\build\macos"
|
|
||||||
|
|
||||||
# Verify Docker is available
|
|
||||||
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
|
|
||||||
Write-Error "docker is not installed or not in PATH."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build the image (skip if already present and -RebuildImage not given)
|
|
||||||
$imageExists = docker image inspect $ImageName 2>$null
|
|
||||||
if ($RebuildImage -or -not $imageExists) {
|
|
||||||
Write-Host "==> Building Docker image: $ImageName"
|
|
||||||
Write-Host " (SDK will be fetched automatically from Apple's catalog)"
|
|
||||||
docker build `
|
|
||||||
-f "$ScriptDir\builder-macos.Dockerfile" `
|
|
||||||
-t $ImageName `
|
|
||||||
"$ScriptDir"
|
|
||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
||||||
} else {
|
|
||||||
Write-Host "==> Using existing Docker image: $ImageName"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create output directory on the host
|
|
||||||
New-Item -ItemType Directory -Force -Path $BuildOutput | Out-Null
|
|
||||||
|
|
||||||
Write-Host "==> Starting macOS cross-compile build (arch=$Arch, output: $BuildOutput)"
|
|
||||||
|
|
||||||
$dockerArgs = @(
|
|
||||||
"run", "--rm",
|
|
||||||
"--mount", "type=bind,src=$ProjectRoot,dst=/src,readonly",
|
|
||||||
"--mount", "type=bind,src=$BuildOutput,dst=/out",
|
|
||||||
"--env", "MACOS_ARCH=$Arch"
|
|
||||||
)
|
|
||||||
|
|
||||||
if ($env:WOWEE_FFX_SDK_REPO) {
|
|
||||||
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REPO=$env:WOWEE_FFX_SDK_REPO")
|
|
||||||
}
|
|
||||||
if ($env:WOWEE_FFX_SDK_REF) {
|
|
||||||
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REF=$env:WOWEE_FFX_SDK_REF")
|
|
||||||
}
|
|
||||||
|
|
||||||
$dockerArgs += $ImageName
|
|
||||||
|
|
||||||
& docker @dockerArgs
|
|
||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
||||||
|
|
||||||
Write-Host "==> macOS cross-compile build complete. Artifacts in: $BuildOutput"
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# run-macos.sh — Cross-compile WoWee for macOS (arm64 or x86_64) inside a Docker container.
|
|
||||||
#
|
|
||||||
# Usage (run from project root):
|
|
||||||
# ./container/run-macos.sh [--rebuild-image]
|
|
||||||
#
|
|
||||||
# The macOS SDK is fetched automatically inside the Docker build from Apple's
|
|
||||||
# public software update catalog. No manual SDK download required.
|
|
||||||
#
|
|
||||||
# Environment variables:
|
|
||||||
# MACOS_ARCH — Target arch: arm64 (default) or x86_64
|
|
||||||
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
|
|
||||||
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
|
|
||||||
# REBUILD_IMAGE — Set to 1 to force a fresh docker build (same as --rebuild-image)
|
|
||||||
#
|
|
||||||
# Toolchain: osxcross (Clang + Apple ld)
|
|
||||||
# vcpkg triplets: arm64-osx-cross (arm64) / x64-osx-cross (x86_64)
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
||||||
|
|
||||||
IMAGE_NAME="wowee-builder-macos"
|
|
||||||
MACOS_ARCH="${MACOS_ARCH:-arm64}"
|
|
||||||
BUILD_OUTPUT="${PROJECT_ROOT}/build/macos"
|
|
||||||
|
|
||||||
# Parse arguments
|
|
||||||
REBUILD_IMAGE="${REBUILD_IMAGE:-0}"
|
|
||||||
for arg in "$@"; do
|
|
||||||
case "$arg" in
|
|
||||||
--rebuild-image) REBUILD_IMAGE=1 ;;
|
|
||||||
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Validate arch
|
|
||||||
if [[ "$MACOS_ARCH" != "arm64" && "$MACOS_ARCH" != "x86_64" ]]; then
|
|
||||||
echo "Error: MACOS_ARCH must be 'arm64' or 'x86_64' (got: ${MACOS_ARCH})" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify Docker is available
|
|
||||||
if ! command -v docker &>/dev/null; then
|
|
||||||
echo "Error: docker is not installed or not in PATH." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build the image (skip if already present and --rebuild-image not given)
|
|
||||||
if [[ "$REBUILD_IMAGE" == "1" ]] || ! docker image inspect "$IMAGE_NAME" &>/dev/null; then
|
|
||||||
echo "==> Building Docker image: ${IMAGE_NAME}"
|
|
||||||
echo " (SDK will be fetched automatically from Apple's catalog)"
|
|
||||||
docker build \
|
|
||||||
-f "${SCRIPT_DIR}/builder-macos.Dockerfile" \
|
|
||||||
-t "$IMAGE_NAME" \
|
|
||||||
"${SCRIPT_DIR}"
|
|
||||||
else
|
|
||||||
echo "==> Using existing Docker image: ${IMAGE_NAME}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create output directory on the host
|
|
||||||
mkdir -p "$BUILD_OUTPUT"
|
|
||||||
|
|
||||||
echo "==> Starting macOS cross-compile build (arch=${MACOS_ARCH}, output: ${BUILD_OUTPUT})"
|
|
||||||
|
|
||||||
docker run --rm \
|
|
||||||
--mount "type=bind,src=${PROJECT_ROOT},dst=/src,readonly" \
|
|
||||||
--mount "type=bind,src=${BUILD_OUTPUT},dst=/out" \
|
|
||||||
--env "MACOS_ARCH=${MACOS_ARCH}" \
|
|
||||||
${WOWEE_FFX_SDK_REPO:+--env "WOWEE_FFX_SDK_REPO=${WOWEE_FFX_SDK_REPO}"} \
|
|
||||||
${WOWEE_FFX_SDK_REF:+--env "WOWEE_FFX_SDK_REF=${WOWEE_FFX_SDK_REF}"} \
|
|
||||||
"$IMAGE_NAME"
|
|
||||||
|
|
||||||
echo "==> macOS cross-compile build complete. Artifacts in: ${BUILD_OUTPUT}"
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
# run-windows.ps1 — Cross-compile WoWee for Windows (x86_64) inside a Docker container.
|
|
||||||
#
|
|
||||||
# Usage (run from project root):
|
|
||||||
# .\container\run-windows.ps1 [-RebuildImage]
|
|
||||||
#
|
|
||||||
# Environment variables:
|
|
||||||
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
|
|
||||||
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
|
|
||||||
|
|
||||||
param(
|
|
||||||
[switch]$RebuildImage
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
|
||||||
$ProjectRoot = (Resolve-Path "$ScriptDir\..").Path
|
|
||||||
|
|
||||||
$ImageName = "wowee-builder-windows"
|
|
||||||
$BuildOutput = "$ProjectRoot\build\windows"
|
|
||||||
|
|
||||||
# Verify Docker is available
|
|
||||||
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
|
|
||||||
Write-Error "docker is not installed or not in PATH."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Build the image (skip if already present and -RebuildImage not given)
|
|
||||||
$imageExists = docker image inspect $ImageName 2>$null
|
|
||||||
if ($RebuildImage -or -not $imageExists) {
|
|
||||||
Write-Host "==> Building Docker image: $ImageName"
|
|
||||||
docker build `
|
|
||||||
-f "$ScriptDir\builder-windows.Dockerfile" `
|
|
||||||
-t $ImageName `
|
|
||||||
"$ScriptDir"
|
|
||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
||||||
} else {
|
|
||||||
Write-Host "==> Using existing Docker image: $ImageName"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create output directory on the host
|
|
||||||
New-Item -ItemType Directory -Force -Path $BuildOutput | Out-Null
|
|
||||||
|
|
||||||
Write-Host "==> Starting Windows cross-compile build (output: $BuildOutput)"
|
|
||||||
|
|
||||||
$dockerArgs = @(
|
|
||||||
"run", "--rm",
|
|
||||||
"--mount", "type=bind,src=$ProjectRoot,dst=/src,readonly",
|
|
||||||
"--mount", "type=bind,src=$BuildOutput,dst=/out"
|
|
||||||
)
|
|
||||||
|
|
||||||
if ($env:WOWEE_FFX_SDK_REPO) {
|
|
||||||
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REPO=$env:WOWEE_FFX_SDK_REPO")
|
|
||||||
}
|
|
||||||
if ($env:WOWEE_FFX_SDK_REF) {
|
|
||||||
$dockerArgs += @("--env", "WOWEE_FFX_SDK_REF=$env:WOWEE_FFX_SDK_REF")
|
|
||||||
}
|
|
||||||
|
|
||||||
$dockerArgs += $ImageName
|
|
||||||
|
|
||||||
& docker @dockerArgs
|
|
||||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
||||||
|
|
||||||
Write-Host "==> Windows cross-compile build complete. Artifacts in: $BuildOutput"
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# run-windows.sh — Cross-compile WoWee for Windows (x86_64) inside a Docker container.
|
|
||||||
#
|
|
||||||
# Usage (run from project root):
|
|
||||||
# ./container/run-windows.sh [--rebuild-image]
|
|
||||||
#
|
|
||||||
# Environment variables:
|
|
||||||
# WOWEE_FFX_SDK_REPO — FidelityFX SDK git repo URL (passed through to container)
|
|
||||||
# WOWEE_FFX_SDK_REF — FidelityFX SDK git ref / tag (passed through to container)
|
|
||||||
# REBUILD_IMAGE — Set to 1 to force a fresh docker build (same as --rebuild-image)
|
|
||||||
#
|
|
||||||
# Toolchain: LLVM-MinGW (Clang + LLD) targeting x86_64-w64-mingw32-ucrt
|
|
||||||
# vcpkg triplet: x64-mingw-static
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
||||||
|
|
||||||
IMAGE_NAME="wowee-builder-windows"
|
|
||||||
BUILD_OUTPUT="${PROJECT_ROOT}/build/windows"
|
|
||||||
|
|
||||||
# Parse arguments
|
|
||||||
REBUILD_IMAGE="${REBUILD_IMAGE:-0}"
|
|
||||||
for arg in "$@"; do
|
|
||||||
case "$arg" in
|
|
||||||
--rebuild-image) REBUILD_IMAGE=1 ;;
|
|
||||||
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Verify Docker is available
|
|
||||||
if ! command -v docker &>/dev/null; then
|
|
||||||
echo "Error: docker is not installed or not in PATH." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build the image (skip if already present and --rebuild-image not given)
|
|
||||||
if [[ "$REBUILD_IMAGE" == "1" ]] || ! docker image inspect "$IMAGE_NAME" &>/dev/null; then
|
|
||||||
echo "==> Building Docker image: ${IMAGE_NAME}"
|
|
||||||
docker build \
|
|
||||||
-f "${SCRIPT_DIR}/builder-windows.Dockerfile" \
|
|
||||||
-t "$IMAGE_NAME" \
|
|
||||||
"${SCRIPT_DIR}"
|
|
||||||
else
|
|
||||||
echo "==> Using existing Docker image: ${IMAGE_NAME}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create output directory on the host
|
|
||||||
mkdir -p "$BUILD_OUTPUT"
|
|
||||||
|
|
||||||
echo "==> Starting Windows cross-compile build (output: ${BUILD_OUTPUT})"
|
|
||||||
|
|
||||||
docker run --rm \
|
|
||||||
--mount "type=bind,src=${PROJECT_ROOT},dst=/src,readonly" \
|
|
||||||
--mount "type=bind,src=${BUILD_OUTPUT},dst=/out" \
|
|
||||||
${WOWEE_FFX_SDK_REPO:+--env "WOWEE_FFX_SDK_REPO=${WOWEE_FFX_SDK_REPO}"} \
|
|
||||||
${WOWEE_FFX_SDK_REF:+--env "WOWEE_FFX_SDK_REF=${WOWEE_FFX_SDK_REF}"} \
|
|
||||||
"$IMAGE_NAME"
|
|
||||||
|
|
||||||
echo "==> Windows cross-compile build complete. Artifacts in: ${BUILD_OUTPUT}"
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
# Animation System
|
|
||||||
|
|
||||||
Unified, FSM-based animation system for all characters (players, NPCs, companions).
|
|
||||||
Every character uses the same `CharacterAnimator` — there is no separate NPC/Mob animator.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
AnimationController (thin adapter — bridges Renderer ↔ CharacterAnimator)
|
|
||||||
└─ CharacterAnimator (FSM composer — implements ICharacterAnimator)
|
|
||||||
├─ CombatFSM (stun, hit reaction, spell cast, melee, ranged, charge)
|
|
||||||
├─ ActivityFSM (emote, loot, sit/stand/kneel/sleep)
|
|
||||||
├─ LocomotionFSM (idle, walk, run, sprint, jump, swim, strafe)
|
|
||||||
└─ MountFSM (mount idle, mount run, flight)
|
|
||||||
|
|
||||||
AnimationManager (registry of CharacterAnimator instances by ID)
|
|
||||||
AnimCapabilitySet (probed once per model — cached resolved anim IDs)
|
|
||||||
AnimCapabilityProbe (queries which animations a model supports)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Priority Resolution
|
|
||||||
|
|
||||||
`CharacterAnimator::resolveAnimation()` runs every frame. The first FSM to
|
|
||||||
return a valid `AnimOutput` wins:
|
|
||||||
|
|
||||||
1. **Mount** — if mounted, return `MOUNT` (overrides everything)
|
|
||||||
2. **Combat** — stun > hit reaction > spell > charge > melee/ranged > combat idle
|
|
||||||
3. **Activity** — emote > loot > sit/stand transitions
|
|
||||||
4. **Locomotion** — run/walk/sprint/jump/swim/strafe/idle
|
|
||||||
|
|
||||||
If no FSM produces a valid output, the last animation continues (STAY policy).
|
|
||||||
|
|
||||||
### Overlay Layer
|
|
||||||
|
|
||||||
After resolution, `applyOverlays()` substitutes stealth animation variants
|
|
||||||
(stealth idle, stealth walk, stealth run) without changing sub-FSM state.
|
|
||||||
|
|
||||||
## File Map
|
|
||||||
|
|
||||||
### Headers (`include/rendering/animation/`)
|
|
||||||
|
|
||||||
| File | Purpose |
|
|
||||||
|---|---|
|
|
||||||
| `i_animator.hpp` | Base interface: `onEvent()`, `update()` |
|
|
||||||
| `i_character_animator.hpp` | 20 virtual methods (combat, spells, emotes, mounts, etc.) |
|
|
||||||
| `character_animator.hpp` | FSM composer — the single animator class |
|
|
||||||
| `locomotion_fsm.hpp` | Movement states: idle, walk, run, sprint, jump, swim |
|
|
||||||
| `combat_fsm.hpp` | Combat states: melee, ranged, spell cast, stun, hit reaction |
|
|
||||||
| `activity_fsm.hpp` | Activity states: emote, loot, sit/stand/kneel |
|
|
||||||
| `mount_fsm.hpp` | Mount states: idle, run, flight, taxi |
|
|
||||||
| `anim_capability_set.hpp` | Probed capability flags + resolved animation IDs |
|
|
||||||
| `anim_capability_probe.hpp` | Probes a model for available animations |
|
|
||||||
| `anim_event.hpp` | `AnimEvent` enum (MOVE_START, MOVE_STOP, JUMP, etc.) |
|
|
||||||
| `animation_manager.hpp` | Central registry of CharacterAnimator instances |
|
|
||||||
| `weapon_type.hpp` | WeaponLoadout, RangedWeaponType enums |
|
|
||||||
| `emote_registry.hpp` | Emote name → animation ID lookup |
|
|
||||||
| `footstep_driver.hpp` | Footstep sound event driver |
|
|
||||||
| `sfx_state_driver.hpp` | State-transition SFX (jump, land, swim enter/exit) |
|
|
||||||
| `i_anim_renderer.hpp` | Interface for renderer animation queries |
|
|
||||||
|
|
||||||
### Sources (`src/rendering/animation/`)
|
|
||||||
|
|
||||||
| File | Purpose |
|
|
||||||
|---|---|
|
|
||||||
| `character_animator.cpp` | ICharacterAnimator implementation + priority resolver |
|
|
||||||
| `locomotion_fsm.cpp` | Locomotion state transitions + resolve logic |
|
|
||||||
| `combat_fsm.cpp` | Combat state transitions + resolve logic |
|
|
||||||
| `activity_fsm.cpp` | Activity state transitions + resolve logic |
|
|
||||||
| `mount_fsm.cpp` | Mount state transitions + resolve logic |
|
|
||||||
| `anim_capability_probe.cpp` | Model animation probing |
|
|
||||||
| `animation_manager.cpp` | Registry CRUD + bulk update |
|
|
||||||
| `emote_registry.cpp` | Emote database |
|
|
||||||
| `footstep_driver.cpp` | Footstep timing logic |
|
|
||||||
| `sfx_state_driver.cpp` | SFX transition detection |
|
|
||||||
|
|
||||||
### Controller (`include/rendering/animation_controller.hpp` + `src/rendering/animation_controller.cpp`)
|
|
||||||
|
|
||||||
Thin adapter that:
|
|
||||||
- Collects per-frame input from camera/renderer → `CharacterAnimator::FrameInput`
|
|
||||||
- Forwards state changes (combat, emote, spell, mount, etc.) → `CharacterAnimator`
|
|
||||||
- Reads `AnimOutput` → applies via `CharacterRenderer`
|
|
||||||
- Owns footstep and SFX drivers
|
|
||||||
|
|
||||||
## Key Types
|
|
||||||
|
|
||||||
- **`AnimEvent`** — discrete events: `MOVE_START`, `MOVE_STOP`, `JUMP`, `LAND`, `MOUNT`, `DISMOUNT`, etc.
|
|
||||||
- **`AnimOutput`** — result of FSM resolution: `{animId, loop, valid}`. `valid=false` means STAY.
|
|
||||||
- **`AnimCapabilitySet`** — probed once per model load. Caches resolved IDs and capability flags.
|
|
||||||
- **`CharacterAnimator::FrameInput`** — per-frame input struct (movement flags, timers, animation state queries).
|
|
||||||
|
|
||||||
## Adding a New Animation State
|
|
||||||
|
|
||||||
1. Decide which FSM owns the state (combat, activity, locomotion, or mount).
|
|
||||||
2. Add the state enum to the FSM's `State` enum.
|
|
||||||
3. Add transitions in the FSM's `resolve()` method.
|
|
||||||
4. Add resolved ID fields to `AnimCapabilitySet` if the animation needs model probing.
|
|
||||||
5. If the state needs external triggering, add a method to `ICharacterAnimator` and implement in `CharacterAnimator`.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Each FSM has its own test file in `tests/`:
|
|
||||||
- `test_locomotion_fsm.cpp`
|
|
||||||
- `test_combat_fsm.cpp`
|
|
||||||
- `test_activity_fsm.cpp`
|
|
||||||
- `test_anim_capability.cpp`
|
|
||||||
|
|
||||||
Run all tests:
|
|
||||||
```bash
|
|
||||||
cd build && ctest --output-on-failure
|
|
||||||
```
|
|
||||||
|
|
@ -93,16 +93,13 @@ The RSA public modulus is extracted from WoW.exe (`.rdata` section at offset 0x0
|
||||||
## Key Files
|
## Key Files
|
||||||
|
|
||||||
```
|
```
|
||||||
include/game/warden_handler.hpp - Packet handler interface
|
|
||||||
src/game/warden_handler.cpp - handleWardenData + module manager init
|
|
||||||
include/game/warden_module.hpp - Module loader interface
|
include/game/warden_module.hpp - Module loader interface
|
||||||
src/game/warden_module.cpp - 8-step pipeline
|
src/game/warden_module.cpp - 8-step pipeline
|
||||||
include/game/warden_emulator.hpp - Emulator interface
|
include/game/warden_emulator.hpp - Emulator interface
|
||||||
src/game/warden_emulator.cpp - Unicorn Engine executor + API hooks
|
src/game/warden_emulator.cpp - Unicorn Engine executor + API hooks
|
||||||
include/game/warden_crypto.hpp - Crypto interface
|
include/game/warden_crypto.hpp - Crypto interface
|
||||||
src/game/warden_crypto.cpp - RC4 / key derivation
|
src/game/warden_crypto.cpp - RC4 / key derivation
|
||||||
include/game/warden_memory.hpp - PE image + memory patch interface
|
src/game/game_handler.cpp - Packet handler (handleWardenData)
|
||||||
src/game/warden_memory.cpp - PE loader, runtime globals patching
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -58,11 +58,10 @@ strict Warden enforcement in that mode.
|
||||||
## Key Files
|
## Key Files
|
||||||
|
|
||||||
```
|
```
|
||||||
include/game/warden_handler.hpp + src/game/warden_handler.cpp - Packet handler
|
src/game/warden_module.hpp/cpp - Module loader (8-step pipeline)
|
||||||
include/game/warden_module.hpp + src/game/warden_module.cpp - Module loader (8-step pipeline)
|
src/game/warden_emulator.hpp/cpp - Unicorn Engine executor
|
||||||
include/game/warden_emulator.hpp + src/game/warden_emulator.cpp - Unicorn Engine executor
|
src/game/warden_crypto.hpp/cpp - RC4/MD5/SHA1/RSA crypto
|
||||||
include/game/warden_crypto.hpp + src/game/warden_crypto.cpp - RC4/MD5/SHA1/RSA crypto
|
src/game/game_handler.cpp - Packet handler (handleWardenData)
|
||||||
include/game/warden_memory.hpp + src/game/warden_memory.cpp - PE image + memory patching
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ Wowee follows a modular architecture with clear separation of concerns:
|
||||||
┌─────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────┐
|
||||||
│ Application (main loop) │
|
│ Application (main loop) │
|
||||||
│ - State management (auth/realms/game) │
|
│ - State management (auth/realms/game) │
|
||||||
│ - Update cycle │
|
│ - Update cycle (60 FPS) │
|
||||||
│ - Event dispatch │
|
│ - Event dispatch │
|
||||||
└──────────────┬──────────────────────────────┘
|
└──────────────┬──────────────────────────────┘
|
||||||
│
|
│
|
||||||
|
|
@ -16,8 +16,8 @@ Wowee follows a modular architecture with clear separation of concerns:
|
||||||
│ │
|
│ │
|
||||||
┌──────▼──────┐ ┌─────▼──────┐
|
┌──────▼──────┐ ┌─────▼──────┐
|
||||||
│ Window │ │ Input │
|
│ Window │ │ Input │
|
||||||
│ (SDL2 + │ │ (Keyboard/ │
|
│ (SDL2) │ │ (Keyboard/ │
|
||||||
│ Vulkan) │ │ Mouse) │
|
│ │ │ Mouse) │
|
||||||
└──────┬──────┘ └─────┬──────┘
|
└──────┬──────┘ └─────┬──────┘
|
||||||
│ │
|
│ │
|
||||||
└───────┬────────┘
|
└───────┬────────┘
|
||||||
|
|
@ -26,294 +26,517 @@ Wowee follows a modular architecture with clear separation of concerns:
|
||||||
│ │
|
│ │
|
||||||
┌───▼────────┐ ┌───────▼──────┐
|
┌───▼────────┐ ┌───────▼──────┐
|
||||||
│ Renderer │ │ UI Manager │
|
│ Renderer │ │ UI Manager │
|
||||||
│ (Vulkan) │ │ (ImGui) │
|
│ (OpenGL) │ │ (ImGui) │
|
||||||
└───┬────────┘ └──────────────┘
|
└───┬────────┘ └──────────────┘
|
||||||
│
|
│
|
||||||
├─ Camera + CameraController
|
├─ Camera
|
||||||
├─ TerrainRenderer (ADT streaming)
|
├─ Scene Graph
|
||||||
├─ WMORenderer (buildings, collision)
|
├─ Shaders
|
||||||
├─ M2Renderer (models, particles, ribbons)
|
├─ Meshes
|
||||||
├─ CharacterRenderer (skeletal animation)
|
└─ Textures
|
||||||
├─ WaterRenderer (refraction, lava, slime)
|
|
||||||
├─ SkyBox + StarField + Weather
|
|
||||||
├─ LightingManager (Light.dbc volumes)
|
|
||||||
└─ SwimEffects, ChargeEffect, Lightning
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Core Systems
|
## Core Systems
|
||||||
|
|
||||||
### 1. Application Layer (`src/core/`)
|
### 1. Application Layer (`src/core/`)
|
||||||
|
|
||||||
**Application** (`application.hpp/cpp`) - Main controller
|
**Application** - Main controller
|
||||||
- Owns all subsystems (renderer, game handler, asset manager, UI)
|
- Owns all subsystems
|
||||||
- Manages application state (AUTH → REALM_SELECT → CHAR_SELECT → IN_WORLD)
|
- Manages application state
|
||||||
- Runs update/render loop
|
- Runs update/render loop
|
||||||
- Populates `GameServices` struct and passes to `GameHandler` at construction
|
- Handles lifecycle (init/shutdown)
|
||||||
|
|
||||||
**Window** (`window.hpp/cpp`) - SDL2 + Vulkan wrapper
|
**Window** - SDL2 wrapper
|
||||||
- Creates SDL2 window with Vulkan surface
|
- Creates window and OpenGL context
|
||||||
- Owns `VkContext` (Vulkan device, swapchain, render passes)
|
|
||||||
- Handles resize events
|
- Handles resize events
|
||||||
|
- Manages VSync and fullscreen
|
||||||
|
|
||||||
**Input** (`input.hpp/cpp`) - Input management
|
**Input** - Input management
|
||||||
- Keyboard state tracking (SDL scancodes)
|
- Keyboard state tracking
|
||||||
- Mouse position, buttons (1-based SDL indices), wheel delta
|
- Mouse position and buttons
|
||||||
- Per-frame delta calculation
|
- Mouse locking for camera control
|
||||||
|
|
||||||
**Logger** (`logger.hpp/cpp`) - Thread-safe logging
|
**Logger** - Logging system
|
||||||
|
- Thread-safe logging
|
||||||
- Multiple log levels (DEBUG, INFO, WARNING, ERROR, FATAL)
|
- Multiple log levels (DEBUG, INFO, WARNING, ERROR, FATAL)
|
||||||
- File output to `logs/wowee.log`
|
- Timestamp formatting
|
||||||
- Configurable via `WOWEE_LOG_LEVEL` env var
|
|
||||||
|
|
||||||
### 2. Rendering System (`src/rendering/`)
|
### 2. Rendering System (`src/rendering/`)
|
||||||
|
|
||||||
**Renderer** (`renderer.hpp/cpp`) - Main rendering coordinator
|
**Renderer** - Main rendering coordinator
|
||||||
- Manages Vulkan pipeline state
|
- Manages OpenGL state
|
||||||
- Coordinates frame rendering across all sub-renderers
|
- Coordinates frame rendering
|
||||||
- Owns camera, sky, weather, lighting, and all sub-renderers
|
- Owns camera and scene
|
||||||
- Shadow mapping with PCF filtering
|
|
||||||
|
|
||||||
**VkContext** (`vk_context.hpp/cpp`) - Vulkan infrastructure
|
**Camera** - View/projection matrices
|
||||||
- Device selection, queue families, swapchain
|
|
||||||
- Render passes, framebuffers, command pools
|
|
||||||
- Sampler cache (FNV-1a hashed dedup)
|
|
||||||
- Pipeline cache persistence for fast startup
|
|
||||||
|
|
||||||
**Camera** (`camera.hpp/cpp`) - View/projection matrices
|
|
||||||
- Position and orientation
|
- Position and orientation
|
||||||
- FOV, aspect ratio, near/far planes
|
- FOV and aspect ratio
|
||||||
- Sub-pixel jitter for TAA/FSR2 (column 2 NDC offset)
|
- View frustum (for culling)
|
||||||
- Frustum extraction for culling
|
|
||||||
|
|
||||||
**TerrainRenderer** - ADT terrain streaming
|
**Scene** - Scene graph
|
||||||
- Async chunk loading within configurable radius
|
- Mesh collection
|
||||||
- 4-layer texture splatting with alpha blending
|
- Spatial organization
|
||||||
- Frustum + distance culling
|
- Visibility determination
|
||||||
- Vegetation/foliage placement via deterministic RNG
|
|
||||||
|
|
||||||
**WMORenderer** - World Map Objects (buildings)
|
**Shader** - GLSL program wrapper
|
||||||
- Multi-material batch rendering
|
- Loads vertex/fragment shaders
|
||||||
- Portal-based visibility culling
|
- Uniform management
|
||||||
- Floor/wall collision (normal-based classification)
|
- Compilation and linking
|
||||||
- Interior glass transparency, doodad placement
|
|
||||||
|
|
||||||
**M2Renderer** - Models (creatures, doodads, spell effects)
|
**Mesh** - Geometry container
|
||||||
- Skeletal animation with GPU bone transforms
|
- Vertex buffer (position, normal, texcoord)
|
||||||
- Particle emitters (WotLK FBlock format)
|
- Index buffer
|
||||||
- Ribbon emitters (charge trails, enchant glows)
|
- VAO/VBO/EBO management
|
||||||
- Portal spin effects, foliage wind displacement
|
|
||||||
- Per-instance animation state
|
|
||||||
|
|
||||||
**CharacterRenderer** - Player/NPC character models
|
**Texture** - Texture management
|
||||||
- GPU vertex skinning (256 bones)
|
- Loading (BLP via `AssetManager`, optional PNG overrides for development)
|
||||||
- Race/gender-aware textures via CharSections.dbc
|
- OpenGL texture object
|
||||||
- Equipment rendering (geoset visibility per slot)
|
- Mipmap generation
|
||||||
- Fallback textures (white/transparent/flat-normal) for missing assets
|
|
||||||
|
|
||||||
**WaterRenderer** - Terrain and WMO water
|
**Material** - Surface properties
|
||||||
- Refraction/reflection rendering
|
- Shader assignment
|
||||||
- Magma/slime with multi-octave FBM noise flow
|
- Texture binding
|
||||||
- Beer-Lambert absorption
|
- Color/properties
|
||||||
|
|
||||||
**Skybox + StarField + Weather**
|
|
||||||
- Procedural sky dome with time-of-day lighting
|
|
||||||
- Star field with day/night fade (dusk 18:00–20:00, dawn 04:00–06:00)
|
|
||||||
- Rain/snow particle systems per zone (via zone weather table)
|
|
||||||
|
|
||||||
**LightingManager** - Light.dbc volume sampling
|
|
||||||
- Time-of-day color bands (half-minutes, 0–2879)
|
|
||||||
- Distance-weighted light volume blending
|
|
||||||
- Fog color/distance parameters
|
|
||||||
|
|
||||||
### 3. Networking (`src/network/`)
|
### 3. Networking (`src/network/`)
|
||||||
|
|
||||||
**TCPSocket** (`tcp_socket.hpp/cpp`) - Platform TCP
|
**Socket** (Abstract base class)
|
||||||
- Non-blocking I/O with per-frame recv budgets
|
- Connection interface
|
||||||
- 4 KB recv buffer per call
|
- Packet send/receive
|
||||||
- Portable across Linux/macOS/Windows
|
- Callback system
|
||||||
|
|
||||||
**WorldSocket** (`world_socket.hpp/cpp`) - WoW world connection
|
**TCPSocket** - Linux TCP sockets
|
||||||
- RC4 header encryption (derived from SRP session key)
|
- Non-blocking I/O
|
||||||
- Packet parsing with configurable per-frame budgets
|
- Raw TCP (replaces WebSocket)
|
||||||
- Compressed move packet handling
|
- Packet framing
|
||||||
|
|
||||||
**Packet** (`packet.hpp/cpp`) - Binary data container
|
**Packet** - Binary data container
|
||||||
- Read/write primitives (uint8–uint64, float, string, packed GUID)
|
- Read/write primitives
|
||||||
- Bounds-checked reads (return 0 past end)
|
- Byte order handling
|
||||||
|
- Opcode management
|
||||||
|
|
||||||
### 4. Authentication (`src/auth/`)
|
### 4. Authentication (`src/auth/`)
|
||||||
|
|
||||||
**AuthHandler** - Auth server protocol (port 3724)
|
**AuthHandler** - Auth server protocol
|
||||||
- SRP6a challenge/proof flow
|
- Connects to port 3724
|
||||||
- Security flags: PIN (0x01), Matrix (0x02), Authenticator (0x04)
|
- SRP authentication flow
|
||||||
- Realm list retrieval
|
- Session key generation
|
||||||
|
|
||||||
**SRP** (`srp.hpp/cpp`) - Secure Remote Password
|
**SRP** - Secure Remote Password
|
||||||
- SRP6a with 19-byte (152-bit) ephemeral
|
- SRP6a algorithm
|
||||||
- OpenSSL BIGNUM math
|
- Big integer math
|
||||||
- Session key generation (40 bytes)
|
- Salt and verifier generation
|
||||||
|
|
||||||
**Integrity** - Client integrity verification
|
**Crypto** - Cryptographic functions
|
||||||
- Checksum computation for Warden compatibility
|
- SHA1 hashing (OpenSSL)
|
||||||
|
- Random number generation
|
||||||
|
- Encryption helpers
|
||||||
|
|
||||||
### 5. Game Logic (`src/game/`)
|
### 5. Game Logic (`src/game/`)
|
||||||
|
|
||||||
**GameHandler** (`game_handler.hpp/cpp`) - Central game state
|
**GameHandler** - World server protocol
|
||||||
- Dispatch table routing 664+ opcodes to domain handlers
|
- Connects to port 8085 (configurable)
|
||||||
- Owns all domain handlers via composition
|
- Packet handlers for 100+ opcodes
|
||||||
- Receives dependencies via `GameServices` struct (no singleton access)
|
- Session management with RC4 encryption
|
||||||
|
- Character enumeration and login flow
|
||||||
|
|
||||||
**Domain Handlers** (SOLID decomposition from GameHandler):
|
**World** - Game world state
|
||||||
- `EntityController` - UPDATE_OBJECT parsing, entity spawn/despawn
|
- Map loading with async terrain streaming
|
||||||
- `MovementHandler` - Movement packets, speed, taxi, swimming, flying
|
- Entity management (players, NPCs, creatures)
|
||||||
- `CombatHandler` - Damage, healing, death, auto-attack, threat
|
- Zone management and exploration
|
||||||
- `SpellHandler` - Spell casting, cooldowns, auras, talents, pet spells
|
- Time-of-day synchronization
|
||||||
- `InventoryHandler` - Equipment, bags, bank, mail, auction, vendors
|
|
||||||
- `QuestHandler` - Quest accept/complete, objectives, progress tracking
|
|
||||||
- `SocialHandler` - Party, guild, LFG, friends, who, duel, trade
|
|
||||||
- `ChatHandler` - Chat messages, channels, emotes, system messages
|
|
||||||
- `WardenHandler` - Anti-cheat module management
|
|
||||||
|
|
||||||
**OpcodeTable** - Expansion-agnostic opcode mapping
|
**Player** - Player character
|
||||||
- `LogicalOpcode` enum → wire opcode via JSON config per expansion
|
- Position and movement (WASD + spline movement)
|
||||||
- Runtime remapping for Classic/TBC/WotLK/Turtle protocol differences
|
- Stats tracking (health, mana, XP, level)
|
||||||
|
- Equipment and inventory (23 + 16 slots)
|
||||||
|
- Action queue and spell casting
|
||||||
|
- Death and resurrection handling
|
||||||
|
|
||||||
**Entity / EntityManager** - Entity lifecycle
|
**Character** - Character data
|
||||||
- Shared entity base class with update fields (uint32 array)
|
- Race, class, gender, appearance
|
||||||
- Player, Unit, GameObject subtypes
|
- Creation and customization
|
||||||
- GUID-based lookup, field extraction (health, level, display ID, etc.)
|
- 3D model preview
|
||||||
|
- Online character lifecycle and state synchronization
|
||||||
|
|
||||||
**TransportManager** - Transport path evaluation
|
**Entity** - Game entities
|
||||||
- Catmull-Rom spline interpolation from TransportAnimation.dbc
|
- NPCs and creatures with display info
|
||||||
- Clock-based motion with server time synchronization
|
- Animation state (idle, combat, walk, run)
|
||||||
- Time-closed looping paths (wrap point duplicated, no index wrapping)
|
- GUID management (player, creature, item, gameobject)
|
||||||
|
- Targeting and selection
|
||||||
|
|
||||||
**Expansion Helpers** (`game_utils.hpp`):
|
**Inventory** - Item management
|
||||||
- `isActiveExpansion("classic")` / `isActiveExpansion("tbc")` / `isActiveExpansion("wotlk")`
|
- Equipment slots (head, shoulders, chest, etc.)
|
||||||
- `isClassicLikeExpansion()` (Classic or Turtle WoW)
|
- Backpack storage (16 slots)
|
||||||
- `isPreWotlk()` (Classic, Turtle, or TBC)
|
- Item metadata (icons, stats, durability)
|
||||||
|
- Drag-drop system
|
||||||
|
- Auto-equip and unequip
|
||||||
|
|
||||||
|
**NPC Interactions** - handled through `GameHandler`
|
||||||
|
- Gossip system
|
||||||
|
- Quest givers with markers (! and ?)
|
||||||
|
- Vendors (buy/sell)
|
||||||
|
- Trainers (placeholder)
|
||||||
|
- Combat animations
|
||||||
|
|
||||||
|
**ZoneManager** - Zone and area tracking
|
||||||
|
- Map exploration
|
||||||
|
- Area discovery
|
||||||
|
- Zone change detection
|
||||||
|
|
||||||
|
**Opcodes** - Protocol definitions
|
||||||
|
- 100+ Client→Server opcodes (CMSG_*)
|
||||||
|
- 100+ Server→Client opcodes (SMSG_*)
|
||||||
|
- WoW 3.3.5a (build 12340) specific
|
||||||
|
|
||||||
### 6. Asset Pipeline (`src/pipeline/`)
|
### 6. Asset Pipeline (`src/pipeline/`)
|
||||||
|
|
||||||
**AssetManager** - Runtime asset access
|
**AssetManager** - Runtime asset access
|
||||||
- Extracted loose-file tree indexed by `Data/manifest.json`
|
- Loads an extracted loose-file tree indexed by `Data/manifest.json`
|
||||||
- Layered resolution via optional overlay manifests (multi-expansion dedup)
|
- Layered resolution via optional overlay manifests (multi-expansion dedup)
|
||||||
- File cache with configurable budget (256 MB min, 12 GB max)
|
- File cache + path normalization
|
||||||
- PNG override support (checks for .png before .blp)
|
|
||||||
|
|
||||||
**asset_extract (tool)** - MPQ extraction
|
**asset_extract (tool)** - MPQ extraction
|
||||||
- Uses StormLib to extract MPQs into `Data/` and generate `manifest.json`
|
- Uses StormLib to extract MPQs into `Data/` and generate `manifest.json`
|
||||||
- Driven by `extract_assets.sh` / `extract_assets.ps1`
|
- Driven by `extract_assets.sh`
|
||||||
|
|
||||||
**BLPLoader** - Texture decompression
|
**BLPLoader** - Texture parser
|
||||||
- DXT1/3/5 block compression (RGB565 color endpoints)
|
- BLP format (Blizzard texture format)
|
||||||
- Palette mode with 1/4/8-bit alpha
|
- DXT1/3/5 compression support
|
||||||
- Mipmap extraction
|
- Mipmap extraction and generation
|
||||||
|
- OpenGL texture object creation
|
||||||
|
|
||||||
**M2Loader** - Model binary parsing
|
**M2Loader** - Model parser
|
||||||
- Version-aware header (Classic v256 vs WotLK v264)
|
- Character/creature models with materials
|
||||||
- Skeletal animation tracks (embedded vs external .anim files, flag 0x20)
|
- Skeletal animation data (256 bones max)
|
||||||
- Compressed quaternions (int16 offset mapping)
|
- Bone hierarchies and transforms
|
||||||
- Particle emitters, ribbon emitters, attachment points
|
- Animation sequences (idle, walk, run, attack, etc.)
|
||||||
- Geoset support (group × 100 + variant encoding)
|
- Particle emitters (WotLK FBlock format)
|
||||||
|
- Attachment points (weapons, mounts, etc.)
|
||||||
|
- Geoset support (hide/show body parts)
|
||||||
|
- Multiple texture units and render batches
|
||||||
|
|
||||||
**WMOLoader** - World object parsing
|
**WMOLoader** - World object parser
|
||||||
- Multi-group rendering with portal visibility
|
- Buildings and structures
|
||||||
- Doodad placement (24-bit name index + 8-bit flags packing)
|
- Multi-material batches
|
||||||
- Liquid data, collision geometry
|
- Portal system (visibility culling)
|
||||||
|
- Doodad placement (decorations)
|
||||||
|
- Group-based rendering
|
||||||
|
- Liquid data (indoor water)
|
||||||
|
|
||||||
**ADTLoader** - Terrain parsing
|
**ADTLoader** - Terrain parser
|
||||||
- 64×64 tiles per map, 16×16 chunks per tile (MCNK)
|
- 64x64 tiles per map (map_XX_YY.adt)
|
||||||
- MCVT height grid (145 vertices: 9 outer + 8 inner per row × 9 rows)
|
- 16x16 chunks per tile (MCNK)
|
||||||
- Texture layers (up to 4 with alpha blending, RLE-compressed alpha maps)
|
- Height map data (9x9 outer + 8x8 inner vertices)
|
||||||
|
- Texture layers (up to 4 per chunk with alpha blending)
|
||||||
|
- Liquid data (water/lava/slime with height and flags)
|
||||||
|
- Object placement (M2 and WMO references)
|
||||||
|
- Terrain holes
|
||||||
- Async loading to prevent frame stalls
|
- Async loading to prevent frame stalls
|
||||||
|
|
||||||
**DBCLoader** - Database table parsing
|
**DBCLoader** - Database parser
|
||||||
- Binary DBC format (fixed 4-byte uint32 fields + string block)
|
- 20+ DBC files loaded (Spell, Item, Creature, SkillLine, Faction, etc.)
|
||||||
- CSV fallback for pre-extracted data
|
- Type-safe record access
|
||||||
- Expansion-aware field layout via `dbc_layouts.json`
|
- String block parsing
|
||||||
- 20+ DBC files: Spell, Item, Creature, Faction, Map, AreaTable, etc.
|
- Memory-efficient caching
|
||||||
|
- Used for:
|
||||||
|
- Spell icons and tooltips (Spell.dbc, SpellIcon.dbc)
|
||||||
|
- Item data (Item.dbc, ItemDisplayInfo.dbc)
|
||||||
|
- Creature display info (CreatureDisplayInfo.dbc, CreatureModelData.dbc)
|
||||||
|
- Class and race info (ChrClasses.dbc, ChrRaces.dbc)
|
||||||
|
- Skill lines (SkillLine.dbc, SkillLineAbility.dbc)
|
||||||
|
- Faction and reputation (Faction.dbc)
|
||||||
|
- Map and area names (Map.dbc, AreaTable.dbc)
|
||||||
|
|
||||||
### 7. UI System (`src/ui/`)
|
### 7. UI System (`src/ui/`)
|
||||||
|
|
||||||
**UIManager** - ImGui coordinator
|
**UIManager** - ImGui coordinator
|
||||||
- ImGui initialization with SDL2/Vulkan backend
|
- ImGui initialization with SDL2/OpenGL backend
|
||||||
- Screen state management and transitions
|
|
||||||
- Event handling and input routing
|
- Event handling and input routing
|
||||||
|
- Render dispatch with opacity control
|
||||||
|
- Screen state management
|
||||||
|
|
||||||
**Screens:**
|
**AuthScreen** - Login interface
|
||||||
- `AuthScreen` - Login with username/password, server address, security code
|
- Username/password input fields
|
||||||
- `RealmScreen` - Realm list with population and type indicators
|
- Server address configuration
|
||||||
- `CharacterScreen` - Character selection with 3D animated preview
|
- Connection status and error messages
|
||||||
- `CharacterCreateScreen` - Race/class/gender/appearance customization
|
|
||||||
- `GameScreen` - Main HUD: chat, action bar, target frame, minimap, nameplates, combat text, tooltips
|
|
||||||
- `InventoryScreen` - Equipment paper doll, backpack, bag windows, item tooltips with stats
|
|
||||||
- `SpellbookScreen` - Tabbed spell list with icons, drag-drop to action bar
|
|
||||||
- `QuestLogScreen` - Quest list with objectives, details, and rewards
|
|
||||||
- `TalentScreen` - Talent tree UI with point allocation
|
|
||||||
- `SettingsScreen` - Graphics presets (LOW/MEDIUM/HIGH/ULTRA), audio, keybindings
|
|
||||||
|
|
||||||
### 8. Audio System (`src/audio/`)
|
**RealmScreen** - Server selection
|
||||||
|
- Realm list display with names and types
|
||||||
|
- Population info (Low/Medium/High/Full)
|
||||||
|
- Realm type indicators (PvP/PvE/RP/RPPvP)
|
||||||
|
- Auto-select for single realm
|
||||||
|
|
||||||
**AudioEngine** - miniaudio-based playback
|
**CharacterScreen** - Character selection
|
||||||
- WAV decode cache (256 entries, LRU eviction)
|
- Character list with 3D animated preview
|
||||||
- 2D and 3D positional audio
|
- Stats panel (level, race, class, location)
|
||||||
- Sample rate preservation (explicit to avoid miniaudio pitch distortion)
|
- Create/delete character buttons
|
||||||
|
- Enter world button
|
||||||
|
- Auto-select for single character
|
||||||
|
|
||||||
**Sound Managers:**
|
**CharacterCreateScreen** - Character creation
|
||||||
- `AmbientSoundManager` - Wind, water, fire, birds, crickets, city ambience, bell tolls
|
- Race selection (all Alliance and Horde races)
|
||||||
- `ActivitySoundManager` - Swimming strokes, jumping, landing
|
- Class selection (class availability by race)
|
||||||
- `MovementSoundManager` - Footsteps (terrain-aware), mount movement
|
- Gender selection
|
||||||
- `MountSoundManager` - Mount-specific movement audio
|
- Appearance customization (face, skin, hair, color, features)
|
||||||
- `MusicManager` - Zone music with day/night variants
|
- Name input with validation
|
||||||
|
- 3D character preview
|
||||||
|
|
||||||
### 9. Warden Anti-Cheat (`src/game/`)
|
**GameScreen** - In-game HUD
|
||||||
|
- Chat window with message history and formatting
|
||||||
|
- Action bar (12 slots with icons, cooldowns, keybindings)
|
||||||
|
- Target frame (name, level, health, hostile/friendly coloring)
|
||||||
|
- Player stats (health, mana/rage/energy)
|
||||||
|
- Minimap with quest markers
|
||||||
|
- Experience bar
|
||||||
|
|
||||||
4-layer architecture:
|
**InventoryScreen** - Inventory management
|
||||||
- `WardenHandler` - Packet handling (SMSG/CMSG_WARDEN_DATA)
|
- Equipment paper doll (23 slots: head, shoulders, chest, etc.)
|
||||||
- `WardenModuleManager` - Module lifecycle and caching
|
- Backpack grid (16 slots)
|
||||||
- `WardenModule` - 8-step pipeline: decrypt (RC4), strip RSA-2048 signature, decompress (zlib), parse PE headers, relocate, resolve imports, execute
|
- Item icons with tooltips
|
||||||
- `WardenEmulator` - Unicorn Engine x86 CPU emulation with Windows API interception
|
- Drag-drop to equip/unequip
|
||||||
- `WardenMemory` - PE image loading with bounds-checked reads, runtime global patching
|
- Item stats and durability
|
||||||
|
- Gold display
|
||||||
|
|
||||||
|
**SpellbookScreen** - Spells and abilities
|
||||||
|
- Tabbed interface (class specialties + General)
|
||||||
|
- Spell icons organized by SkillLine
|
||||||
|
- Spell tooltips (name, rank, cost, cooldown, description)
|
||||||
|
- Drag-drop to action bar
|
||||||
|
- Known spell tracking
|
||||||
|
|
||||||
|
**QuestLogScreen** - Quest tracking
|
||||||
|
- Active quest list
|
||||||
|
- Quest objectives and progress
|
||||||
|
- Quest details (description, objectives, rewards)
|
||||||
|
- Abandon quest button
|
||||||
|
- Quest level and recommended party size
|
||||||
|
|
||||||
|
**TalentScreen** - Talent trees
|
||||||
|
- Placeholder for talent system
|
||||||
|
- Tree visualization (TODO)
|
||||||
|
- Talent point allocation (TODO)
|
||||||
|
|
||||||
|
**Settings Window** - Configuration
|
||||||
|
- UI opacity slider
|
||||||
|
- Graphics options (TODO)
|
||||||
|
- Audio controls (TODO)
|
||||||
|
- Keybinding customization (TODO)
|
||||||
|
|
||||||
|
**Loading Screen** - Map loading progress
|
||||||
|
- Progress bar with percentage
|
||||||
|
- Background image (map-specific, TODO)
|
||||||
|
- Loading tips (TODO)
|
||||||
|
- Shown during world entry and map transitions
|
||||||
|
|
||||||
|
## Data Flow Examples
|
||||||
|
|
||||||
|
### Authentication Flow
|
||||||
|
```
|
||||||
|
User Input (username/password)
|
||||||
|
↓
|
||||||
|
AuthHandler::authenticate()
|
||||||
|
↓
|
||||||
|
SRP::calculateVerifier()
|
||||||
|
↓
|
||||||
|
TCPSocket::send(LOGON_CHALLENGE)
|
||||||
|
↓
|
||||||
|
Server Response (LOGON_CHALLENGE)
|
||||||
|
↓
|
||||||
|
AuthHandler receives packet
|
||||||
|
↓
|
||||||
|
SRP::calculateProof()
|
||||||
|
↓
|
||||||
|
TCPSocket::send(LOGON_PROOF)
|
||||||
|
↓
|
||||||
|
Server Response (LOGON_PROOF) → Success
|
||||||
|
↓
|
||||||
|
Application::setState(REALM_SELECTION)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rendering Flow
|
||||||
|
```
|
||||||
|
Application::render()
|
||||||
|
↓
|
||||||
|
Renderer::beginFrame()
|
||||||
|
├─ glClearColor() - Clear screen
|
||||||
|
└─ glClear() - Clear buffers
|
||||||
|
↓
|
||||||
|
Renderer::renderWorld(world)
|
||||||
|
├─ Update camera matrices
|
||||||
|
├─ Frustum culling
|
||||||
|
├─ For each visible chunk:
|
||||||
|
│ ├─ Bind shader
|
||||||
|
│ ├─ Set uniforms (matrices, lighting)
|
||||||
|
│ ├─ Bind textures
|
||||||
|
│ └─ Mesh::draw() → glDrawElements()
|
||||||
|
└─ For each entity:
|
||||||
|
├─ Calculate bone transforms
|
||||||
|
└─ Render skinned mesh
|
||||||
|
↓
|
||||||
|
UIManager::render()
|
||||||
|
├─ ImGui::NewFrame()
|
||||||
|
├─ Render current UI screen
|
||||||
|
└─ ImGui::Render()
|
||||||
|
↓
|
||||||
|
Renderer::endFrame()
|
||||||
|
↓
|
||||||
|
Window::swapBuffers()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Asset Loading Flow
|
||||||
|
```
|
||||||
|
World::loadMap(mapId)
|
||||||
|
↓
|
||||||
|
AssetManager::readFile("World/Maps/{map}/map.adt")
|
||||||
|
↓
|
||||||
|
ADTLoader::load(adtData)
|
||||||
|
├─ Parse MCNK chunks (terrain)
|
||||||
|
├─ Parse MCLY chunks (textures)
|
||||||
|
├─ Parse MCVT chunks (vertices)
|
||||||
|
└─ Parse MCNR chunks (normals)
|
||||||
|
↓
|
||||||
|
For each texture reference:
|
||||||
|
AssetManager::readFile(texturePath)
|
||||||
|
↓
|
||||||
|
BLPLoader::load(blpData)
|
||||||
|
↓
|
||||||
|
Texture::loadFromMemory(imageData)
|
||||||
|
↓
|
||||||
|
Create Mesh from vertices/normals/texcoords
|
||||||
|
↓
|
||||||
|
Add to Scene
|
||||||
|
↓
|
||||||
|
Renderer draws in next frame
|
||||||
|
```
|
||||||
|
|
||||||
## Threading Model
|
## Threading Model
|
||||||
|
|
||||||
- **Main thread**: Window events, game logic update, rendering
|
Currently **single-threaded** with async operations:
|
||||||
- **Async terrain**: Non-blocking chunk loading (std::async)
|
- Main thread: Window events, update, render
|
||||||
- **Network I/O**: Non-blocking recv in main thread with per-frame budgets
|
- Network I/O: Non-blocking in main thread (event-driven)
|
||||||
- **Normal maps**: Background CPU generation with mutex-protected result queue
|
- Asset loading: Async terrain streaming (non-blocking chunk loads)
|
||||||
- **GPU uploads**: Second Vulkan queue for parallel texture/buffer transfers
|
|
||||||
|
**Async Systems Implemented:**
|
||||||
|
- Terrain streaming loads ADT chunks asynchronously to prevent frame stalls
|
||||||
|
- Network packets processed in batches per frame
|
||||||
|
- UI rendering deferred until after world rendering
|
||||||
|
|
||||||
|
**Future multi-threading opportunities:**
|
||||||
|
- Asset loading thread pool (background texture/model decompression)
|
||||||
|
- Network thread (dedicated for socket I/O)
|
||||||
|
- Physics thread (if collision detection is added)
|
||||||
|
- Audio streaming thread
|
||||||
|
|
||||||
## Memory Management
|
## Memory Management
|
||||||
|
|
||||||
- **Smart pointers**: `std::unique_ptr` / `std::shared_ptr` throughout
|
- **Smart pointers:** Used throughout (std::unique_ptr, std::shared_ptr)
|
||||||
- **RAII**: All Vulkan resources wrapped with proper destructors
|
- **RAII:** All resources (OpenGL, SDL) cleaned up automatically
|
||||||
- **VMA**: Vulkan Memory Allocator for GPU memory
|
- **No manual memory management:** No raw new/delete
|
||||||
- **Object pooling**: Weather particles, combat text entries
|
- **OpenGL resources:** Wrapped in classes with proper destructors
|
||||||
- **DBC caching**: Lazy-loaded mutable caches in const getters
|
|
||||||
|
## Performance Considerations
|
||||||
|
|
||||||
|
### Rendering
|
||||||
|
- **Frustum culling:** Only render visible chunks (terrain and WMO groups)
|
||||||
|
- **Distance culling:** WMO groups culled beyond 160 units
|
||||||
|
- **Batching:** Group draw calls by material and shader
|
||||||
|
- **LOD:** Distance-based level of detail (TODO)
|
||||||
|
- **Occlusion:** Portal-based visibility (WMO system)
|
||||||
|
- **GPU skinning:** Character animation computed on GPU (256 bones)
|
||||||
|
- **Instancing:** Future optimization for repeated models
|
||||||
|
|
||||||
|
### Asset Streaming
|
||||||
|
- **Async loading:** Terrain chunks load asynchronously (prevents frame stalls)
|
||||||
|
- **Lazy loading:** Load chunks as player moves within streaming radius
|
||||||
|
- **Unloading:** Free distant chunks automatically
|
||||||
|
- **Caching:** Keep frequently used assets in memory (textures, models)
|
||||||
|
- **Priority queue:** Load visible chunks first
|
||||||
|
|
||||||
|
### Network
|
||||||
|
- **Non-blocking I/O:** Never stall main thread
|
||||||
|
- **Packet buffering:** Handle multiple packets per frame
|
||||||
|
- **Batch processing:** Process received packets in batches
|
||||||
|
- **RC4 encryption:** Efficient header encryption (minimal overhead)
|
||||||
|
- **Compression:** Some packets are compressed (TODO)
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
- **Smart pointers:** Automatic cleanup, no memory leaks
|
||||||
|
- **Object pooling:** Reuse particle objects (weather system)
|
||||||
|
- **DBC caching:** Load once, access fast
|
||||||
|
- **Texture sharing:** Same texture used by multiple models
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
- **Logging:** All errors logged with context
|
||||||
|
- **Graceful degradation:** Missing assets show placeholder
|
||||||
|
- **State recovery:** Network disconnect → back to auth screen
|
||||||
|
- **No crashes:** Exceptions caught at application level
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Currently hardcoded, future config system:
|
||||||
|
- Window size and fullscreen
|
||||||
|
- Graphics quality settings
|
||||||
|
- Server addresses
|
||||||
|
- Keybindings
|
||||||
|
- Audio volume
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
**Unit Testing** (TODO):
|
||||||
|
- Packet serialization/deserialization
|
||||||
|
- SRP math functions
|
||||||
|
- Asset parsers with sample files
|
||||||
|
- DBC record parsing
|
||||||
|
- Inventory slot calculations
|
||||||
|
|
||||||
|
**Integration Testing** (TODO):
|
||||||
|
- Full auth flow against test server
|
||||||
|
- Realm list retrieval
|
||||||
|
- Character creation and selection
|
||||||
|
- Quest turn-in flow
|
||||||
|
- Vendor transactions
|
||||||
|
|
||||||
|
**Manual Testing:**
|
||||||
|
- Visual verification of rendering (terrain, water, models, particles)
|
||||||
|
- Performance profiling (F1 performance HUD)
|
||||||
|
- Memory leak checking (valgrind)
|
||||||
|
- Online gameplay against AzerothCore/TrinityCore/MaNGOS servers
|
||||||
|
- UI interactions (drag-drop, click events)
|
||||||
|
|
||||||
|
**Current Test Coverage:**
|
||||||
|
- Full authentication flow tested against live servers
|
||||||
|
- Character creation and selection verified
|
||||||
|
- Quest system tested (accept, track, turn-in)
|
||||||
|
- Vendor system tested (buy, sell)
|
||||||
|
- Combat system tested (targeting, auto-attack, spells)
|
||||||
|
- Inventory system tested (equip, unequip, drag-drop)
|
||||||
|
|
||||||
## Build System
|
## Build System
|
||||||
|
|
||||||
**CMake** with modular targets:
|
**CMake:**
|
||||||
- `wowee` - Main executable
|
- Modular target structure
|
||||||
- `asset_extract` - MPQ extraction tool (requires StormLib)
|
- Automatic dependency discovery
|
||||||
- `dbc_to_csv` / `auth_probe` / `blp_convert` - Utility tools
|
- Cross-platform (Linux focus, but portable)
|
||||||
|
- Out-of-source builds
|
||||||
|
|
||||||
**Dependencies:**
|
**Dependencies:**
|
||||||
- SDL2, Vulkan SDK, OpenSSL, GLM, zlib (system)
|
- SDL2 (system)
|
||||||
|
- OpenGL/GLEW (system)
|
||||||
|
- OpenSSL (system)
|
||||||
|
- GLM (system or header-only)
|
||||||
- ImGui (submodule in extern/)
|
- ImGui (submodule in extern/)
|
||||||
- VMA, vk-bootstrap, stb_image (vendored in extern/)
|
- StormLib (system, optional)
|
||||||
- StormLib (system, optional — only for asset_extract)
|
|
||||||
- Unicorn Engine (system, optional — only for Warden emulation)
|
|
||||||
- FFmpeg (system, optional — for video playback)
|
|
||||||
|
|
||||||
**CI**: GitHub Actions for Linux (x86-64, ARM64), Windows (MSYS2), macOS (ARM64)
|
|
||||||
**Container builds**: Docker cross-compilation for Linux, macOS (osxcross), Windows (LLVM-MinGW)
|
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
- **C++20 standard**
|
- **C++20 standard**
|
||||||
- **Namespaces**: `wowee::core`, `wowee::rendering`, `wowee::game`, `wowee::ui`, `wowee::network`, `wowee::auth`, `wowee::audio`, `wowee::pipeline`
|
- **Namespaces:** wowee::core, wowee::rendering, etc.
|
||||||
- **Naming**: PascalCase for classes, camelCase for functions/variables, kPascalCase for constants
|
- **Naming:** PascalCase for classes, camelCase for functions/variables
|
||||||
- **Headers**: `.hpp` extension, `#pragma once`
|
- **Headers:** .hpp extension
|
||||||
- **Commits**: Conventional style (`feat:`, `fix:`, `refactor:`, `docs:`, `perf:`)
|
- **Includes:** Relative to project root
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This architecture provides a solid foundation for a full-featured native WoW client!
|
||||||
|
|
|
||||||
|
|
@ -563,4 +563,5 @@ The client is now ready for character operations and world entry! 🎮
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Implementation Status:** Complete — authentication, character enumeration, and world entry all working.
|
**Implementation Status:** 100% Complete for authentication
|
||||||
|
**Next Milestone:** Character enumeration and world entry
|
||||||
|
|
|
||||||
|
|
@ -397,4 +397,6 @@ The authentication system can now reliably communicate with WoW 3.3.5a servers!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Status:** ✅ Complete and tested against AzerothCore, TrinityCore, Mangos, and Turtle WoW.
|
**Status:** ✅ Complete and tested
|
||||||
|
|
||||||
|
**Next Steps:** Test with live server and implement realm list protocol.
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
# Performance Baseline — WoWee
|
|
||||||
|
|
||||||
> Phase 0.3 deliverable. Measurements taken before any optimization work.
|
|
||||||
> Re-run after each phase to quantify improvement.
|
|
||||||
|
|
||||||
## Tracy Profiler Integration
|
|
||||||
|
|
||||||
Tracy v0.11.1 integrated under `WOWEE_ENABLE_TRACY` CMake option (default: OFF).
|
|
||||||
When enabled, zero-cost zone markers instrument the following critical paths:
|
|
||||||
|
|
||||||
### Instrumented Zones
|
|
||||||
|
|
||||||
| Zone Name | File | Purpose |
|
|
||||||
|-----------|------|---------|
|
|
||||||
| `Application::run` | src/core/application.cpp | Main loop entry |
|
|
||||||
| `Application::update` | src/core/application.cpp | Per-frame game logic |
|
|
||||||
| `Renderer::beginFrame` | src/rendering/renderer.cpp | Vulkan frame begin |
|
|
||||||
| `Renderer::endFrame` | src/rendering/renderer.cpp | Post-process + present |
|
|
||||||
| `Renderer::update` | src/rendering/renderer.cpp | Renderer per-frame update |
|
|
||||||
| `Renderer::renderWorld` | src/rendering/renderer.cpp | Main world draw call |
|
|
||||||
| `Renderer::renderShadowPass` | src/rendering/renderer.cpp | Shadow depth pass |
|
|
||||||
| `PostProcess::execute` | src/rendering/post_process_pipeline.cpp | FSR/FXAA post-process |
|
|
||||||
| `M2::computeBoneMatrices` | src/rendering/m2_renderer.cpp | CPU skeletal animation |
|
|
||||||
| `M2Renderer::update` | src/rendering/m2_renderer.cpp | M2 instance update + culling |
|
|
||||||
| `TerrainManager::update` | src/rendering/terrain_manager.cpp | Terrain streaming logic |
|
|
||||||
| `TerrainManager::processReadyTiles` | src/rendering/terrain_manager.cpp | GPU tile uploads |
|
|
||||||
| `ADTLoader::load` | src/pipeline/adt_loader.cpp | ADT binary parsing |
|
|
||||||
| `AssetManager::loadTexture` | src/pipeline/asset_manager.cpp | BLP texture loading |
|
|
||||||
| `AssetManager::loadDBC` | src/pipeline/asset_manager.cpp | DBC data file loading |
|
|
||||||
| `WorldSocket::update` | src/network/world_socket.cpp | Network packet dispatch |
|
|
||||||
|
|
||||||
`FrameMark` placed at frame boundary in Application::update to track FPS.
|
|
||||||
|
|
||||||
### How to Profile
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build with Tracy enabled
|
|
||||||
mkdir -p build_tracy && cd build_tracy
|
|
||||||
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo -DWOWEE_ENABLE_TRACY=ON
|
|
||||||
cmake --build . --parallel $(nproc)
|
|
||||||
|
|
||||||
# Run the client — Tracy will broadcast on default port (8086)
|
|
||||||
cd bin && ./wowee
|
|
||||||
|
|
||||||
# Connect with Tracy profiler GUI (separate download from https://github.com/wolfpld/tracy/releases)
|
|
||||||
# Or capture from CLI: tracy-capture -o trace.tracy
|
|
||||||
```
|
|
||||||
|
|
||||||
## Baseline Scenarios
|
|
||||||
|
|
||||||
> **TODO:** Record measurements once profiler is connected to a running instance.
|
|
||||||
> Each scenario should record: avg FPS, frame time (p50/p95/p99), and per-zone timings.
|
|
||||||
|
|
||||||
### Scenario 1: Stormwind (Heavy M2/WMO)
|
|
||||||
- **Location:** Stormwind City center
|
|
||||||
- **Load:** Dense M2 models (NPCs, doodads), multiple WMO interiors
|
|
||||||
- **Avg FPS:** _pending_
|
|
||||||
- **Frame time (p50/p95/p99):** _pending_
|
|
||||||
- **Top zones:** _pending_
|
|
||||||
|
|
||||||
### Scenario 2: The Barrens (Heavy Terrain)
|
|
||||||
- **Location:** Central Barrens
|
|
||||||
- **Load:** Many terrain tiles loaded, sparse M2, large draw distance
|
|
||||||
- **Avg FPS:** _pending_
|
|
||||||
- **Frame time (p50/p95/p99):** _pending_
|
|
||||||
- **Top zones:** _pending_
|
|
||||||
|
|
||||||
### Scenario 3: Dungeon Instance (WMO-only)
|
|
||||||
- **Location:** Any dungeon instance (e.g., Deadmines entrance)
|
|
||||||
- **Load:** WMO interior rendering, no terrain
|
|
||||||
- **Avg FPS:** _pending_
|
|
||||||
- **Frame time (p50/p95/p99):** _pending_
|
|
||||||
- **Top zones:** _pending_
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- When `WOWEE_ENABLE_TRACY` is OFF (default), all `ZoneScopedN` / `FrameMark` macros expand to nothing — zero runtime overhead.
|
|
||||||
- Tracy requires a network connection to capture traces. Run the Tracy profiler GUI or `tracy-capture` CLI alongside the client.
|
|
||||||
- Debug builds are significantly slower due to -Og and no LTO; use RelWithDebInfo for representative measurements.
|
|
||||||
|
|
@ -19,11 +19,17 @@ For a more honest snapshot of gaps and current direction, see `docs/status.md`.
|
||||||
### 1. Clone
|
### 1. Clone
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone --recurse-submodules https://github.com/Kelsidavis/WoWee.git
|
git clone https://github.com/Kelsidavis/WoWee.git
|
||||||
cd WoWee
|
cd wowee
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Build
|
### 2. Install ImGui
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/ocornut/imgui.git extern/imgui
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
@ -90,7 +96,7 @@ Use `BUILD_INSTRUCTIONS.md` for distro-specific package lists.
|
||||||
|
|
||||||
- Verify auth/world server is running
|
- Verify auth/world server is running
|
||||||
- Check host/port settings
|
- Check host/port settings
|
||||||
- Check server logs and client logs in `logs/wowee.log`
|
- Check server logs and client logs in `build/bin/logs/`
|
||||||
|
|
||||||
### Missing assets (models/textures/terrain)
|
### Missing assets (models/textures/terrain)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -609,6 +609,6 @@ Once you have a working local server connection:
|
||||||
---
|
---
|
||||||
|
|
||||||
**Status**: Ready for local server testing
|
**Status**: Ready for local server testing
|
||||||
**Last Updated**: 2026-03-30
|
**Last Updated**: 2026-01-27
|
||||||
**Client Version**: v1.8.9-preview
|
**Client Version**: 1.0.3
|
||||||
**Server Compatibility**: Vanilla 1.12, TBC 2.4.3, WotLK 3.3.5a (12340), Turtle WoW 1.17
|
**Server Compatibility**: WoW 3.3.5a (12340)
|
||||||
|
|
|
||||||
|
|
@ -351,13 +351,13 @@ The expensive operation (session key computation) only happens once per login.
|
||||||
2. **No Plaintext Storage:** Password is immediately hashed, never stored
|
2. **No Plaintext Storage:** Password is immediately hashed, never stored
|
||||||
3. **Forward Secrecy:** Ephemeral keys (a, A) are generated per session
|
3. **Forward Secrecy:** Ephemeral keys (a, A) are generated per session
|
||||||
4. **Mutual Authentication:** Both client and server prove knowledge of password
|
4. **Mutual Authentication:** Both client and server prove knowledge of password
|
||||||
5. **Secure Channel:** Session key K is used for RC4 header encryption after auth completes
|
5. **Secure Channel:** Session key K can be used for encryption (not implemented yet)
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- [SRP Protocol](http://srp.stanford.edu/)
|
- [SRP Protocol](http://srp.stanford.edu/)
|
||||||
- [WoWDev Wiki - SRP](https://wowdev.wiki/SRP)
|
- [WoWDev Wiki - SRP](https://wowdev.wiki/SRP)
|
||||||
- Implementation: `src/auth/srp.cpp`, `include/auth/srp.hpp`
|
- Original wowee: `/wowee/src/lib/crypto/srp.js`
|
||||||
- OpenSSL BIGNUM: https://www.openssl.org/docs/man1.1.1/man3/BN_new.html
|
- OpenSSL BIGNUM: https://www.openssl.org/docs/man1.1.1/man3/BN_new.html
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Project Status
|
# Project Status
|
||||||
|
|
||||||
**Last updated**: 2026-03-30
|
**Last updated**: 2026-03-11
|
||||||
|
|
||||||
## What This Repo Is
|
## What This Repo Is
|
||||||
|
|
||||||
|
|
@ -25,23 +25,19 @@ Implemented (working in normal use):
|
||||||
- Talent tree UI with proper visuals and functionality
|
- Talent tree UI with proper visuals and functionality
|
||||||
- Pet tracking (SMSG_PET_SPELLS), dismiss pet button
|
- Pet tracking (SMSG_PET_SPELLS), dismiss pet button
|
||||||
- Party: group invites, party list, out-of-range member health (SMSG_PARTY_MEMBER_STATS)
|
- Party: group invites, party list, out-of-range member health (SMSG_PARTY_MEMBER_STATS)
|
||||||
- Nameplates: NPC subtitles, guild names, elite/boss/rare borders, quest/raid indicators, cast bars, debuff dots
|
|
||||||
- Floating combat text: world-space damage/heal numbers above entities with 3D projection
|
|
||||||
- Target/focus frames: guild name, creature type, rank badges, combo points, cast bars
|
|
||||||
- Map exploration: subzone-level fog-of-war reveal
|
- Map exploration: subzone-level fog-of-war reveal
|
||||||
- Warden anti-cheat: full module execution via Unicorn Engine x86 emulation; module caching
|
- Warden anti-cheat: full module execution via Unicorn Engine x86 emulation; module caching
|
||||||
- Audio: ambient, movement, combat, spell, and UI sound systems; NPC voice lines for all playable races (greeting/farewell/vendor/pissed/aggro/flee)
|
- Audio: ambient, movement, combat, spell, and UI sound systems
|
||||||
- Bag UI: independent bag windows (any bag closable independently), open-bag indicator on bag bar, server-synced bag sort, off-screen position reset, optional collapse-empty mode in aggregate view
|
- Bag UI: separate bag windows, open-bag indicator on bag bar, optional collapse-empty mode in aggregate bag view
|
||||||
- DBC auto-detection: CharSections.dbc field layout auto-detected at runtime (handles stock WotLK vs HD-textured clients)
|
|
||||||
- Multi-expansion: Classic/Vanilla, TBC, WotLK, and Turtle WoW (1.17) protocol and asset variants
|
- Multi-expansion: Classic/Vanilla, TBC, WotLK, and Turtle WoW (1.17) protocol and asset variants
|
||||||
- CI: GitHub Actions for Linux (x86-64, ARM64), Windows (MSYS2 x86-64 + ARM64), macOS (ARM64); container builds via Podman
|
- CI: GitHub Actions for Linux (x86-64, ARM64), Windows (MSYS2), macOS (ARM64); container builds via Podman
|
||||||
|
|
||||||
In progress / known gaps:
|
In progress / known gaps:
|
||||||
|
|
||||||
- Transports: M2 transports (trams) working with position-delta riding; WMO transports (ships, zeppelins) working with path following; some edge cases remain
|
- Transports: M2 transports (trams) working with position-delta riding; WMO transports (ships, zeppelins) working with path following; some edge cases remain
|
||||||
- Quest GO interaction: CMSG_GAMEOBJ_USE + CMSG_LOOT sent correctly, but some AzerothCore/ChromieCraft servers don't grant quest credit for chest-type GOs (server-side limitation)
|
- Visual edge cases: some M2/WMO rendering gaps (character shin mesh, some particle effects)
|
||||||
- Visual edge cases: some M2/WMO rendering gaps (some particle effects)
|
- Lava steam particles: sparse in some areas (tuning opportunity)
|
||||||
- Water refraction: enabled by default; srcAccessMask barrier fix (2026-03-18) resolved prior VK_ERROR_DEVICE_LOST on AMD/Mali GPUs
|
- Water refraction: implemented but disabled by default (can cause VK_ERROR_DEVICE_LOST on some GPUs); currently requires FSR to be active
|
||||||
|
|
||||||
## Where To Look
|
## Where To Look
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,156 +0,0 @@
|
||||||
# Threading Model
|
|
||||||
|
|
||||||
This document describes the threading architecture of WoWee, the synchronisation
|
|
||||||
primitives that protect shared state, and the conventions that new code must
|
|
||||||
follow.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Thread Inventory
|
|
||||||
|
|
||||||
| # | Name / Role | Created At | Lifetime |
|
|
||||||
|----|------------------------|-------------------------------------------------|-------------------------------|
|
|
||||||
| 1 | **Main thread** | `Application::run()` (`main.cpp`) | Entire session |
|
|
||||||
| 2 | **Async network pump** | `WorldSocket::connectAsync()` (`world_socket.cpp`) | Connect → disconnect |
|
|
||||||
| 3 | **Terrain workers** | `TerrainManager::startWorkers()` (`terrain_manager.cpp`) | Map load → map unload |
|
|
||||||
| 4 | **Watchdog** | `Application::startWatchdog()` (`application.cpp`) | After first frame → shutdown |
|
|
||||||
| 5 | **Fire-and-forget** | `std::async` / `std::thread(...).detach()` (various) | Task-scoped (bone anim, normal-map gen, warden crypto, world preload, entity model loading) |
|
|
||||||
|
|
||||||
### Thread Responsibilities
|
|
||||||
|
|
||||||
* **Main thread** — SDL event pumping, game logic (entity update, camera, UI),
|
|
||||||
GPU resource upload/finalization, render command recording, Vulkan present.
|
|
||||||
* **Network pump** — `recv()` loop, header decryption, packet parsing. Pushes
|
|
||||||
parsed packets into `pendingPacketCallbacks_` (locked by `callbackMutex_`).
|
|
||||||
The main thread drains this queue via `dispatchQueuedPackets()`.
|
|
||||||
* **Terrain workers** — background ADT/WMO/M2 file I/O, mesh decoding, texture
|
|
||||||
decompression. Workers push completed `PendingTile` objects into `readyQueue`
|
|
||||||
(locked by `queueMutex`). The main thread finalizes (GPU upload) via
|
|
||||||
`processReadyTiles()`.
|
|
||||||
* **Watchdog** — periodic frame-stall detection. Reads `watchdogHeartbeatMs`
|
|
||||||
(atomic) and optionally requests a Vulkan device reset via
|
|
||||||
`watchdogRequestRelease` (atomic).
|
|
||||||
* **Fire-and-forget** — short-lived tasks. Each captures only the data it
|
|
||||||
needs or uses a dedicated result channel (e.g. `std::future`,
|
|
||||||
`completedNormalMaps_` with `normalMapResultsMutex_`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Shared State Map
|
|
||||||
|
|
||||||
### Legend
|
|
||||||
|
|
||||||
| Annotation | Meaning |
|
|
||||||
|-------------------------|---------|
|
|
||||||
| `THREAD-SAFE: <mutex>` | Protected by the named mutex/atomic. |
|
|
||||||
| `MAIN-THREAD-ONLY` | Accessed exclusively by the main thread. No lock needed. |
|
|
||||||
|
|
||||||
### Asset Manager (`include/pipeline/asset_manager.hpp`)
|
|
||||||
|
|
||||||
| Variable | Guard | Notes |
|
|
||||||
|-------------------------|------------------|-------|
|
|
||||||
| `fileCache` | `cacheMutex` (shared_mutex) | `shared_lock` for reads, `lock_guard` for writes/eviction |
|
|
||||||
| `dbcCache` | `cacheMutex` | Same mutex as fileCache |
|
|
||||||
| `fileCacheTotalBytes` | `cacheMutex` | Written under exclusive lock only |
|
|
||||||
| `fileCacheAccessCounter`| `cacheMutex` | Written under exclusive lock only |
|
|
||||||
| `fileCacheHits` | `std::atomic` | Incremented after releasing cacheMutex |
|
|
||||||
| `fileCacheMisses` | `std::atomic` | Incremented after releasing cacheMutex |
|
|
||||||
|
|
||||||
### Audio Engine (`src/audio/audio_engine.cpp`)
|
|
||||||
|
|
||||||
| Variable | Guard | Notes |
|
|
||||||
|------------------------|---------------------------|-------|
|
|
||||||
| `gDecodedWavCache` | `gDecodedWavCacheMutex` (shared_mutex) | `shared_lock` for cache hits, `lock_guard` for miss+eviction. Double-check after decoding. |
|
|
||||||
|
|
||||||
### World Socket (`include/network/world_socket.hpp`)
|
|
||||||
|
|
||||||
| Variable | Guard | Notes |
|
|
||||||
|---------------------------|------------------|-------|
|
|
||||||
| `sockfd`, `connected`, `encryptionEnabled`, `receiveBuffer`, `receiveReadOffset_`, `headerBytesDecrypted`, cipher state, `recentPacketHistory_` | `ioMutex_` | Consistent `lock_guard` in `send()` and `pumpNetworkIO()` |
|
|
||||||
| `pendingPacketCallbacks_` | `callbackMutex_` | Pump thread produces, main thread consumes in `dispatchQueuedPackets()` |
|
|
||||||
| `asyncPumpStop_`, `asyncPumpRunning_` | `std::atomic<bool>` | Memory-order acquire/release |
|
|
||||||
| `packetCallback` | *implicit* | Set once before `connectAsync()` starts the pump thread |
|
|
||||||
|
|
||||||
### Terrain Manager (`include/rendering/terrain_manager.hpp`)
|
|
||||||
|
|
||||||
| Variable | Guard | Notes |
|
|
||||||
|-----------------------|--------------------------|-------|
|
|
||||||
| `loadQueue`, `readyQueue`, `pendingTiles` | `queueMutex` + `queueCV` | Workers wait; main signals on enqueue/finalize |
|
|
||||||
| `tileCache_`, `tileCacheLru_`, `tileCacheBytes_` | `tileCacheMutex_` | Read/write by both main and workers |
|
|
||||||
| `uploadedM2Ids_` | `uploadedM2IdsMutex_` | Workers check, main inserts on finalize |
|
|
||||||
| `preparedWmoUniqueIds_`| `preparedWmoUniqueIdsMutex_` | Workers only |
|
|
||||||
| `missingAdtWarnings_` | `missingAdtWarningsMutex_` | Workers only |
|
|
||||||
| `workerRunning` | `std::atomic<bool>` | — |
|
|
||||||
| `placedDoodadIds`, `placedWmoIds`, `loadedTiles`, `failedTiles` | MAIN-THREAD-ONLY | Only touched in processReadyTiles / unloadDistantTiles |
|
|
||||||
|
|
||||||
### Entity Manager (`include/game/entity.hpp`)
|
|
||||||
|
|
||||||
| Variable | Guard | Notes |
|
|
||||||
|------------|------------------|-------|
|
|
||||||
| `entities` | MAIN-THREAD-ONLY | All mutations via `dispatchQueuedPackets()` on main thread |
|
|
||||||
|
|
||||||
### Character Renderer (`include/rendering/character_renderer.hpp`)
|
|
||||||
|
|
||||||
| Variable | Guard | Notes |
|
|
||||||
|------------------------|---------------------------|-------|
|
|
||||||
| `completedNormalMaps_` | `normalMapResultsMutex_` | Detached threads push, main thread drains |
|
|
||||||
| `pendingNormalMapCount_`| `std::atomic<int>` | acq_rel ordering |
|
|
||||||
|
|
||||||
### Logger (`include/core/logger.hpp`)
|
|
||||||
|
|
||||||
| Variable | Guard | Notes |
|
|
||||||
|-------------|-----------------|-------|
|
|
||||||
| `minLevel_` | `std::atomic<int>` | Fast path check in `shouldLog()` |
|
|
||||||
| `fileStream`, `lastMessage_`, suppression state | `mutex` | Locked in `log()` |
|
|
||||||
|
|
||||||
### Application (`src/core/application.cpp`)
|
|
||||||
|
|
||||||
| Variable | Guard | Notes |
|
|
||||||
|-------------------------|--------------------|-------|
|
|
||||||
| `watchdogHeartbeatMs` | `std::atomic<int64_t>` | Main stores, watchdog loads |
|
|
||||||
| `watchdogRequestRelease`| `std::atomic<bool>` | Watchdog stores, main exchanges |
|
|
||||||
| `watchdogRunning` | `std::atomic<bool>` | — |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Conventions for New Code
|
|
||||||
|
|
||||||
1. **Prefer `std::shared_mutex`** for read-heavy caches. Use `std::shared_lock`
|
|
||||||
for lookups and `std::lock_guard<std::shared_mutex>` for mutations.
|
|
||||||
|
|
||||||
2. **Annotate shared state** at the declaration site with either
|
|
||||||
`// THREAD-SAFE: protected by <mutex_name>` or `// MAIN-THREAD-ONLY`.
|
|
||||||
|
|
||||||
3. **Keep lock scope minimal.** Copy data under the lock, then process outside.
|
|
||||||
|
|
||||||
4. **Avoid detaching threads** when possible. Prefer `std::async` with a
|
|
||||||
`std::future` stored on the owning object so shutdown can wait for completion.
|
|
||||||
|
|
||||||
5. **Use `std::atomic` for counters and flags** that are read/written without
|
|
||||||
other invariants (e.g. cache hit stats, boolean run flags).
|
|
||||||
|
|
||||||
6. **No lock-order inversions.** Current order (most-outer first):
|
|
||||||
`ioMutex_` → `callbackMutex_` → `queueMutex` → `cacheMutex`.
|
|
||||||
|
|
||||||
7. **ThreadSanitizer** — run periodically with `-fsanitize=thread` to catch
|
|
||||||
regressions:
|
|
||||||
```bash
|
|
||||||
cmake -DCMAKE_CXX_FLAGS="-fsanitize=thread" .. && make -j$(nproc)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Known Limitations
|
|
||||||
|
|
||||||
* `EntityManager::entities` relies on the convention that all entity mutations
|
|
||||||
happen on the main thread through `dispatchQueuedPackets()`. There is no
|
|
||||||
compile-time enforcement. If a future change introduces direct entity
|
|
||||||
modification from the network pump thread, a mutex must be added.
|
|
||||||
|
|
||||||
* `packetCallback` in `WorldSocket` is set once before `connectAsync()` and
|
|
||||||
never modified afterwards. This is safe in practice but not formally
|
|
||||||
synchronized — do not change the callback after `connectAsync()`.
|
|
||||||
|
|
||||||
* `fileCacheMisses` is declared as `std::atomic<size_t>` for consistency but is
|
|
||||||
currently never incremented; the actual miss count must be inferred from
|
|
||||||
`fileCacheAccessCounter - fileCacheHits`.
|
|
||||||
1
extern/FidelityFX-FSR2
vendored
1
extern/FidelityFX-FSR2
vendored
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 3d22aefd90fd861e5cee1c3cde18ff185e221f2d
|
|
||||||
1
extern/FidelityFX-SDK
vendored
1
extern/FidelityFX-SDK
vendored
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit ce81c674d92d81ad1253841f39a359811dd738cf
|
|
||||||
14
extern/VERSIONS.md
vendored
14
extern/VERSIONS.md
vendored
|
|
@ -1,14 +0,0 @@
|
||||||
# Vendored Library Versions
|
|
||||||
|
|
||||||
Versions of third-party libraries vendored in `extern/`. Update this file
|
|
||||||
when upgrading any dependency so maintainers can track drift.
|
|
||||||
|
|
||||||
| Library | Version | Source | Notes |
|
|
||||||
|---------|---------|--------|-------|
|
|
||||||
| Dear ImGui | 1.92.6 WIP | https://github.com/ocornut/imgui | Git submodule |
|
|
||||||
| vk-bootstrap | latest | https://github.com/charles-lunarg/vk-bootstrap | Git submodule |
|
|
||||||
| Vulkan Memory Allocator | 3.4.0 | https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator | Single header |
|
|
||||||
| miniaudio | 0.11.24 | https://miniaud.io/ | Single header |
|
|
||||||
| stb_image | 2.30 | https://github.com/nothings/stb | Single header |
|
|
||||||
| stb_image_write | 1.16 | https://github.com/nothings/stb | Single header |
|
|
||||||
| Lua | 5.1.5 | https://www.lua.org/ | Intentionally 5.1 for WoW addon API compatibility |
|
|
||||||
11811
extern/catch2/catch_amalgamated.cpp
vendored
11811
extern/catch2/catch_amalgamated.cpp
vendored
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue