diff --git a/.gitignore b/.gitignore index 6db545d..08cc229 100644 --- a/.gitignore +++ b/.gitignore @@ -111,3 +111,11 @@ local.properties # !modules/yourmodule # # ================== + +# Fractured: ship custom modules (default AC ignores /modules/*) +!/modules/mod-paragon/ +!/modules/mod-paragon/** +!/modules/mod-ale/ +!/modules/mod-ale/** +# Team Docker: ALE needs Lua at configure time (see mod-ale docs) +!docker-compose.override.yml diff --git a/apps/docker/Dockerfile b/apps/docker/Dockerfile index 0cdc476..6aec89b 100644 --- a/apps/docker/Dockerfile +++ b/apps/docker/Dockerfile @@ -54,6 +54,14 @@ ARG CWITH_WARNINGS="ON" ARG CMAKE_EXTRA_OPTIONS="" ARG GIT_DISCOVERY_ACROSS_FILESYSTEM=1 +# mod-paragon: hard cap the build's `-j` parallelism. The default of +# `nproc + 1` reliably OOM-kills the Docker Desktop / WSL2 VM partway +# through the heavy mod-ale (Eluna) hook compiles when memory is at the +# stock 8 GB allocation. Lower this if your VM still chokes; raise it +# (or set to 0 to mean "nproc+1") if you've given Docker plenty of RAM +# and want the fastest possible build. +ARG CBUILD_PARALLEL="4" + ARG CCACHE_DIR="/ccache" ARG CCACHE_MAXSIZE="1000MB" ARG CCACHE_SLOPPINESS="pch_defines,time_macros,include_file_mtime" @@ -101,7 +109,9 @@ RUN --mount=type=cache,target=/ccache,sharing=locked \ -DCMAKE_CXX_COMPILER_LAUNCHER="ccache" \ -DCMAKE_C_COMPILER_LAUNCHER="ccache" \ -DBoost_USE_STATIC_LIBS="ON" ${CMAKE_EXTRA_OPTIONS} \ - && cmake --build . --config "$CTYPE" -j $(($(nproc) + 1)) \ + && BUILD_J="$([ "$CBUILD_PARALLEL" = "0" ] && echo $(($(nproc) + 1)) || echo "$CBUILD_PARALLEL")" \ + && echo "[paragon-build] cmake --build -j ${BUILD_J} (CBUILD_PARALLEL=${CBUILD_PARALLEL})" \ + && cmake --build . --config "$CTYPE" -j "${BUILD_J}" \ && cmake --install . --config "$CTYPE" ############################# diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 0000000..0dcf46c --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,19 @@ +# Local overrides (not overwritten by git pull). ALE needs Lua at configure time. +# See https://github.com/azerothcore/mod-ale/blob/master/docs/INSTALL.md +services: + ac-worldserver: + build: + args: + CMAKE_EXTRA_OPTIONS: "-DLUA_VERSION=luajit" + ac-authserver: + build: + args: + CMAKE_EXTRA_OPTIONS: "-DLUA_VERSION=luajit" + ac-db-import: + build: + args: + CMAKE_EXTRA_OPTIONS: "-DLUA_VERSION=luajit" + ac-tools: + build: + args: + CMAKE_EXTRA_OPTIONS: "-DLUA_VERSION=luajit" diff --git a/modules/mod-ale/.editorconfig b/modules/mod-ale/.editorconfig new file mode 100644 index 0000000..eb64e2f --- /dev/null +++ b/modules/mod-ale/.editorconfig @@ -0,0 +1,8 @@ +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +tab_width = 4 +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 80 diff --git a/modules/mod-ale/.git_commit_template.txt b/modules/mod-ale/.git_commit_template.txt new file mode 100644 index 0000000..708b551 --- /dev/null +++ b/modules/mod-ale/.git_commit_template.txt @@ -0,0 +1,49 @@ +### TITLE +## Type(Scope/Subscope): Commit ultra short explanation +## |---- Write below the examples with a maximum of 50 characters ----| +## Example 1: fix(DB/SAI): Missing spell to NPC Hogger +## Example 2: fix(CORE/Raid): Phase 2 of Ragnaros +## Example 3: feat(CORE/Commands): New GM command to do something + + +### DESCRIPTION +## Explain why this change is being made, what does it fix etc... +## |---- Write below the examples with a maximum of 72 characters per lines ----| +## Example: Hogger (id: 492) was not charging player when being engaged. + + +## Provide links to any issue, commit, pull request or other resource +## Example 1: Closes issue #23 +## Example 2: Ported from other project's commit (link) +## Example 3: References taken from wowpedia / wowhead / wowwiki / https://wowgaming.altervista.org/aowow/ + + + +## ======================================================= +## EXTRA INFOS +## ======================================================= +## "Type" can be: +## feat (new feature) +## fix (bug fix) +## refactor (refactoring production code) +## style (formatting, missing semi colons, etc; no code change) +## docs (changes to documentation) +## test (adding or refactoring tests; no production code change) +## chore (updating bash scripts, git files etc; no production code change) +## -------------------- +## Remember to +## Capitalize the subject line +## Use the imperative mood in the subject line +## Do not end the subject line with a period +## Separate subject from body with a blank line +## Use the body to explain what and why rather than how +## Can use multiple lines with "-" for bullet points in body +## -------------------- +## More info here https://www.conventionalcommits.org/en/v1.0.0-beta.2/ +## ======================================================= +## "Scope" can be: +## CORE (core related, c++) +## DB (database related, sql) +## ======================================================= +## "Subscope" is optional and depends on the nature of the commit. +## ======================================================= diff --git a/modules/mod-ale/.gitattributes b/modules/mod-ale/.gitattributes new file mode 100644 index 0000000..7ef9001 --- /dev/null +++ b/modules/mod-ale/.gitattributes @@ -0,0 +1,105 @@ +## AUTO-DETECT +## Handle line endings automatically for files detected as +## text and leave all files detected as binary untouched. +## This will handle all files NOT defined below. +* text=auto eol=lf + +# Text +*.conf text +*.conf.dist text +*.cmake text + +## Scripts +*.sh text +*.fish text +*.lua text + +## SQL +*.sql text + +## C++ +*.c text +*.cc text +*.cxx text +*.cpp text +*.c++ text +*.hpp text +*.h text +*.h++ text +*.hh text + + +## For documentation + +# Documents +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain + +## DOCUMENTATION +*.markdown text +*.md text +*.mdwn text +*.mdown text +*.mkd text +*.mkdn text +*.mdtxt text +*.mdtext text +*.txt text +AUTHORS text +CHANGELOG text +CHANGES text +CONTRIBUTING text +COPYING text +copyright text +*COPYRIGHT* text +INSTALL text +license text +LICENSE text +NEWS text +readme text +*README* text +TODO text + +## GRAPHICS +*.ai binary +*.bmp binary +*.eps binary +*.gif binary +*.ico binary +*.jng binary +*.jp2 binary +*.jpg binary +*.jpeg binary +*.jpx binary +*.jxr binary +*.pdf binary +*.png binary +*.psb binary +*.psd binary +*.svg text +*.svgz binary +*.tif binary +*.tiff binary +*.wbmp binary +*.webp binary + + +## ARCHIVES +*.7z binary +*.gz binary +*.jar binary +*.rar binary +*.tar binary +*.zip binary + +## EXECUTABLES +*.exe binary +*.pyc binary diff --git a/modules/mod-ale/.github/images/workflow.svg b/modules/mod-ale/.github/images/workflow.svg new file mode 100644 index 0000000..f46fc90 --- /dev/null +++ b/modules/mod-ale/.github/images/workflow.svg @@ -0,0 +1,4 @@ + + + +
Fork Repository
Create Branch
Make Changes
Submit PR
Review Process
diff --git a/modules/mod-ale/.github/workflows/build-lua51.yml b/modules/mod-ale/.github/workflows/build-lua51.yml new file mode 100644 index 0000000..96997b5 --- /dev/null +++ b/modules/mod-ale/.github/workflows/build-lua51.yml @@ -0,0 +1,14 @@ +name: Build mod-eluna with Lua51 🌙 + +on: + push: + branches: + - 'master' + - 'main' + pull_request: + +jobs: + build_lua51: + uses: ./.github/workflows/core-build-base.yml + with: + lua_version: 'lua51' diff --git a/modules/mod-ale/.github/workflows/build-lua52.yml b/modules/mod-ale/.github/workflows/build-lua52.yml new file mode 100644 index 0000000..66da006 --- /dev/null +++ b/modules/mod-ale/.github/workflows/build-lua52.yml @@ -0,0 +1,14 @@ +name: Build mod-eluna with Lua52 🌙 + +on: + push: + branches: + - 'master' + - 'main' + pull_request: + +jobs: + build_lua52: + uses: ./.github/workflows/core-build-base.yml + with: + lua_version: 'lua52' diff --git a/modules/mod-ale/.github/workflows/build-lua53.yml b/modules/mod-ale/.github/workflows/build-lua53.yml new file mode 100644 index 0000000..a3fe63e --- /dev/null +++ b/modules/mod-ale/.github/workflows/build-lua53.yml @@ -0,0 +1,14 @@ +name: Build mod-eluna with Lua53 🌙 + +on: + push: + branches: + - 'master' + - 'main' + pull_request: + +jobs: + build_lua53: + uses: ./.github/workflows/core-build-base.yml + with: + lua_version: 'lua53' diff --git a/modules/mod-ale/.github/workflows/build-lua54.yml b/modules/mod-ale/.github/workflows/build-lua54.yml new file mode 100644 index 0000000..7aa8c87 --- /dev/null +++ b/modules/mod-ale/.github/workflows/build-lua54.yml @@ -0,0 +1,14 @@ +name: Build mod-eluna with Lua54 🌙 + +on: + push: + branches: + - 'master' + - 'main' + pull_request: + +jobs: + build_lua54: + uses: ./.github/workflows/core-build-base.yml + with: + lua_version: 'lua54' diff --git a/modules/mod-ale/.github/workflows/build-luajit.yml b/modules/mod-ale/.github/workflows/build-luajit.yml new file mode 100644 index 0000000..c73f26f --- /dev/null +++ b/modules/mod-ale/.github/workflows/build-luajit.yml @@ -0,0 +1,14 @@ +name: Build mod-eluna with LuaJIT 🌙 + +on: + push: + branches: + - 'master' + - 'main' + pull_request: + +jobs: + build_luajit: + uses: ./.github/workflows/core-build-base.yml + with: + lua_version: 'luajit' diff --git a/modules/mod-ale/.github/workflows/core-build-base.yml b/modules/mod-ale/.github/workflows/core-build-base.yml new file mode 100644 index 0000000..5d1d0d1 --- /dev/null +++ b/modules/mod-ale/.github/workflows/core-build-base.yml @@ -0,0 +1,85 @@ +name: Build mod-eluna base 🛠️ + +on: + workflow_call: + inputs: + lua_version: + required: true + type: string + +jobs: + install_and_build: + runs-on: ubuntu-24.04 + steps: + - name: Check out AzerothCore 🧑‍💻 + uses: actions/checkout@v4 + with: + repository: 'azerothcore/azerothcore-wotlk' + ref: 'master' + submodules: 'recursive' + fetch-depth: 0 + + - name: Check out module repository 📂 + uses: actions/checkout@v4 + with: + submodules: 'recursive' + path: 'modules/${{ github.event.repository.name }}' + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: Cache compilation artifacts 💾 + uses: actions/cache@v4 + with: + path: var/ccache + key: ccache:${{ matrix.compiler.CC }}:${{ github.ref }}:${{ github.sha }} + restore-keys: | + ccache:clang-18:${{ github.ref }} + ccache:clang-18 + + - name: Install build dependencies 🧰 + shell: bash + run: | + sudo apt update + sudo apt-get -y install ccache clang cmake curl google-perftools \ + libmysqlclient-dev make unzip build-essential cmake-data \ + libboost-all-dev libbz2-dev libncurses5-dev libmysql++-dev \ + libreadline6-dev libssl-dev libtool openssl zlib1g-dev + + - name: Build mod-eluna with ${{ inputs.lua_version }} 🏗️ + run: | + rm -rf build + mkdir build && cd build + cmake .. \ + -DCMAKE_C_COMPILER=clang-18 \ + -DCMAKE_CXX_COMPILER=clang++-18 \ + -DSCRIPTS="static" \ + -DMODULES="static" \ + -DWITH_WARNINGS="ON" \ + -DCMAKE_BUILD_TYPE="Release" \ + -DCMAKE_CXX_COMPILER_LAUNCHER="ccache" \ + -DCMAKE_C_COMPILER_LAUNCHER="ccache" \ + -DCMAKE_C_FLAGS="-Werror" \ + -DCMAKE_CXX_FLAGS="-Werror" \ + -DLUA_VERSION=${{ inputs.lua_version }} + make -j$(nproc) + cd .. + + - name: Run Cppcheck for static code analysis 🔍 + run: | + sudo apt update -y + sudo apt install -y cppcheck + cd modules/${{ github.event.repository.name }} + cppcheck -j$(nproc) --force --inline-suppr \ + -I src/LuaEngine/ \ + -I src/ \ + --suppress=*:src/lualib/* \ + --suppress=*:src/LuaEngine/libs/* \ + --output-file=report.txt \ + . + if [ -s report.txt ]; then + echo "Cppcheck detected issues 🚨:" + cat report.txt + exit 1 + else + echo "No issues detected by cppcheck ✅." + fi diff --git a/modules/mod-ale/.gitignore b/modules/mod-ale/.gitignore new file mode 100644 index 0000000..c6e1299 --- /dev/null +++ b/modules/mod-ale/.gitignore @@ -0,0 +1,48 @@ +!.gitignore + +# +#Generic +# + +.directory +.mailmap +*.orig +*.rej +*.*~ +.hg/ +*.kdev* +.DS_Store +CMakeLists.txt.user +*.bak +*.patch +*.diff +*.REMOTE.* +*.BACKUP.* +*.BASE.* +*.LOCAL.* + +# +# IDE & other softwares +# +/.settings/ +/.externalToolBuilders/* +# exclude in all levels +nbproject/ +.sync.ffs_db +*.kate-swp + +# +# Eclipse +# +*.pydevproject +.metadata +.gradle +tmp/ +*.tmp +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.project +.cproject diff --git a/modules/mod-ale/CMakeLists.txt b/modules/mod-ale/CMakeLists.txt new file mode 100644 index 0000000..41b59b0 --- /dev/null +++ b/modules/mod-ale/CMakeLists.txt @@ -0,0 +1,21 @@ +set(LUA_VERSION "lua52" CACHE STRING "Lua version to use") +set_property(CACHE LUA_VERSION PROPERTY STRINGS luajit lua51 lua52 lua53 lua54) +MESSAGE(STATUS "Lua version: ${LUA_VERSION}") + +# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24: +if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") + cmake_policy(SET CMP0135 NEW) +endif() + +option(LUA_STATIC "link lua statically" ON) +if (LUA_STATIC) + MESSAGE(STATUS "Lua linking: static") +else() + MESSAGE(STATUS "Lua linking: dynamic") +endif() + +if (LUA_VERSION MATCHES "luajit") + add_subdirectory(src/lualib/luajit) +else() + add_subdirectory(src/lualib/lua) +endif() diff --git a/modules/mod-ale/COMMUNITY_UPDATES.md b/modules/mod-ale/COMMUNITY_UPDATES.md new file mode 100644 index 0000000..9772e82 --- /dev/null +++ b/modules/mod-ale/COMMUNITY_UPDATES.md @@ -0,0 +1,93 @@ +
+ +# [![Eluna](src/LuaEngine/docs/Eluna.png)](https://github.com/ElunaLuaEngine/Eluna) + +*Unleash the power of Lua scripting in your AzerothCore server* + +[![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.com/invite/ZKSVREE7) +[![Lua](https://img.shields.io/badge/Lua-5.2-2C2D72?style=for-the-badge&logo=lua&logoColor=white)](http://www.lua.org/manual/5.2/) +[![AzerothCore](https://img.shields.io/badge/AzerothCore-Integrated-darkgreen?style=for-the-badge)](http://www.azerothcore.org/) + +--- +
+ +> [!NOTE] +> mod-eluna © is a powerful Lua scripting engine embedded into the AzerothCore emulator. We are committed to continuously improving mod-eluna for both developers and server administrators. + +## 📚 Additions from Eluna/master +| Category | Method | Github | +|:---------|:-----------|:-------| +| [**RegisterPlayerEvent**](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | | | +| | [`PLAYER_EVENT_ON_PET_ADDED_TO_WORLD`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/3 | +| | [`PLAYER_EVENT_ON_LEARN_SPELL`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/46 | +| | [`PLAYER_ON_ACHIEVEMENT_COMPLETE`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/47 | +| | [`PLAYER_EVENT_ON_FFAPVP_CHANGE`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/63 | +| | [`PLAYER_EVENT_ON_UPDATE_AREA`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/65 | +| | [`PLAYER_EVENT_ON_CAN_INIT_TRADE`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/83 | +| | [`PLAYER_EVENT_ON_CAN_SEND_MAIL`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/85 | +| | [`PLAYER_EVENT_ON_CAN_JOIN_LFG`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/86 | +| | [`PLAYER_EVENT_ON_QUEST_REWARD_ITEM`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/88 | +| | [`PLAYER_EVENT_ON_CREATE_ITEM`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/88 | +| | [`PLAYER_EVENT_ON_STORE_NEW_ITEM`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/88 | +| | [`PLAYER_EVENT_ON_COMPLETE_QUEST`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/90 | +| | [`PLAYER_EVENT_ON_CAN_GROUP_INVITE`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/100 | +| | [`PLAYER_EVENT_ON_GROUP_ROLL_REWARD_ITEM`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/119 | +| | [`PLAYER_EVENT_ON_BG_DESERTION`](https://www.azerothcore.org/eluna/Global/RegisterPlayerEvent.html) | https://github.com/azerothcore/mod-eluna/pull/146 | +| **[Player](https://www.azerothcore.org/eluna/Player/index.html)** | | | +| | [`Player:GetMailCount()`](https://www.azerothcore.org/eluna/Player/GetMailCount.html) | https://github.com/azerothcore/mod-eluna/pull/76 | +| | [`Player:GetXP()`](https://www.azerothcore.org/eluna/Player/GetXP.html) | https://github.com/azerothcore/mod-eluna/pull/77 | +| | [`Player:GetAchievementCriteriaProgress()`](https://www.azerothcore.org/eluna/Player/GetAchievementCriteriaProgress.html) | https://github.com/azerothcore/mod-eluna/pull/78 | +| | [`Player:SendListInventory(object, vendorentry)`](https://www.azerothcore.org/eluna/Player/SendListInventory.html) | https://github.com/azerothcore/mod-eluna/pull/48 | +| | [`Player:GetPlayerSettingValue()`](https://www.azerothcore.org/eluna/Player/GetPlayerSettingValue.html) and [`Player:UpdatePlayerSetting()`](https://www.azerothcore.org/eluna/Player/GetPlayerSettingValue.html) | https://github.com/azerothcore/mod-eluna/pull/125 | +| | [`Player:GetTrader()`](https://www.azerothcore.org/eluna/Player/GetTrader.html) | https://github.com/azerothcore/mod-eluna/pull/126 | +| | [`Player:CanCompleteRepeatableQuest(questid)`](https://www.azerothcore.org/eluna/Player/CanCompleteRepeatableQuest.html) | https://github.com/azerothcore/mod-eluna/pull/141 | +| | [`Player:CanRewardQuest(questId)`](https://www.azerothcore.org/eluna/Player/CanRewardQuest.html) | https://github.com/azerothcore/mod-eluna/pull/141 | +| | [`Player:SetGlyph(glyphId, slotIndex)`](https://www.azerothcore.org/eluna/Player/SetGlyph.html) | https://github.com/azerothcore/mod-eluna/pull/152 | +| **[Group](https://www.azerothcore.org/eluna/Group/index.html)** | | | +| | [`Group:GetGroupType()`](https://www.azerothcore.org/eluna/Group/GetGroupType.html) | https://github.com/azerothcore/mod-eluna/pull/82 | +| | [`Group:SetMemberFlag()`](https://www.azerothcore.org/eluna/Group/SetMemberFlag.html) | https://github.com/azerothcore/mod-eluna/pull/102 | +| **[Unit](https://www.azerothcore.org/eluna/Unit/index.html)** | | | +| | [`Unit:ModifyThreatPct()`](https://www.azerothcore.org/eluna/Unit/ModifyThreatPct.html) | https://github.com/azerothcore/mod-eluna/pull/25 | +| | [`Unit:GetAttackers()`](https://www.azerothcore.org/eluna/Unit/GetAttackers.html) | https://github.com/azerothcore/mod-eluna/pull/116 | +| | [`Unit:GetThreatList()`](https://www.azerothcore.org/eluna/Unit/GetThreatList.html) | https://github.com/azerothcore/mod-eluna/pull/117 | +| | [`Unit:GetUnitFlags()`](https://www.azerothcore.org/eluna/Unit/GetUnitFlags.html) | https://github.com/azerothcore/mod-eluna/pull/137 | +| | [`Unit:GetUnitFlagsTwo()`](https://www.azerothcore.org/eluna/Unit/GetUnitFlagsTwo.html) | https://github.com/azerothcore/mod-eluna/pull/137 | +| | [`Unit:SetUnitFlags(flags)`](https://www.azerothcore.org/eluna/Unit/SetUnitFlags.html) | https://github.com/azerothcore/mod-eluna/pull/137 | +| | [`Unit:SetUnitFlagsTwo(flags)`](https://www.azerothcore.org/eluna/Unit/SetUnitFlagsTwo.html) | https://github.com/azerothcore/mod-eluna/pull/137 | +| | [`Unit:SetSpeedRate(unitMoveType, speed)`](https://www.azerothcore.org/eluna/Unit/SetSpeedRate.html) | https://github.com/azerothcore/mod-eluna/pull/155 | +| | [`Unit:GetSpeedRate()`](https://www.azerothcore.org/eluna/Unit/GetSpeedRate.html) | https://github.com/azerothcore/mod-eluna/pull/155 | +| **[GameObject](https://www.azerothcore.org/eluna/GameObject/index.html)** | | | +| | [`GameObject:AddLoot()`](https://www.azerothcore.org/eluna/GameObject/AddLoot.html) | https://github.com/azerothcore/mod-eluna/pull/52 | +| **[Object](https://www.azerothcore.org/eluna/Object/index.html)** | | | +| | [`Object:IsPlayer()`](https://www.azerothcore.org/eluna/Object/IsPlayer.html) | https://github.com/azerothcore/mod-eluna/pull/42 | +| **[Item](https://www.azerothcore.org/eluna/Item/index.html)** | | | +| | [`Item:GetItemTemplate()`](https://www.azerothcore.org/eluna/Item/GetItemTemplate.html) | https://github.com/azerothcore/mod-eluna/pull/84 | +| **[Global](https://www.azerothcore.org/eluna/Unit/index.html)** | | | +| | [`HttpRequest`](https://www.azerothcore.org/eluna/Global/HttpRequest.html) | https://github.com/azerothcore/mod-eluna/pull/2 | +| | [`GetItemTemplate(itemEntry)`](https://www.azerothcore.org/eluna/Global/GetItemTemplate.html) | https://github.com/azerothcore/mod-eluna/pull/84 | +| | [`ChatHandler`](https://www.azerothcore.org/eluna/Global/ChatHandler.html) | https://github.com/azerothcore/mod-eluna/pull/23 | +| | [`ItemTemplate`](https://www.azerothcore.org/eluna/ItemTemplate/index.html) | https://github.com/azerothcore/mod-eluna/pull/84 | +| | [`Roll`](https://www.azerothcore.org/eluna/Roll/index.html) | https://github.com/azerothcore/mod-eluna/pull/119 | +| | `ELUNA_LOG_INFO` for [`RunCommand()`](https://www.azerothcore.org/eluna/Global/RunCommand.html) | https://github.com/azerothcore/mod-eluna/pull/75 | +| | [`GetOwnerHalaa()`](https://www.azerothcore.org/eluna/Global/GetOwnerHalaa.html) and [`SetOwnerHalaa(teamId)`](https://www.azerothcore.org/eluna/Global/SetOwnerHalaa.html) | https://github.com/azerothcore/mod-eluna/pull/79 | +| | [`WorldDBQueryAsync`](https://www.azerothcore.org/eluna/Global/WorldDBQueryAsync.html), [`CharDBQueryAsync`](https://www.azerothcore.org/eluna/Global/CharDBQueryAsync.html) and [`AuthDBQueryAsync`](https://www.azerothcore.org/eluna/Global/AuthDBQueryAsync.html) | https://github.com/azerothcore/mod-eluna/pull/113 | + +## 🤝 Contributing + +We welcome contributions! Here's how you can help: + +```mermaid +graph LR + A[Fork Repository] --> B[Create Branch] + B --> C[Make Changes] + C --> D[Submit PR] + D --> E[Review Process] +``` + +
+ +--- +Made with ❤️ by the Eluna Community + +[⬆ Back to Top](#) +
diff --git a/modules/mod-ale/LICENSE b/modules/mod-ale/LICENSE new file mode 100644 index 0000000..9cecc1d --- /dev/null +++ b/modules/mod-ale/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/modules/mod-ale/README.md b/modules/mod-ale/README.md new file mode 100644 index 0000000..e0f56aa --- /dev/null +++ b/modules/mod-ale/README.md @@ -0,0 +1,154 @@ +
+ +# ALE - AzerothCore Lua Engine + +*Unleash the power of Lua scripting in your AzerothCore server* + +[![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.com/invite/sqkPb623) +[![Lua](https://img.shields.io/badge/Lua-5.2-2C2D72?style=for-the-badge&logo=lua&logoColor=white)](http://www.lua.org/manual/5.2/) +[![AzerothCore](https://img.shields.io/badge/AzerothCore-Integrated-darkgreen?style=for-the-badge)](http://www.azerothcore.org/) + +--- +
+ +> [!IMPORTANT] +> **ALE** is an independent Lua scripting engine specifically designed for AzerothCore. This project has **diverged from the original Eluna project** and is no longer compatible with standard Eluna scripts. Scripts written for ALE will not work with the original Eluna engine and vice versa. + +## 🚀 Overview + +ALE is a powerful, AzerothCore-specific implementation of a Lua scripting engine that enables server administrators and developers to create custom gameplay features, events, and mechanics without modifying the core server code. + +### Key Features +- **Native AzerothCore Integration**: Built specifically for AzerothCore's architecture +- **Enhanced API**: Extended functionality for AzerothCore, beyond the original Eluna specification. +- **Community-Driven Development**: Actively maintained with community contributions + +## ⚠️ Compatibility Notice + +### ALE vs Original Eluna + +**ALE is NOT compatible with the original Eluna project.** This fork has evolved independently with AzerothCore-specific enhancements and API changes that make scripts non-interchangeable. + +### For Original Eluna Compatibility + +If you need to use standard Eluna scripts or maintain compatibility with the original Eluna project, please use the dedicated AzerothCore port available at: + +**🔗 [ElunaAzerothCore](https://github.com/ElunaLuaEngine/ElunaAzerothcore)** + +This repository maintains compatibility with the original Eluna API and supports standard Eluna scripts. + +## 📋 Table of Contents + +- [Installation](#-installation) +- [Documentation](#-documentation) +- [API Reference](#api-reference) +- [Support](#-support) +- [Contributing](#-contributing) +- [Acknowledgements](#-acknowledgements) + +## ⚡ Installation + +### Prerequisites +- AzerothCore server installation +- Git version control system +- CMake build system + +### Installation Steps + +```bash +# Navigate to your AzerothCore modules directory +cd /modules + +# Clone the mod-ale repository +git clone https://github.com/azerothcore/mod-ale.git + +# Configure build with your preferred Lua version +cd +cmake ../ -DLUA_VERSION=luajit # Options: luajit, lua52, lua53, lua54 + +# Default: If no version is specified, Lua 5.2 will be used + +# Rebuild your AzerothCore server +make -j$(nproc) +``` + +### Supported Lua Versions +- **LuaJIT** (Recommended for performance) +- **Lua 5.2** (Default) +- **Lua 5.3** +- **Lua 5.4** + +## 📚 Documentation + +### Getting Started +- **[Installation Guide](docs/INSTALL.md)** - Complete installation and setup instructions +- **[Usage Guide](docs/USAGE.md)** - Learn how to write your first Lua scripts +- **[Implementation Details](docs/IMPL_DETAILS.md)** - Advanced features and technical details + +### Advanced Topics +- **[Contributing Guide](docs/CONTRIBUTING.md)** - How to contribute to ALE development +- **[Advanced Integration](docs/MERGING.md)** - Custom builds and maintaining modifications + +### API Reference +- **[mod-ale API Documentation](https://www.azerothcore.org/eluna/)** - Complete API reference for mod-ale +- **[Hooks Documentation](https://github.com/azerothcore/mod-ale/blob/master/src/LuaEngine/Hooks.h)** - Available event hooks +- **[Lua 5.2 Reference](http://www.lua.org/manual/5.2/)** - Official Lua language documentation + +> [!WARNING] +> **API Differences**: ALE functions may not be available in the original Eluna project and vice versa. Always refer to the ALE specific documentation when developing scripts. + +## 💬 Support + +### Getting Help +- **GitHub Issues**: [Report bugs or request features](https://github.com/azerothcore/mod-ale/issues) +- **Discord Community**: [Join our Discord server](https://discord.com/invite/sqkPb623) +- **AzerothCore Discord**: [Official AzerothCore support](http://www.azerothcore.org/) + +### Resources +- [Lua Programming Guide](http://www.lua.org/) +- [AzerothCore Documentation](http://www.azerothcore.org/) + +## 🤝 Contributing + +We welcome contributions from the community! Whether you code or not, there are many ways to help improve ALE. + +### Development Workflow +![](.github/images/workflow.svg "workflow example") + +### Quick Start +1. **Fork** the repository +2. **Create** a feature branch from `master` +3. **Implement** your changes with proper testing +4. **Follow** the existing code style and conventions +5. **Submit** a pull request with a clear description + +### Full Contributing Guide +For detailed information on how to contribute code, documentation, scripts, or help the community, see our **[Contributing Guide](docs/CONTRIBUTING.md)**. + +## 🌟 Acknowledgements + +### Original Project +mod-ale is built upon the foundation of the original [Eluna](https://github.com/ElunaLuaEngine/Eluna) project. We acknowledge and thank the original Eluna team for their pioneering work in Lua scripting for World of Warcraft server emulators. + +### Related Projects +- **[Original Eluna Repository](https://github.com/ElunaLuaEngine/Eluna)** - The original Eluna project +- **[Eluna Discord Community](https://discord.gg/bjkCVWqqfX)** - Original Eluna community support +- **[ElunaAzerothCore](https://github.com/Eluna-Ports/ElunaAzerothCore)** - AzerothCore with original Eluna compatibility + +### Supported Emulators +- **[AzerothCore](http://www.azerothcore.org/)** - Primary target (this project) +- **[TrinityCore](https://www.trinitycore.org/)** - Original Eluna support +- **[MaNGOS](https://www.getmangos.eu/)** +- **[cMaNGOS](https://cmangos.net/)** + +## 📄 License + +This project is licensed under the GNU General Public License v3.0. See [LICENSE](https://github.com/azerothcore/mod-ale/blob/master/LICENSE) for details. + +--- + +
+Developed with ❤️ by the AzerothCore and ALE community + +[⬆ Back to Top](#-overview) +
diff --git a/modules/mod-ale/README_CN.md b/modules/mod-ale/README_CN.md new file mode 100644 index 0000000..1789c31 --- /dev/null +++ b/modules/mod-ale/README_CN.md @@ -0,0 +1,92 @@ +### [![Eluna](src/LuaEngine/docs/Eluna.png)](https://github.com/ElunaLuaEngine/Eluna) + +## 关于 + +Eluna Lua Engine © 是嵌入到魔兽世界模拟器中的lua引擎。 Eluna支持MaNGOS,CMaNGOS,TrinityCore和AzerothCore。 +我们目前正在努力使Eluna从内到外变得更好。 + +如果您在安装或脚本方面遇到问题,请随时提出问题。 +有关文档和参考,请参阅[Eluna API (AC版)](https://www.azerothcore.org/pages/eluna/index.html) and [Lua 参考手册](http://www.lua.org/manual/5.2/). + + +## 社区 + +您可以加入官方的Eluna Discord服务器,在那里您将能够找到社区提供的资源,版本和支持: + + + + +官方的Azerothcore Discord服务器也提供了一个专门用于lua开发的通道: + + + + +# ![logo](https://raw.githubusercontent.com/azerothcore/azerothcore.github.io/master/images/logo-github.png) mod-eluna for AzerothCore +- azerothcore 的最新构建状态:[![Build Status](https://github.com/azerothcore/mod-eluna/workflows/core-build/badge.svg?branch=master&event=push)](https://github.com/azerothcore/mod-eluna) + +[english](README.md) | [中文说明](README_CN.md) | [Español](README_ES.md) + +一个AzerothCore的[Eluna](https://github.com/ElunaLuaEngine/Eluna)模块。 + + +## 如何安装: + +### 1) 下载源代码 + +您可以使用 git 获取源代码。 + + +#### 使用 git 下载 + +1. 在命令行中打开 `azerothcore-wotlk` 的文件夹。 +2. 进入 **modules** 文件夹: `cd modules` +3. 使用以下命令下载模块源代码。 +``` +git clone https://github.com/azerothcore/mod-eluna.git mod-eluna +``` + +### 2) 构建 + +您需要再次运行 cmake 并重新生成项目。 + +AC版的Eluna API: +[https://www.azerothcore.org/pages/eluna/index.html](https://www.azerothcore.org/pages/eluna/index.html) + + +## 文档 + +* [入门指南](https://github.com/ElunaLuaEngine/Eluna/blob/master/docs/USAGE.md) +* [Eluna特性](https://github.com/ElunaLuaEngine/Eluna/blob/master/docs/IMPL_DETAILS.md) +* [功能文档(AC版本)](https://www.azerothcore.org/pages/eluna/index.html) +* [Hook文档](https://github.com/ElunaLuaEngine/Eluna/blob/master/Hooks.h) +* [Lua参考手册](http://www.lua.org/manual/5.2/) +* [论坛 - 支持, 发布, 指南](https://www.getmangos.eu/forums/forum/119-eluna-central/) +* [示例脚本](https://github.com/ElunaLuaEngine/Scripts) +* [贡献](https://github.com/ElunaLuaEngine/Eluna/blob/master/docs/CONTRIBUTING.md) + + +## 链接 + +* [MaNGOS](http://getmangos.eu/) +* [cMaNGOS](http://cmangos.net/) +* [TrinityCore](http://www.trinitycore.org/) +* [AzerothCore](http://www.azerothcore.org/) +* [Lua.org](http://www.lua.org/) +* [License](https://github.com/ElunaLuaEngine/Eluna/blob/master/docs/LICENSE.md) + + +## 来自Eluna/master的拓展 + +- 添加了 HttpRequest 方法. https://github.com/azerothcore/Eluna/pull/2 +- 添加玩家注册事件43(当宠物添加到世界中时): `PLAYER_EVENT_ON_PET_ADDED_TO_WORLD` https://github.com/azerothcore/Eluna/pull/3 +- 添加聊天处理方法到玩家事件中。 https://github.com/azerothcore/Eluna/pull/23 +- 暴露方法 `ModifyThreatPct()`. https://github.com/azerothcore/Eluna/pull/25 +- 暴露方法 `Object:IsPlayer()`. https://github.com/azerothcore/Eluna/pull/42 +- 添加玩家注册事件44(当玩家学习技能时): `PLAYER_EVENT_ON_LEARN_SPELL`. https://github.com/azerothcore/mod-eluna/pull/46 +- 添加玩家注册事件45(当玩家完成成就时): `PLAYER_ON_ACHIEVEMENT_COMPLETE`。 https://github.com/azerothcore/mod-eluna/pull/47 +- 添加玩家注册事件51(当玩家获得任务奖励时) `PLAYER_EVENT_ON_QUEST_REWARD_ITEM`。https://github.com/azerothcore/mod-eluna/pull/88 +- 添加玩家注册事件52(当玩家创建物品时) `PLAYER_EVENT_ON_CREATE_ITEM`。https://github.com/azerothcore/mod-eluna/pull/88 +- 添加玩家注册事件53(当玩家创建物品实例时) `PLAYER_EVENT_ON_STORE_NEW_ITEM`。https://github.com/azerothcore/mod-eluna/pull/88 +- 添加玩家注册事件54(当玩家完成任务时) `PLAYER_EVENT_ON_COMPLETE_QUEST`。https://github.com/azerothcore/mod-eluna/pull/90 +- 新增参数*商人Id*到方法player:SendListInventory(object, vendorentry)中。 https://github.com/azerothcore/mod-eluna/pull/48 +- 添加方法`gameobject:AddLoot()`, 可以在线给**空**的容器中添加战利品。 https://github.com/azerothcore/mod-eluna/pull/52 diff --git a/modules/mod-ale/README_ES.md b/modules/mod-ale/README_ES.md new file mode 100644 index 0000000..0be2db3 --- /dev/null +++ b/modules/mod-ale/README_ES.md @@ -0,0 +1,26 @@ +# Esta traducción proviene de una versión desactualizada. +## +## +## +## + +# mod-LuaEngine + [English](README.md) | [中文说明](README_CN.md) | [Español](README_ES.md) + +Un módulo de Eluna para AzerothCore. + +Cómo instalar: + +1. Descargar o clonar este módulo: +> [Descargar archivo zip](https://github.com/azerothcore/mod-eluna-lua-engine/archive/master.zip) +> o clonar `git clone https://github.com/azerothcore/mod-eluna-lua-engine.git` +2. Póngalo en la carpeta de módulos del Azerothcore. +> $HOME/azerothcore/modules/ +3. Descargar o clonar el archivo central de ELUNA: +> [Descargar archivo zip](https://github.com/ElunaLuaEngine/Eluna/archive/master.zip) +> o clonar `git clone https://github.com/ElunaLuaEngine/Eluna.git .` +4. Dentro de la carpeta del módulo de Eluna de Azeroth, se encuentra una carpeta / directorio llamado: `LuaEngine` (mod-eluna-lua-engine/LuaEngine). Debe depositar los ficheros de lua, directamente dentro de esa carpeta. Los archivos directamente, no un directorio y luego los ficheros dentro. Por eso te utiliza el “.” cuando se está clonando, para que no genere un directorio nuevo. +5. Una vez copiado los ficheros y descargado el modulo, debes volver a compilar. Si seguiste la guía de instalación, debiste haber generado un directorio build, dentro de azerothcore. Dirígete a él y realiza la compilación como lo menciona en la guía. + +Eluna API : +[http://elunaluaengine.github.io/](http://elunaluaengine.github.io/) diff --git a/modules/mod-ale/_config.yml b/modules/mod-ale/_config.yml new file mode 100644 index 0000000..3397c9a --- /dev/null +++ b/modules/mod-ale/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-architect \ No newline at end of file diff --git a/modules/mod-ale/conf/mod_ale.conf.dist b/modules/mod-ale/conf/mod_ale.conf.dist new file mode 100644 index 0000000..3eca4cc --- /dev/null +++ b/modules/mod-ale/conf/mod_ale.conf.dist @@ -0,0 +1,159 @@ +[worldserver] + +################################################################################################### +# ALE SETTINGS +# +# ALE.Enabled +# Description: Enable or disable ALE LuaEngine +# Default: true - (enabled) +# false - (disabled) +# +# ALE.TraceBack +# Description: Sets whether to use debug.traceback function on a lua error or not. +# Notice that you can redefine the function. +# Default: false - (use default error output) +# true - (use debug.traceback function) +# +# ALE.ScriptPath +# Description: Sets the location of the script folder to load scripts from +# The path can be relative or absolute. +# Default: "lua_scripts" +# +# ALE.PlayerAnnounceReload +# Description: Enable or disable whether the reload announcement is sent to players (Lowest security level). +# Default: false - (disabled) +# true - (enabled) +# +# ALE.RequirePaths +# Description: Sets the location of additional require paths. +# These paths are absolute and follows the standard Lua require path patterns. +# Below are a set of "standard" paths used by most package managers. +# "/usr/share/%s/?.lua;/usr/local/share/lua/%s/?.lua;/usr/local/share/lua/%s/?/init.lua;/usr/share/lua/%s/?.lua;/usr/share/lua/%s/?/init.lua;" +# Default: "" +# +# ALE.RequireCPaths +# Description: Sets the location of additional require C paths. +# These paths are absolute and follows the standard Lua require path patterns. +# Below are a set of "standard" paths used by most package managers. +# "/usr/local/lib/lua/%s/?.so;/usr/lib/x86_64-linux-gnu/lua/%s/?.so;/usr/local/lib/lua/%s/loadall.so;" +# Default: "" +# +# ALE.AutoReload +# Description: Enable or disable automatic reloading of Lua scripts when files are modified. +# This feature watches the script directory for changes and automatically +# triggers a reload when .lua files are added, modified, or deleted. +# Useful for development but should be disabled in production environments. +# Default: false - (disabled) +# true - (enabled) +# +# ALE.AutoReloadInterval +# Description: Sets the interval in seconds between file system checks for auto-reload. +# Lower values provide faster detection but use more CPU resources. +# Higher values reduce CPU usage but increase detection delay. +# Default: 1 - (check every 1 second) +# +# ALE.BytecodeCache +# Description: Enable or disable bytecode caching for improved performance. +# When enabled, Lua/MoonScript files are compiled to bytecode and cached in memory. +# This significantly speeds up script reloading (.reload ALE). +# Cache is cleared only when files are modified or server restarts. +# Default: true - (enabled) +# false - (disabled) + +ALE.Enabled = true +ALE.TraceBack = false +ALE.ScriptPath = "lua_scripts" +ALE.PlayerAnnounceReload = false +ALE.RequirePaths = "" +ALE.RequireCPaths = "" +ALE.AutoReload = false +ALE.AutoReloadInterval = 1 +ALE.BytecodeCache = true + +################################################################################################### +# LOGGING SYSTEM SETTINGS +# +# Appender config values: Given an appender "name" +# Appender.name +# Description: Defines 'where to log'. +# Format: Type,LogLevel,Flags,optional1,optional2,optional3 +# +# Type +# 0 - (None) +# 1 - (Console) +# 2 - (File) +# 3 - (DB) +# +# LogLevel +# 0 - (Disabled) +# 1 - (Fatal) +# 2 - (Error) +# 3 - (Warning) +# 4 - (Info) +# 5 - (Debug) +# 6 - (Trace) +# +# Flags: +# 0 - None +# 1 - Prefix Timestamp to the text +# 2 - Prefix Log Level to the text +# 4 - Prefix Log Filter type to the text +# 8 - Append timestamp to the log file name. Format: YYYY-MM-DD_HH-MM-SS +# (Only used with Type = 2) +# 16 - Make a backup of existing file before overwrite +# (Only used with Mode = w) +# +# Colors (read as optional1 if Type = Console) +# Format: "fatal error warn info debug trace" +# 0 - BLACK +# 1 - RED +# 2 - GREEN +# 3 - BROWN +# 4 - BLUE +# 5 - MAGENTA +# 6 - CYAN +# 7 - GREY +# 8 - YELLOW +# 9 - LRED +# 10 - LGREEN +# 11 - LBLUE +# 12 - LMAGENTA +# 13 - LCYAN +# 14 - WHITE +# Example: "1 9 3 6 5 8" +# +# File: Name of the file (read as optional1 if Type = File) +# Allows to use one "%s" to create dynamic files +# +# Mode: Mode to open the file (read as optional2 if Type = File) +# a - (Append) +# w - (Overwrite) +# +# MaxFileSize: Maximum file size of the log file before creating a new log file +# (read as optional3 if Type = File) +# Size is measured in bytes expressed in a 64-bit unsigned integer. +# Maximum value is 4294967295 (4 GB). Leave blank for no limit. +# NOTE: Does not work with dynamic filenames. +# Example: 536870912 (512 MB) +# +Appender.ALELog=2,5,0,ALE.log,w +Appender.ALEConsole=1,4,0,"0 9 0 3 5 0" + +# Logger config values: Given a logger "name" +# Logger.name +# Description: Defines 'What to log' +# Format: LogLevel,AppenderList +# +# LogLevel +# 0 - (Disabled) +# 1 - (Fatal) +# 2 - (Error) +# 3 - (Warning) +# 4 - (Info) +# 5 - (Debug) +# 6 - (Trace) +# +# AppenderList: List of appenders linked to logger +# (Using spaces as separator). +# +Logger.ALE=4,ALELog ALEConsole diff --git a/modules/mod-ale/docs/CONTRIBUTING.md b/modules/mod-ale/docs/CONTRIBUTING.md new file mode 100644 index 0000000..2529a51 --- /dev/null +++ b/modules/mod-ale/docs/CONTRIBUTING.md @@ -0,0 +1,437 @@ +
+ +# 🤝 Contributing to ALE + +*Help improve the AzerothCore Lua Engine* + +[![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.com/invite/ZKSVREE7) +[![GitHub Issues](https://img.shields.io/badge/GitHub-Issues-181717?style=for-the-badge&logo=github&logoColor=white)](https://github.com/azerothcore/mod-ale/issues) + +--- +
+ +> [!IMPORTANT] +> We welcome contributions from everyone! Whether you code or not, there are many ways to help improve ALE. + +## 📋 Table of Contents + +- [Ways to Contribute](#-ways-to-contribute) +- [Contributing Code](#-contributing-code) +- [Reporting Issues](#-reporting-issues) +- [Improving Documentation](#-improving-documentation) +- [Creating Scripts](#-creating-scripts) +- [Development Guidelines](#-development-guidelines) + +## 🌟 Ways to Contribute + +You don't need to be a developer to contribute to ALE! Here are various ways you can help: + +### 💻 For Developers + +- **Fix Bugs**: Browse open issues and submit fixes +- **Add Features**: Implement new functionality +- **Improve Performance**: Optimize existing code +- **Write Tests**: Help ensure code quality +- **Review Pull Requests**: Provide feedback on proposed changes + +### 📚 For Writers + +- **Improve Documentation**: Fix typos, clarify explanations, add examples +- **Write Tutorials**: Create guides for common use cases +- **Translate**: Help make documentation available in other languages +- **API Documentation**: Document undocumented functions + +### 🎮 For Scripters + +- **Create Example Scripts**: Share useful script templates +- **Report Script Issues**: Help identify API problems +- **Share Best Practices**: Document scripting patterns +- **Test New Features**: Verify new functionality works as expected + +### 💬 For Community Members + +- **Answer Questions**: Help others on Discord or GitHub +- **Report Bugs**: Submit detailed issue reports +- **Test Releases**: Try pre-release versions and provide feedback +- **Spread the Word**: Share ALE with other server administrators + +## 💻 Contributing Code + +### Getting Started + +1. **[Set up Git](https://help.github.com/articles/set-up-git/)** if you haven't already + +2. **[Fork the repository](https://help.github.com/articles/fork-a-repo/)** to your GitHub account: + - Main repository: [mod-ale](https://github.com/azerothcore/mod-ale) + - Core engine: [ALE source in mod-ale](https://github.com/azerothcore/mod-ale/tree/master/src/LuaEngine) + +3. **Clone your fork** to your local machine: + ```bash + git clone https://github.com/YOUR_USERNAME/mod-ale.git + cd mod-ale + ``` + +4. **Add the upstream repository**: + ```bash + git remote add upstream https://github.com/azerothcore/mod-ale.git + ``` + +### Development Workflow + +1. **Create a feature branch** from `master`: + ```bash + git checkout master + git pull upstream master + git checkout -b feature/my-awesome-feature + ``` + +2. **Make your changes**: + - Write clean, readable code + - Follow existing code style + - Add comments where necessary + - Test your changes thoroughly + +3. **Commit your changes**: + ```bash + git add . + git commit -m "Add feature: description of what you did" + ``` + + **Commit Message Guidelines:** + - Use clear, descriptive messages + - Start with a verb (Add, Fix, Update, Remove, etc.) + - Keep the first line under 50 characters + - Add detailed explanation in subsequent lines if needed + + ``` + Add new Player:GetQuestStatus method + + This method allows scripts to check the status of a player's quest + without needing to retrieve the full quest object. This improves + performance for common quest-checking scenarios. + ``` + +4. **Push to your fork**: + ```bash + git push origin feature/my-awesome-feature + ``` + +5. **[Open a Pull Request](https://help.github.com/articles/using-pull-requests/)**: + - Go to your fork on GitHub + - Click "New Pull Request" + - Select your feature branch + - Provide a clear description of your changes + - Link any related issues + +### Code Style Guidelines + +#### C++ Code + +```cpp +// Use clear, descriptive names +int GetPlayerLevel(Player* player) +{ + if (!player) + return 0; + + return player->getLevel(); +} + +// Prefer modern C++ features +std::vector GetPlayerQuests(Player* player) +{ + std::vector quests; + // ... implementation + return quests; +} +``` + +**Key Points:** +- Use 4 spaces for indentation (no tabs) +- Opening braces on the same line for functions +- Use meaningful variable names +- Add comments for complex logic +- Follow existing patterns in the codebase + +#### Lua Code (for examples/tests) + +```lua +-- Use local variables +local QUEST_STATUS_COMPLETE = 1 + +-- Clear, descriptive function names +local function OnQuestComplete(event, player, quest) + if not player or not quest then + return + end + + local questId = quest:GetId() + print("Player", player:GetName(), "completed quest", questId) +end + +-- Register with clear event IDs +RegisterPlayerEvent(8, OnQuestComplete) -- PLAYER_EVENT_ON_QUEST_COMPLETE +``` + +### Testing Your Changes + +Before submitting a PR: + +1. **Compile the code** without errors +2. **Test basic functionality** with your changes +3. **Test edge cases** (nil values, invalid input, etc.) +4. **Verify no regressions** (existing features still work) +5. **Test on a real server** if possible + +## 🐛 Reporting Issues + +Good bug reports help us fix problems faster! + +### Before Reporting + +1. **Search existing issues** to avoid duplicates +2. **Test on latest version** to ensure it's not already fixed +3. **Gather information** about your setup and the problem + +### Creating a Good Issue Report + +Use this template: + +```markdown +**Describe the bug** +A clear description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Load script '...' +2. Trigger event '...' +3. See error + +**Expected behavior** +What you expected to happen. + +**Actual behavior** +What actually happened. + +**Environment:** +- AzerothCore version: [e.g., commit hash] +- ALE version: [e.g., commit hash] +- Lua version: [e.g., LuaJIT, Lua 5.2] +- OS: [e.g., Ubuntu 20.04] + +**Script/Code:** +```lua +-- Minimal script that reproduces the issue +local function OnLogin(event, player) + -- problematic code +end +RegisterPlayerEvent(3, OnLogin) +``` + +**Error messages:** +``` +Paste any error messages from the log here +``` + +**Additional context** +Any other information that might be helpful. +``` + +## 📖 Improving Documentation + +Documentation is crucial for helping users understand ALE. + +### What to Document + +- **API Functions**: Descriptions, parameters, return values, examples +- **Events**: When they trigger, what parameters they pass +- **Best Practices**: Common patterns and anti-patterns +- **Examples**: Real-world use cases and complete scripts + +### Documentation Workflow + +1. Fork the repository +2. Edit documentation files in the `docs/` folder +3. Use clear, concise language +4. Add code examples where helpful +5. Test any code examples you include +6. Submit a Pull Request + +### Markdown Tips + +```markdown +# Use headers for structure + +## Subsections help organize content + +**Bold** for emphasis + +`code` for inline code + +```lua +-- Code blocks for examples +``` + +> [!TIP] +> Use callouts for important information + +- Lists +- Keep +- Things +- Organized +``` + +## 🎮 Creating Scripts + +Share your scripts with the community! + +### Where to Share + +- **Discord**: [ALE Community](https://discord.com/invite/bx3y5Qmy) +- **GitHub Discussions**: For detailed explanations +- **Pull Requests**: Add example scripts to the repository + +### Script Quality Guidelines + +Good example scripts should: + +1. **Be Well Commented**: Explain what the code does +2. **Handle Edge Cases**: Check for nil values, validate input +3. **Follow Best Practices**: Use local variables, cache data, etc. +4. **Include Usage Instructions**: How to install and configure +5. **Be Self-Contained**: Include all necessary code + +### Example Template + +```lua +--[[ +Script Name: Player Welcome System +Description: Sends customized welcome message based on player level +Author: YourName +Version: 1.0 +Date: 2025-01-09 + +Installation: +1. Place this file in lua_scripts folder +2. Configure MESSAGES table below +3. Restart server + +Features: +- Different messages for different level ranges +- Personalized with player name +- Configurable message tiers +]] + +-- Configuration +local MESSAGES = { + [1] = "Welcome to the server, %s! Enjoy your adventure!", + [10] = "Welcome back, %s! You've grown stronger!", + [60] = "Greetings, veteran %s! The endgame awaits!", + [80] = "Welcome, champion %s! You've mastered your class!" +} + +-- Get appropriate message for player level +local function GetWelcomeMessage(player) + local level = player:GetLevel() + local message = MESSAGES[1] -- Default message + + -- Find highest tier message player qualifies for + for minLevel, msg in pairs(MESSAGES) do + if level >= minLevel then + message = msg + end + end + + return string.format(message, player:GetName()) +end + +-- Event handler +local function OnLogin(event, player) + if not player then + return + end + + local message = GetWelcomeMessage(player) + player:SendBroadcastMessage(message) +end + +-- Register the event +RegisterPlayerEvent(3, OnLogin) -- PLAYER_EVENT_ON_LOGIN +``` + +## 🔨 Development Guidelines + +### Project Structure + +``` +mod-ale/ +├── src/ +│ └── LuaEngine/ # Core ALE source code +│ ├── docs/ # Source documentation +│ ├── methods/ # API method implementations +│ └── hooks/ # Event hook implementations +├── docs/ # User-facing documentation +├── conf/ # Configuration file templates +└── README.md # Project overview +``` + +### Branch Strategy + +- **master**: Stable, production-ready code +- **feature/**: New features (e.g., `feature/add-new-hook`) +- **fix/**: Bug fixes (e.g., `fix/player-null-check`) +- **docs/**: Documentation updates (e.g., `docs/update-install-guide`) + +### Pull Request Guidelines + +**Good PR:** +- Single, focused change +- Clear title and description +- Links related issues +- Passes compilation +- Includes documentation updates if needed + +**PR Template:** + +```markdown +## Description +Brief description of the changes + +## Type of Change +- [ ] Bug fix +- [ ] New feature +- [ ] Documentation update +- [ ] Performance improvement + +## Testing +Describe how you tested your changes + +## Related Issues +Fixes #123 +Related to #456 + +## Checklist +- [ ] Code compiles without errors +- [ ] Tested on a running server +- [ ] Updated documentation +- [ ] Followed code style guidelines +``` + +--- + +## 🌟 Acknowledgements + +ALE is built upon the foundation of the [Eluna Lua Engine](https://github.com/ElunaLuaEngine/Eluna). We acknowledge and thank the Eluna team for their pioneering work in Lua scripting for World of Warcraft server emulators. + +- **[Original Eluna Repository](https://github.com/ElunaLuaEngine/Eluna)** +- **[Eluna Discord Community](https://discord.gg/bjkCVWqqfX)** + +--- + +
+Developed with ❤️ by the AzerothCore and ALE community + +Thank you for contributing! 🎉 + +[⬆ Back to Top](#-contributing-to-ale) +
diff --git a/modules/mod-ale/docs/IMPL_DETAILS.md b/modules/mod-ale/docs/IMPL_DETAILS.md new file mode 100644 index 0000000..7c9dd02 --- /dev/null +++ b/modules/mod-ale/docs/IMPL_DETAILS.md @@ -0,0 +1,430 @@ +
+ +# ⚙️ ALE Implementation Details + +*Advanced features and technical documentation for ALE* + +[![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.com/invite/ZKSVREE7) +[![AzerothCore](https://img.shields.io/badge/AzerothCore-Integrated-darkgreen?style=for-the-badge)](http://www.azerothcore.org/) + +--- +
+ +> [!IMPORTANT] +> This document covers advanced implementation details and best practices for ALE (AzerothCore Lua Engine). For basic usage, see the [Usage Guide](USAGE.md). + +## 📋 Table of Contents + +- [Configuration](#-configuration) +- [Script Management](#-script-management) +- [Advanced Features](#-advanced-features) +- [Database Integration](#-database-integration) +- [Performance Tips](#-performance-tips) +- [Debugging](#-debugging) + +## ⚙️ Configuration + +### Server Configuration File + +ALE settings are located in the AzerothCore server configuration file. + +> [!WARNING] +> **Important:** Always use the new configuration file generated after compiling with ALE. Without it, error logging and output may not function correctly. + +### Available Settings + +- **Enable/Disable ALE**: Toggle the Lua engine on or off +- **Traceback Function**: Enable detailed debug information in error messages +- **Script Folder Location**: Configure where ALE looks for script files +- **Logging Settings**: Control log verbosity and output destinations + +## 🔄 Script Management + +### Script Reloading + +Reload scripts during development with: + +``` +.reload ale +``` + +> [!CAUTION] +> **Development Only:** This command is for testing purposes only. For production use or troubleshooting, always restart the server. + +**Limitations:** +- Events are not re-triggered for existing entities (e.g., logged-in players) +- Some state may persist from the previous load +- Race conditions may occur with active scripts + +### Script Loading + +#### Default Behavior + +- **Default Folder**: `lua_scripts` (configurable in server config) +- **Hidden Folders**: Ignored during loading +- **File Names**: Must be unique across all subdirectories +- **Loading Order**: Not guaranteed to be alphabetical + +#### Load Priority + +Files with `.ext` extension load before standard `.lua` files: +- `init.ext` loads before `script.lua` + +> [!TIP] +> Instead of using `.ext`, prefer the standard Lua `require()` function for better maintainability. + +#### Using Require + +The entire script folder structure is added to Lua's require path: + +```lua +-- Require file: lua_scripts/utilities/helpers.lua +require("utilities/helpers") + +-- Or simply (if in root) +require("helpers") +``` + +**Note:** Omit the `.lua` extension when using `require()`. + +## 🎯 Advanced Features + +### Automatic Type Conversion + +In C++, you must explicitly cast between types: +```cpp +Unit* unit = ...; +Player* player = unit->ToPlayer(); // Manual cast required +``` + +In ALE, this happens automatically: +```lua +-- unit is automatically converted to the most specific type +-- No manual casting needed! +local name = unit:GetName() -- Works for Unit, Player, Creature, etc. +``` + +All objects are automatically converted to their most specific type, giving you full access to all available methods. + +### Storing Userdata Objects + +> [!CAUTION] +> **Critical:** Never store C++-managed userdata objects in global variables or across events! + +#### The Problem + +C++ manages object lifetimes. A stored pointer can become invalid when: +- A player logs out +- A creature despawns +- An object is deleted by the core + +Accessing invalid pointers causes crashes. + +#### The Solution + +Objects are automatically set to `nil` when they become unsafe (usually when the hook function ends). + +**Instead of storing objects:** + +```lua +-- ❌ WRONG: Don't do this +local savedPlayer = nil + +local function OnLogin(event, player) + savedPlayer = player -- Bad! Will be nil after function ends +end + +local function OnLogout(event, player) + savedPlayer:SendMessage("Test") -- CRASH! savedPlayer is nil +end +``` + +**Store GUIDs instead:** + +```lua +-- ✅ CORRECT: Store GUID and retrieve object when needed +local playerGUID = nil + +local function OnLogin(event, player) + playerGUID = player:GetGUID() +end + +local function SomeLaterEvent(event, ...) + local player = GetPlayerByGUID(playerGUID) + if player then + player:SendMessage("Test") -- Safe! + end +end +``` + +#### Safe to Store + +These userdata objects are Lua-managed and safe to store: +- Query results (`ALEQuery`) +- World packets (`WorldPacket`) +- 64-bit numbers (`uint64`, `int64`) + +### Userdata Metamethods + +#### ToString Support + +All userdata objects implement `tostring`: + +```lua +print(player) -- Outputs: Player (Name: "John", GUID: 123456) +print(creature) -- Outputs: Creature (Entry: 1234, GUID: 789012) +``` + +#### Global Metatables + +Each class has a global table containing its methods: + +```lua +-- These global tables exist: +Player = { GetName = function(...) end, ... } +Creature = { GetEntry = function(...) end, ... } +GameObject = { GetDisplayId = function(...) end, ... } +``` + +#### Custom Methods + +You can extend classes with custom methods: + +```lua +function Player:CustomGreeting() + self:SendBroadcastMessage("Welcome, " .. self:GetName() .. "!") +end + +function GameObject:IsChest() + return self:GetGoType() == 3 +end + +-- Usage: +player:CustomGreeting() +if gameobject:IsChest() then + print("Found a chest!") +end +``` + +> [!WARNING] +> Avoid modifying or deleting global class tables in normal code, as this can break other scripts. + +## 🗄️ Database Integration + +### Query Performance + +> [!IMPORTANT] +> Database queries are slow! The entire server waits while data is fetched from disk. + +#### Synchronous vs Asynchronous + +**Use `Execute` for non-SELECT queries:** +```lua +-- Asynchronous - doesn't block server +WorldDBExecute("UPDATE creature SET level = 80 WHERE entry = 1234") +``` + +**Use `Query` only when you need results:** +```lua +-- Synchronous - blocks server until complete +local result = WorldDBQuery("SELECT name FROM creature_template WHERE entry = 1234") +``` + +#### Best Practices + +1. **Cache at Startup**: Load data once during server start or script load +2. **Use Tables**: Store frequently accessed data in Lua tables +3. **Batch Operations**: Combine multiple queries when possible +4. **Async When Possible**: Use `Execute` instead of `Query` if you don't need results + +```lua +-- ✅ Good: Cache data at startup +local creatureNames = {} + +local function LoadCreatureNames() + local query = WorldDBQuery("SELECT entry, name FROM creature_template") + if query then + repeat + local entry = query:GetUInt32(0) + local name = query:GetString(1) + creatureNames[entry] = name + until not query:NextRow() + end +end + +-- Call once at server start +RegisterServerEvent(33, LoadCreatureNames) -- SERVER_EVENT_ON_CONFIG_LOAD + +-- Now use cached data +local function OnSpawn(event, creature) + local name = creatureNames[creature:GetEntry()] + print("Spawned:", name) +end +``` + +### Database Types + +> [!CAUTION] +> **Critical:** Use the correct getter function for each database type! + +MySQL performs math in specific formats. Using the wrong getter can return incorrect values on different systems. + +#### Type Mapping Table + +| Base Type | Defined Type | Database Type | Query Getter | +|---------------------------|--------------|-----------------------|-------------------| +| char | int8 | tinyint(3) | `GetInt8()` | +| short int | int16 | smallint(5) | `GetInt16()` | +| (long int / int) | int32 | mediumint(8) | `GetInt32()` | +| (long int / int) | int32 | int(10) | `GetInt32()` | +| long long int | int64 | bigint(20) | `GetInt64()` | +| unsigned char | uint8 | tinyint(3) unsigned | `GetUInt8()` | +| unsigned short int | uint16 | smallint(5) unsigned | `GetUInt16()` | +| unsigned (long int / int) | uint32 | mediumint(8) unsigned | `GetUInt32()` | +| unsigned (long int / int) | uint32 | int(10) unsigned | `GetUInt32()` | +| unsigned long long int | uint64 | bigint(20) unsigned | `GetUInt64()` | +| float | float | float | `GetFloat()` | +| double | double | double, decimal | `GetDouble()` | +| std::string | std::string | varchar, text, etc. | `GetString()` | + +#### Example + +```lua +-- ❌ WRONG: Can return 0 or 1 depending on system +local result = WorldDBQuery("SELECT 1") +local value = result:GetUInt32(0) -- Incorrect type! + +-- ✅ CORRECT: Always returns 1 +local result = WorldDBQuery("SELECT 1") +local value = result:GetInt64(0) -- Correct type for literal numbers +``` + +## ⚡ Performance Tips + +### Variable Scope + +```lua +-- ✅ Fast: Local variables +local count = 0 +for i = 1, 1000 do + count = count + 1 +end + +-- ❌ Slow: Global variables +count = 0 +for i = 1, 1000 do + count = count + 1 +end +``` + +### Table Efficiency + +```lua +-- ❌ Avoid: Creating tables in loops +for i = 1, 1000 do + local data = {i, i*2, i*3} -- 1000 table allocations! +end + +-- ✅ Better: Reuse tables +local data = {} +for i = 1, 1000 do + data[1], data[2], data[3] = i, i*2, i*3 +end +``` + +### Cache Frequently Used Values + +```lua +-- ❌ Avoid: Repeated method calls +for i = 1, 100 do + player:GetName() -- Calls C++ function 100 times +end + +-- ✅ Better: Cache the value +local playerName = player:GetName() +for i = 1, 100 do + -- Use playerName +end +``` + +### Minimize Database Access + +```lua +-- ❌ Bad: Query in a loop +for entry = 1, 100 do + local query = WorldDBQuery("SELECT name FROM creature_template WHERE entry = " .. entry) +end + +-- ✅ Good: Single query with IN clause +local query = WorldDBQuery("SELECT entry, name FROM creature_template WHERE entry BETWEEN 1 AND 100") +``` + +## 🐛 Debugging + +### Print Debugging + +```lua +-- Basic output +print("Debug: Function called") + +-- With variables +print("Player:", player:GetName(), "Level:", player:GetLevel()) + +-- Object inspection +print(player) -- Uses tostring metamethod +``` + +### Error Logs + +Check these locations for errors: +- **Server Console**: Real-time output +- **Log File**: Persistent record in server folder + +### Traceback + +Enable traceback in the server config for detailed error information: +``` +ALE.TraceBack = 1 +``` + +This adds call stack information to errors. + +### Incremental Testing + +1. **Start Small**: Test basic functionality first +2. **Add Gradually**: Implement features one at a time +3. **Test Each Step**: Verify each addition works before moving on +4. **Use Reload**: Use `.reload ale` for quick iteration (dev only) +5. **Full Restart**: Always do final testing with a server restart + +### Common Issues + +**Objects becoming nil:** +- You're storing userdata objects instead of GUIDs +- See [Storing Userdata Objects](#storing-userdata-objects) + +**Wrong database values:** +- Using incorrect getter function for database type +- See [Database Types](#database-types) + +**Script not loading:** +- Check for duplicate filenames +- Check log for syntax errors +- Verify script folder configuration + +--- + +## 🌟 Acknowledgements + +ALE is built upon the foundation of the [Eluna Lua Engine](https://github.com/ElunaLuaEngine/Eluna). We acknowledge and thank the Eluna team for their pioneering work in Lua scripting for World of Warcraft server emulators. + +- **[Original Eluna Repository](https://github.com/ElunaLuaEngine/Eluna)** +- **[Eluna Discord Community](https://discord.gg/bjkCVWqqfX)** + +--- + +
+Developed with ❤️ by the AzerothCore and ALE community + +[⬆ Back to Top](#-ale-implementation-details) +
diff --git a/modules/mod-ale/docs/INSTALL.md b/modules/mod-ale/docs/INSTALL.md new file mode 100644 index 0000000..80fa23a --- /dev/null +++ b/modules/mod-ale/docs/INSTALL.md @@ -0,0 +1,308 @@ +
+ +# 🔧 ALE Installation Guide + +*Step-by-step instructions for installing ALE on AzerothCore* + +[![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.com/invite/ZKSVREE7) +[![AzerothCore](https://img.shields.io/badge/AzerothCore-Integrated-darkgreen?style=for-the-badge)](http://www.azerothcore.org/) + +--- +
+ +> [!IMPORTANT] +> ALE is designed specifically for **AzerothCore**. If you're looking for compatibility with other emulators, check out [ElunaAzerothCore](https://github.com/Eluna-Ports/ElunaAzerothCore) for original Eluna compatibility. + +## 📋 Table of Contents + +- [Requirements](#-requirements) +- [Installation](#-installation) +- [Configuration](#-configuration) +- [Updating](#-updating) +- [Troubleshooting](#-troubleshooting) +- [Next Steps](#-next-steps) + +## ⚡ Requirements + +Before installing ALE, ensure you have: + +### System Requirements + +- **AzerothCore Server**: A working AzerothCore installation +- **Git**: Version control system for cloning repositories +- **CMake**: Build system (3.16 or higher recommended) +- **Compiler with C++11 Support**: + - Windows: Visual Studio 2019 or later + - Linux: GCC 7+ or Clang 5+ + - macOS: Xcode 10+ + +### Build Dependencies + +ALE can use either: +- **ACE** (ADAPTIVE Communication Environment), or +- **Boost** (for filesystem library) + +These should already be available if you have AzerothCore set up. + +## 🚀 Installation + +### Step 1: Navigate to Modules Directory + +Open your terminal (or Git Bash on Windows) and navigate to your AzerothCore modules directory: + +```bash +cd /modules +``` + +**Example:** +```bash +cd /home/user/azerothcore/modules +``` + +### Step 2: Clone the Repository + +Clone the mod-ale repository into your modules folder: + +```bash +git clone https://github.com/azerothcore/mod-ale.git +``` + +This will create a `mod-ale` folder containing all necessary files. + +### Step 3: Configure Build + +Navigate to your AzerothCore build directory and configure with CMake: + +```bash +cd +cmake ../ -DLUA_VERSION=luajit +``` + +#### Lua Version Options + +Choose your preferred Lua version with the `-DLUA_VERSION` flag: + +| Version | Flag | Notes | +|---------|------|-------| +| **LuaJIT** | `luajit` | **Recommended** - Best performance via JIT compilation | +| **Lua 5.2** | `lua52` | **Default** - Used if no version specified | +| **Lua 5.3** | `lua53` | Newer features, compatible | +| **Lua 5.4** | `lua54` | Latest version, all features | + +**Examples:** + +```bash +# Using LuaJIT (recommended for performance) +cmake ../ -DLUA_VERSION=luajit + +# Using Lua 5.3 +cmake ../ -DLUA_VERSION=lua53 + +# Using default (Lua 5.2) +cmake ../ +``` + +### Step 4: Compile + +Compile AzerothCore with the newly added module: + +**Linux/macOS:** +```bash +make -j$(nproc) +``` + +**Windows:** +```bash +cmake --build . --config Release +``` + +> [!TIP] +> The `-j$(nproc)` flag uses all available CPU cores for faster compilation. + +### Step 5: Update Configuration + +> [!CAUTION] +> **Critical Step:** After compiling, you must use the newly generated configuration files! + +The compilation process generates updated config files with ALE settings. Without these, ALE may not function correctly (no error messages, logging issues, etc.). + +**Location of config files:** +- Usually in your server's `etc/modules` or `configs/modules` directory +- Look for files like `mod-ale.conf` + +Copy the new `.conf.dist` files: + +```bash +# Example - adjust paths as needed +cp mod-ale.conf.dist mod-ale.conf +``` + +Then edit `worldserver.conf` and configure ALE settings (see [Configuration](#-configuration) below). + +## ⚙️ Configuration + +### ALE Settings in mod-ale.conf + +After installation, configure ALE by editing your `mod-ale.conf` file: + +```ini +################################################################################################### +# ALE (AZEROTHCORE LUA ENGINE) +################################################################################################### + +# Enable or disable ALE +# Default: 1 (enabled) +ALE.Enabled = 1 + +# Enable traceback for detailed error information +# Useful for debugging but has performance overhead +# Default: 1 (enabled) +ALE.TraceBack = 1 + +# Script folder location (relative to server binary) +# Default: "lua_scripts" +ALE.ScriptPath = "lua_scripts" +``` + +### Creating the Scripts Folder + +Create the scripts folder next to your server executable: + +```bash +mkdir lua_scripts +``` + +Place your `.lua` script files in this folder. ALE will automatically load them on server start. + +## 🔄 Updating + +Keep your ALE installation up to date with the latest features and bug fixes. + +### Update Steps + +1. **Navigate to the mod-ale directory:** + +```bash +cd /modules/mod-ale +``` + +2. **Pull the latest changes:** + +```bash +git pull +``` + +3. **Navigate to your build directory:** + +```bash +cd +``` + +4. **Reconfigure if needed (optional):** + +```bash +cmake ../ -DLUA_VERSION=luajit +``` + +5. **Recompile:** + +```bash +# Linux/macOS +make -j$(nproc) + +# Windows +cmake --build . --config Release +``` + +6. **Restart your server** to load the updated version + +> [!TIP] +> Always backup your database and scripts before updating! + +## 🔧 Troubleshooting + +### ALE Not Loading + +**Check these things:** + +1. **Config file**: Ensure you're using the new `worldserver.conf` generated after compilation +2. **Enabled setting**: Verify `ALE.Enabled = 1` in config +3. **Script path**: Ensure `lua_scripts` folder exists in the correct location +4. **Logs**: Check server logs for error messages + +### No Error Messages + +If you're not seeing ALE errors: + +- **Solution**: You're using an old config file. Copy the new `.conf.dist` file and reconfigure. + +### Compilation Errors + +**"Lua headers not found":** +- ALE should automatically download Lua dependencies +- Ensure you have internet connection during CMake configuration + +**C++11 errors:** +- Update your compiler to one that supports C++11 or later + +**ACE/Boost errors:** +- These should be installed with AzerothCore +- Check your AzerothCore installation + +### Scripts Not Loading + +1. **Check file extension**: Must be `.lua` +2. **Check file names**: Must be unique across all subdirectories +3. **Check syntax**: Look for errors in the log file +4. **Check location**: Scripts must be in the configured `ScriptPath` folder + +### Getting Help + +If you encounter issues: + +- **GitHub Issues**: [Report problems](https://github.com/azerothcore/mod-ale/issues) +- **Discord**: [Join our community](https://discord.com/invite/bx3y5Qmy) +- **AzerothCore Discord**: [Get support](http://www.azerothcore.org/) + +## 📚 Next Steps + +Now that ALE is installed, you're ready to start scripting! + +### Recommended Reading + +1. **[Usage Guide](USAGE.md)** - Learn how to write your first script +2. **[Implementation Details](IMPL_DETAILS.md)** - Advanced features and best practices +3. **[API Documentation](https://www.azerothcore.org/eluna/)** - Complete API reference +4. **[Hooks Reference](https://github.com/azerothcore/mod-ale/blob/master/src/LuaEngine/Hooks.h)** - Available event hooks + +### Example Scripts + +Create a test script to verify everything works: + +**File:** `lua_scripts/test.lua` +```lua +local function OnServerStartup() + print("ALE is working! Server started successfully.") +end + +RegisterServerEvent(33, OnServerStartup) -- SERVER_EVENT_ON_CONFIG_LOAD +``` + +Restart your server and look for the message in the console. + +--- + +## 🌟 Acknowledgements + +ALE is built upon the foundation of the [Eluna Lua Engine](https://github.com/ElunaLuaEngine/Eluna). We acknowledge and thank the Eluna team for their pioneering work in Lua scripting for World of Warcraft server emulators. + +- **[Original Eluna Repository](https://github.com/ElunaLuaEngine/Eluna)** +- **[Eluna Discord Community](https://discord.gg/bjkCVWqqfX)** + +--- + +
+Developed with ❤️ by the AzerothCore and ALE community + +[⬆ Back to Top](#-ale-installation-guide) +
diff --git a/modules/mod-ale/docs/MERGING.md b/modules/mod-ale/docs/MERGING.md new file mode 100644 index 0000000..0fa2323 --- /dev/null +++ b/modules/mod-ale/docs/MERGING.md @@ -0,0 +1,407 @@ +
+ +# 🔀 Advanced ALE Integration + +*Customizing and extending ALE for your specific needs* + +[![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.com/invite/ZKSVREE7) +[![AzerothCore](https://img.shields.io/badge/AzerothCore-Integrated-darkgreen?style=for-the-badge)](http://www.azerothcore.org/) + +--- +
+ +> [!IMPORTANT] +> ALE is distributed as an AzerothCore module. Unlike traditional forks, you don't need to merge code. However, this guide covers advanced integration scenarios for custom AzerothCore builds. + +## 📋 Table of Contents + +- [Standard Installation](#-standard-installation) +- [Custom AzerothCore Builds](#-custom-azerothcore-builds) +- [Maintaining Custom Changes](#-maintaining-custom-changes) +- [Updating ALE](#-updating-ale) +- [Contributing Back](#-contributing-back) +- [Troubleshooting](#-troubleshooting) + +## ✅ Standard Installation + +For most users, installing ALE is straightforward: + +```bash +# Navigate to modules directory +cd /modules + +# Clone ALE +git clone https://github.com/azerothcore/mod-ale.git + +# Rebuild AzerothCore +cd +cmake ../ -DLUA_VERSION=luajit +make -j$(nproc) +``` + +> [!TIP] +> If you're using the standard AzerothCore setup, see the [Installation Guide](INSTALL.md) instead. This guide is for advanced scenarios only. + +## 🔧 Custom AzerothCore Builds + +### Working with Forked AzerothCore + +If you maintain a fork of AzerothCore with custom modifications: + +#### Step 1: Add ALE as a Submodule (Recommended) + +```bash +cd + +# Add ALE as a submodule +git submodule add https://github.com/azerothcore/mod-ale.git modules/mod-ale + +# Initialize and update +git submodule init +git submodule update + +# Commit the submodule addition +git add .gitmodules modules/mod-ale +git commit -m "Add mod-ale as submodule" +``` + +**Benefits:** +- Easy updates with `git submodule update` +- Tracks specific ALE versions +- Clean separation between your code and ALE + +#### Step 2: Configure Build + +Your CMake configuration should automatically detect the module: + +```bash +cd build +cmake ../ -DLUA_VERSION=luajit +make -j$(nproc) +``` + +### Alternative: Clone Directly + +If you prefer not to use submodules: + +```bash +cd /modules +git clone https://github.com/azerothcore/mod-ale.git +``` + +> [!WARNING] +> **Not Recommended:** Avoid adding the `modules/mod-ale` directory to your fork's git repository. It makes updates more difficult. + +Add to your `.gitignore`: +``` +modules/mod-ale/ +``` + +## 🛠️ Maintaining Custom Changes + +### Modifying ALE for Your Server + +If you need to customize ALE: + +#### Option 1: Fork ALE (Recommended) + +1. **Fork the repository** on GitHub +2. **Clone your fork** instead of the official repository: + ```bash + cd modules + git clone https://github.com/YOUR_USERNAME/mod-ale.git + ``` +3. **Make your changes** in a feature branch +4. **Keep in sync** with upstream: + ```bash + git remote add upstream https://github.com/azerothcore/mod-ale.git + git fetch upstream + git merge upstream/master + ``` + +#### Option 2: Patch Files + +For small changes, create patch files: + +```bash +# Make your changes to ALE +cd modules/mod-ale + +# Create a patch +git diff > ../../patches/ale-custom-changes.patch + +# Apply the patch after updating +git pull +git apply ../../patches/ale-custom-changes.patch +``` + +### Recommended Approach: Extension Scripts + +Instead of modifying ALE core, create extension scripts: + +**File:** `lua_scripts/extensions/custom_functions.lua` +```lua +-- Extend existing classes with custom methods +function Player:CustomMethod() + -- Your custom functionality +end + +function Creature:CustomBehavior() + -- Your custom functionality +end +``` + +**Benefits:** +- No ALE core modifications needed +- Easy to update ALE +- Clean separation of custom code +- Portable across ALE versions + +## 🔄 Updating ALE + +### Standard Update Process + +For standard installations: + +```bash +cd modules/mod-ale +git pull +cd ../../build +make -j$(nproc) +``` + +### Updating with Submodules + +If you added ALE as a submodule: + +```bash +# Update to latest ALE +git submodule update --remote modules/mod-ale + +# Or update all submodules +git submodule update --remote + +# Commit the update +git add modules/mod-ale +git commit -m "Update mod-ale to latest version" +``` + +### Updating with Custom Changes + +If you maintain a fork: + +```bash +cd modules/mod-ale + +# Fetch upstream changes +git fetch upstream + +# Merge or rebase your changes +git merge upstream/master +# or +git rebase upstream/master + +# Resolve any conflicts +# Then rebuild +cd ../../build +make -j$(nproc) +``` + +### Version Pinning + +To pin a specific ALE version: + +```bash +cd modules/mod-ale + +# List available tags +git tag + +# Checkout specific version +git checkout v1.2.3 + +# Or specific commit +git checkout abc123def +``` + +> [!TIP] +> Pin versions in production environments for stability. Use latest in development for new features. + +## 🤝 Contributing Back + +If you've made improvements that could benefit others: + +### Preparing a Contribution + +1. **Ensure it's generic**: Your change should be useful for the broader community +2. **Follow coding standards**: See [Contributing Guide](CONTRIBUTING.md) +3. **Test thoroughly**: Verify your changes don't break existing functionality +4. **Document changes**: Add comments and update documentation + +### Submission Process + +1. **Fork the official repository** +2. **Create a feature branch**: + ```bash + git checkout -b feature/my-improvement + ``` +3. **Make your changes** +4. **Push to your fork**: + ```bash + git push origin feature/my-improvement + ``` +5. **Open a Pull Request** on GitHub + +See the [Contributing Guide](CONTRIBUTING.md) for detailed instructions. + +## 🔧 Troubleshooting + +### Merge Conflicts + +If you encounter conflicts when updating: + +```bash +# Check which files have conflicts +git status + +# Edit conflicting files to resolve +# Look for conflict markers: <<<<<<<, =======, >>>>>>> + +# Mark as resolved +git add + +# Complete the merge +git merge --continue +``` + +### Module Not Loading + +**Check CMake configuration:** +```bash +cd build +cmake ../ -DLUA_VERSION=luajit +``` + +Look for output like: +``` +-- ALE module found: /path/to/modules/mod-ale +``` + +**Verify module directory structure:** +``` +modules/ +└── mod-ale/ + ├── CMakeLists.txt + ├── src/ + │ └── LuaEngine/ + └── conf/ +``` + +### Build Errors After Update + +**Clean build recommended:** +```bash +cd build +rm -rf * +cmake ../ -DLUA_VERSION=luajit +make -j$(nproc) +``` + +### Submodule Issues + +**Submodule not initialized:** +```bash +git submodule init +git submodule update +``` + +**Submodule in detached HEAD state:** +```bash +cd modules/mod-ale +git checkout master +git pull +``` + +## 🎯 Advanced Scenarios + +### Multiple ALE Versions + +Testing with different ALE versions: + +```bash +# Clone to different directories +cd modules +git clone https://github.com/azerothcore/mod-ale.git mod-ale-stable +git clone https://github.com/azerothcore/mod-ale.git mod-ale-dev + +# Switch between them +ln -sf mod-ale-stable mod-ale # Use stable +ln -sf mod-ale-dev mod-ale # Use dev +``` + +### Custom Lua Version + +Using a custom Lua installation: + +```bash +cmake ../ \ + -DLUA_VERSION=custom \ + -DLUA_INCLUDE_DIR=/path/to/lua/include \ + -DLUA_LIBRARIES=/path/to/lua/lib/liblua.a +``` + +### Automated Updates + +Create an update script: + +**File:** `scripts/update-ale.sh` +```bash +#!/bin/bash + +cd modules/mod-ale || exit 1 + +echo "Fetching latest ALE..." +git fetch upstream + +echo "Current version: $(git rev-parse --short HEAD)" +echo "Latest version: $(git rev-parse --short upstream/master)" + +read -p "Update to latest? (y/n) " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + git merge upstream/master + cd ../../build + make -j$(nproc) + echo "ALE updated successfully!" +fi +``` + +## 📚 Additional Resources + +- **[Installation Guide](INSTALL.md)** - Standard installation process +- **[Contributing Guide](CONTRIBUTING.md)** - How to contribute changes +- **[Usage Guide](USAGE.md)** - Getting started with scripting +- **[AzerothCore Documentation](http://www.azerothcore.org/)** - Core documentation + +### Getting Help + +- **GitHub Issues**: [Report problems](https://github.com/azerothcore/mod-ale/issues) +- **Discord**: [Join our community](https://discord.com/invite/bx3y5Qmy) +- **AzerothCore Discord**: [Get support](http://www.azerothcore.org/) + +--- + +## 🌟 Acknowledgements + +ALE is built upon the foundation of the [Eluna Lua Engine](https://github.com/ElunaLuaEngine/Eluna). We acknowledge and thank the Eluna team for their pioneering work in Lua scripting for World of Warcraft server emulators. + +- **[Original Eluna Repository](https://github.com/ElunaLuaEngine/Eluna)** +- **[Eluna Discord Community](https://discord.gg/bjkCVWqqfX)** + +--- + +
+Developed with ❤️ by the AzerothCore and ALE community + +[⬆ Back to Top](#-advanced-ale-integration) +
diff --git a/modules/mod-ale/docs/USAGE.md b/modules/mod-ale/docs/USAGE.md new file mode 100644 index 0000000..8232bfd --- /dev/null +++ b/modules/mod-ale/docs/USAGE.md @@ -0,0 +1,279 @@ +
+ +# 📖 ALE Usage Guide + +*Learn how to create powerful Lua scripts for your AzerothCore server* + +[![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.com/invite/ZKSVREE7) +[![Lua](https://img.shields.io/badge/Lua-5.2-2C2D72?style=for-the-badge&logo=lua&logoColor=white)](http://www.lua.org/manual/5.2/) + +--- +
+ +> [!IMPORTANT] +> This guide is for **ALE (AzerothCore Lua Engine)**, which is specifically designed for AzerothCore. Scripts and APIs may differ from other Lua engines. + +## 📋 Table of Contents + +- [Prerequisites](#-prerequisites) +- [Your First Script](#-your-first-script) +- [Lua Basics](#-lua-basics) +- [ALE Basics](#-ale-basics) +- [Script Reloading](#-script-reloading) +- [Getting Help](#-getting-help) + +## ✅ Prerequisites + +This guide assumes you have already installed ALE successfully. If you have not, see the [Installation Guide](INSTALL.md) or the [Installation section](../README.md#-installation) in the README. + +## 🚀 Your First Script + +Let's create a simple "Hello World" script to get you started. + +### Creating the Script File + +Create a file named `hello_world.lua` in your scripts folder. By default, this folder is called `lua_scripts` and is located in your server folder (the folder containing server executables). + +```lua +local PLAYER_EVENT_ON_LOGIN = 3 + +local function OnLogin(event, player) + player:SendBroadcastMessage("Hello world from ALE!") +end + +RegisterPlayerEvent(PLAYER_EVENT_ON_LOGIN, OnLogin) +``` + +### Running the Script + +1. Restart your AzerothCore server +2. Log in to the game +3. You should see "Hello world from ALE!" in your chat + +### What Just Happened? + +No core compiling was needed! Your script runs directly from the file you created. + +Here's what the script does: +- The file is compiled and run by the Lua engine when the server starts +- The code registers a function to be executed when a player logs in +- When the event triggers, the function sends a message to the player + +## 📚 Lua Basics + +Before diving deep into ALE scripting, it's helpful to understand some Lua fundamentals. + +### Learning Resources + +- **Lua Users Wiki** + - [Lua Directory](http://lua-users.org/wiki/LuaDirectory) + - [Tutorial Directory](http://lua-users.org/wiki/TutorialDirectory) + - [Sample Code](http://lua-users.org/wiki/SampleCode) +- **[Programming in Lua](http://www.lua.org/pil/1.html)** - Comprehensive guide +- **[Lua 5.2 Reference Manual](http://www.lua.org/manual/5.2/)** - Official documentation + +### Essential Concepts + +#### Debugging with Print + +The `print()` function outputs to the server console - perfect for debugging: + +```lua +print("Debug message here") +print("Player name:", player:GetName()) +``` + +#### Control Structures + +Learn about loops and conditionals: +- [Control Structure Tutorial](http://lua-users.org/wiki/ControlStructureTutorial) +- [Lua Manual - Control Structures](http://www.lua.org/manual/5.2/manual.html#3.3.5) + +#### String Manipulation + +Essential for text processing: +- [String Library Tutorial](http://lua-users.org/wiki/StringLibraryTutorial) +- [Pattern Matching](http://www.wowwiki.com/Pattern_matching) + +#### Tables + +Tables are Lua's only data structure (arrays and hash maps combined): +- [Table Functions](http://www.lua.org/manual/5.2/manual.html#6.5) +- [Table Semantics](http://www.lua.org/manual/5.2/manual.html#4.3) +- [Tables Tutorial](http://lua-users.org/wiki/TablesTutorial) +- [Programming in Lua - Tables](http://www.lua.org/pil/2.5.html) + +#### Variable Scope + +**Important:** Prefer local variables over global variables! + +```lua +-- Good: Local variable +local playerName = "John" + +-- Avoid: Global variable +playerName = "John" +``` + +Global variables can create conflicts with other scripts. Local variables outside functions are shared within the same script file (locally global). + +## ⚡ ALE Basics + +### Documentation Resources + +- **[ALE API Documentation](https://www.azerothcore.org/eluna/)** - Complete API reference +- **[Hooks Documentation](https://github.com/azerothcore/mod-ale/blob/master/src/LuaEngine/Hooks.h)** - Available event hooks +- **[Implementation Details](IMPL_DETAILS.md)** - Advanced features and best practices + +### Error Messages + +ALE outputs errors to: +- Server console (real-time) +- Log file in the server folder (persistent) + +If your script doesn't work, check the log file for errors. You can configure logging settings in the server configuration file. + +### Global Functions + +Global functions can be called from anywhere without requiring an object. + +Besides standard Lua functions like `print()`, ALE provides its own global functions. Find them in the documentation under the `Global` class: [Global Functions](https://www.azerothcore.org/eluna/Global/index.html). + +```lua +-- Example: Get the Lua engine name +print(GetLuaEngine()) + +-- Example: Get current game time +local time = GetGameTime() +``` + +### Member Functions (Methods) + +Methods require a userdata object to run. Different object types (Player, Creature, GameObject, etc.) have different available methods. + +#### Class Inheritance + +Classes inherit from each other. For example: +- `Player` and `Creature` both inherit from `Unit` +- This means both players and creatures can use `Unit` methods + +#### Calling Methods + +Use the `:` notation to call methods on objects: + +```lua +-- Get player name +local name = player:GetName() + +-- Get creature level +local level = creature:GetLevel() +``` + +#### Example: Creature Combat Script + +```lua +local entry = 6 -- Creature entry ID +local on_combat = 1 -- Event ID for combat start + +local function OnCombat(event, creature, target) + -- creature is of type Creature + -- target is of type Creature or Player + + print("Creature level:", creature:GetLevel()) + print("Target level:", target:GetLevel()) + + creature:SendUnitYell("You dare challenge me?!", 0) +end + +RegisterCreatureEvent(entry, on_combat, OnCombat) +``` + +### Registering Event Handlers + +Scripts work by registering functions to events. When the event occurs, your function executes. + +#### Finding Register Functions + +Search for "register" in the [ALE documentation](https://www.azerothcore.org/eluna/) to find all available register functions: +- `RegisterPlayerEvent` +- `RegisterCreatureEvent` +- `RegisterServerEvent` +- And many more... + +#### Event IDs + +Each register function requires an event ID. Find these IDs in the function's documentation page. + +#### Function Parameters + +ALE automatically passes parameters to your registered functions. The parameters are always in the same order, but you can name them anything you want. + +Example: `PLAYER_EVENT_ON_LOGIN` passes: +1. Event ID (number) +2. Player object (Player) + +```lua +local function OnLogin(event, player) + -- event = event ID number + -- player = Player object +end +``` + +#### Return Values + +Some events allow your function to return values to modify behavior. Check the event's documentation to see what can be returned. + +```lua +local function OnChat(event, player, msg, type, lang) + if msg == "blocked" then + return false -- Blocks the chat message + end +end +``` + +## 🔄 Script Reloading + +For quick testing during development, you can reload scripts without restarting: + +``` +.reload ale +``` + +> [!WARNING] +> **Development Only:** Use `.reload ale` only for development. For production or if experiencing issues, always restart the server. + +**Important Limitations:** +- Reloading doesn't trigger events like login for already-connected players +- Some state may not reset properly +- Always do final testing with a full server restart + +## 💬 Getting Help + +### Support Channels + +- **GitHub Issues**: [Report bugs or request features](https://github.com/azerothcore/mod-ale/issues) +- **Discord Community**: [Join our Discord server](https://discord.com/invite/bx3y5Qmy) +- **AzerothCore Discord**: [Official AzerothCore support](http://www.azerothcore.org/) + +### Additional Resources + +- [Lua Programming Guide](http://www.lua.org/) +- [AzerothCore Documentation](http://www.azerothcore.org/) +- [Implementation Details](IMPL_DETAILS.md) - Advanced ALE features + +--- + +## 🌟 Acknowledgements + +ALE is built upon the foundation of the [Eluna Lua Engine](https://github.com/ElunaLuaEngine/Eluna). We acknowledge and thank the Eluna team for their pioneering work in Lua scripting for World of Warcraft server emulators. + +- **[Original Eluna Repository](https://github.com/ElunaLuaEngine/Eluna)** +- **[Eluna Discord Community](https://discord.gg/bjkCVWqqfX)** + +--- + +
+Developed with ❤️ by the AzerothCore and ALE community + +[⬆ Back to Top](#-ale-usage-guide) +
diff --git a/modules/mod-ale/icon.png b/modules/mod-ale/icon.png new file mode 100644 index 0000000..35e9047 Binary files /dev/null and b/modules/mod-ale/icon.png differ diff --git a/modules/mod-ale/include.sh b/modules/mod-ale/include.sh new file mode 100644 index 0000000..e69de29 diff --git a/modules/mod-ale/sql/README.md b/modules/mod-ale/sql/README.md new file mode 100644 index 0000000..3afd348 --- /dev/null +++ b/modules/mod-ale/sql/README.md @@ -0,0 +1,24 @@ +# BEST PRACTICES + +## Create a new table + +**Example:** +``` +CREATE TABLE IF NOT EXISTS `table`( + `id` int(11) unsigned NOT NULL, + `active` BOOLEAN DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +``` + +**Boolean datatype in mysql:** +Use "TinyInt(1)"" or "Boolean" (this is the same thing) + +"bit(1)" can also work, but it may require a syntax like b'(0) and b'(1) when inserting (not sure). + +If there are multiple booleans in the same table, bit(1) is better, otherwise it's the same result. + + +## Resources + +https://www.w3schools.com/sql/sql_datatypes.asp diff --git a/modules/mod-ale/sql/auth/.gitkeep b/modules/mod-ale/sql/auth/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/mod-ale/sql/characters/.gitkeep b/modules/mod-ale/sql/characters/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/mod-ale/sql/world/.gitkeep b/modules/mod-ale/sql/world/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/mod-ale/src/ALE_SC.cpp b/modules/mod-ale/src/ALE_SC.cpp new file mode 100644 index 0000000..d8886ce --- /dev/null +++ b/modules/mod-ale/src/ALE_SC.cpp @@ -0,0 +1,1368 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "Chat.h" +#include "ALEEventMgr.h" +#include "Log.h" +#include "LuaEngine.h" +#include "Pet.h" +#include "Player.h" +#include "ScriptMgr.h" +#include "ScriptedGossip.h" + +class ALE_AllCreatureScript : public AllCreatureScript +{ +public: + ALE_AllCreatureScript() : AllCreatureScript("ALE_AllCreatureScript") { } + + // Creature + bool CanCreatureGossipHello(Player* player, Creature* creature) override + { + if (sALE->OnGossipHello(player, creature)) + return true; + + return false; + } + + bool CanCreatureGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action) override + { + if (sALE->OnGossipSelect(player, creature, sender, action)) + return true; + + return false; + } + + bool CanCreatureGossipSelectCode(Player* player, Creature* creature, uint32 sender, uint32 action, const char* code) override + { + if (sALE->OnGossipSelectCode(player, creature, sender, action, code)) + return true; + + return false; + } + + void OnCreatureAddWorld(Creature* creature) override + { + sALE->OnAddToWorld(creature); + sALE->OnAllCreatureAddToWorld(creature); + + if (creature->IsGuardian() && creature->ToTempSummon() && creature->ToTempSummon()->GetSummonerGUID().IsPlayer()) + sALE->OnPetAddedToWorld(creature->ToTempSummon()->GetSummonerUnit()->ToPlayer(), creature); + } + + void OnCreatureRemoveWorld(Creature* creature) override + { + sALE->OnRemoveFromWorld(creature); + sALE->OnAllCreatureRemoveFromWorld(creature); + } + + bool CanCreatureQuestAccept(Player* player, Creature* creature, Quest const* quest) override + { + sALE->OnPlayerQuestAccept(player, quest); + sALE->OnQuestAccept(player, creature, quest); + return false; + } + + bool CanCreatureQuestReward(Player* player, Creature* creature, Quest const* quest, uint32 opt) override + { + if (sALE->OnQuestReward(player, creature, quest, opt)) + { + ClearGossipMenuFor(player); + return true; + } + + return false; + } + + CreatureAI* GetCreatureAI(Creature* creature) const override + { + if (CreatureAI* luaAI = sALE->GetAI(creature)) + return luaAI; + + return nullptr; + } + + void OnCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature) override + { + sALE->OnAllCreatureSelectLevel(cinfo, creature); + } + + void OnBeforeCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature, uint8& level) override + { + sALE->OnAllCreatureBeforeSelectLevel(cinfo, creature, level); + } +}; + +class ALE_AllGameObjectScript : public AllGameObjectScript +{ +public: + ALE_AllGameObjectScript() : AllGameObjectScript("ALE_AllGameObjectScript") { } + + void OnGameObjectAddWorld(GameObject* go) override + { + sALE->OnAddToWorld(go); + } + + void OnGameObjectRemoveWorld(GameObject* go) override + { + sALE->OnRemoveFromWorld(go); + } + + void OnGameObjectUpdate(GameObject* go, uint32 diff) override + { + sALE->UpdateAI(go, diff); + } + + bool CanGameObjectGossipHello(Player* player, GameObject* go) override + { + if (sALE->OnGossipHello(player, go)) + return true; + + if (sALE->OnGameObjectUse(player, go)) + return true; + + return false; + } + + void OnGameObjectDamaged(GameObject* go, Player* player) override + { + sALE->OnDamaged(go, player); + } + + void OnGameObjectDestroyed(GameObject* go, Player* player) override + { + sALE->OnDestroyed(go, player); + } + + void OnGameObjectLootStateChanged(GameObject* go, uint32 state, Unit* /*unit*/) override + { + sALE->OnLootStateChanged(go, state); + } + + void OnGameObjectStateChanged(GameObject* go, uint32 state) override + { + sALE->OnGameObjectStateChanged(go, state); + } + + bool CanGameObjectQuestAccept(Player* player, GameObject* go, Quest const* quest) override + { + sALE->OnPlayerQuestAccept(player, quest); + sALE->OnQuestAccept(player, go, quest); + return false; + } + + bool CanGameObjectGossipSelect(Player* player, GameObject* go, uint32 sender, uint32 action) override + { + if (sALE->OnGossipSelect(player, go, sender, action)) + return true; + + return false; + } + + bool CanGameObjectGossipSelectCode(Player* player, GameObject* go, uint32 sender, uint32 action, const char* code) override + { + if (sALE->OnGossipSelectCode(player, go, sender, action, code)) + return true; + + return false; + } + + bool CanGameObjectQuestReward(Player* player, GameObject* go, Quest const* quest, uint32 opt) override + { + if (sALE->OnQuestAccept(player, go, quest)) + { + sALE->OnPlayerQuestAccept(player, quest); + return false; + } + + if (sALE->OnQuestReward(player, go, quest, opt)) + return false; + + return false; + } + + GameObjectAI* GetGameObjectAI(GameObject* go) const override + { + sALE->OnSpawn(go); + return nullptr; + } +}; + +class ALE_AllItemScript : public AllItemScript +{ +public: + ALE_AllItemScript() : AllItemScript("ALE_AllItemScript") { } + + bool CanItemQuestAccept(Player* player, Item* item, Quest const* quest) override + { + if (sALE->OnQuestAccept(player, item, quest)) + { + sALE->OnPlayerQuestAccept(player, quest); + return false; + } + + return true; + } + + bool CanItemUse(Player* player, Item* item, SpellCastTargets const& targets) override + { + if (!sALE->OnUse(player, item, targets)) + return true; + + return false; + } + + bool CanItemExpire(Player* player, ItemTemplate const* proto) override + { + if (sALE->OnExpire(player, proto)) + return false; + + return true; + } + + bool CanItemRemove(Player* player, Item* item) override + { + if (sALE->OnRemove(player, item)) + return false; + + return true; + } + + void OnItemGossipSelect(Player* player, Item* item, uint32 sender, uint32 action) override + { + sALE->HandleGossipSelectOption(player, item, sender, action, ""); + } + + void OnItemGossipSelectCode(Player* player, Item* item, uint32 sender, uint32 action, const char* code) override + { + sALE->HandleGossipSelectOption(player, item, sender, action, code); + } +}; + +class ALE_AllMapScript : public AllMapScript +{ +public: + ALE_AllMapScript() : AllMapScript("ALE_AllMapScript", { + ALLMAPHOOK_ON_BEFORE_CREATE_INSTANCE_SCRIPT, + ALLMAPHOOK_ON_DESTROY_INSTANCE, + ALLMAPHOOK_ON_CREATE_MAP, + ALLMAPHOOK_ON_DESTROY_MAP, + ALLMAPHOOK_ON_PLAYER_ENTER_ALL, + ALLMAPHOOK_ON_PLAYER_LEAVE_ALL, + ALLMAPHOOK_ON_MAP_UPDATE + }) { } + + void OnBeforeCreateInstanceScript(InstanceMap* instanceMap, InstanceScript** instanceData, bool /*load*/, std::string /*data*/, uint32 /*completedEncounterMask*/) override + { + if (instanceData) + *instanceData = sALE->GetInstanceData(instanceMap); + } + + void OnDestroyInstance(MapInstanced* /*mapInstanced*/, Map* map) override + { + sALE->FreeInstanceId(map->GetInstanceId()); + } + + void OnCreateMap(Map* map) override + { + sALE->OnCreate(map); + } + + void OnDestroyMap(Map* map) override + { + sALE->OnDestroy(map); + } + + void OnPlayerEnterAll(Map* map, Player* player) override + { + sALE->OnPlayerEnter(map, player); + } + + void OnPlayerLeaveAll(Map* map, Player* player) override + { + sALE->OnPlayerLeave(map, player); + } + + void OnMapUpdate(Map* map, uint32 diff) override + { + sALE->OnUpdate(map, diff); + } +}; + +class ALE_AuctionHouseScript : public AuctionHouseScript +{ +public: + ALE_AuctionHouseScript() : AuctionHouseScript("ALE_AuctionHouseScript", { + AUCTIONHOUSEHOOK_ON_AUCTION_ADD, + AUCTIONHOUSEHOOK_ON_AUCTION_REMOVE, + AUCTIONHOUSEHOOK_ON_AUCTION_SUCCESSFUL, + AUCTIONHOUSEHOOK_ON_AUCTION_EXPIRE + }) { } + + void OnAuctionAdd(AuctionHouseObject* ah, AuctionEntry* entry) override + { + sALE->OnAdd(ah, entry); + } + + void OnAuctionRemove(AuctionHouseObject* ah, AuctionEntry* entry) override + { + sALE->OnRemove(ah, entry); + } + + void OnAuctionSuccessful(AuctionHouseObject* ah, AuctionEntry* entry) override + { + sALE->OnSuccessful(ah, entry); + } + + void OnAuctionExpire(AuctionHouseObject* ah, AuctionEntry* entry) override + { + sALE->OnExpire(ah, entry); + } +}; + +class ALE_BGScript : public BGScript +{ +public: + ALE_BGScript() : BGScript("ALE_BGScript", { + ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_START, + ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_END, + ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_DESTROY, + ALLBATTLEGROUNDHOOK_ON_BATTLEGROUND_CREATE + }) { } + + void OnBattlegroundStart(Battleground* bg) override + { + sALE->OnBGStart(bg, bg->GetBgTypeID(), bg->GetInstanceID()); + } + + void OnBattlegroundEnd(Battleground* bg, TeamId winnerTeam) override + { + sALE->OnBGEnd(bg, bg->GetBgTypeID(), bg->GetInstanceID(), winnerTeam); + } + + void OnBattlegroundDestroy(Battleground* bg) override + { + sALE->OnBGDestroy(bg, bg->GetBgTypeID(), bg->GetInstanceID()); + } + + void OnBattlegroundCreate(Battleground* bg) override + { + sALE->OnBGCreate(bg, bg->GetBgTypeID(), bg->GetInstanceID()); + } +}; + +class ALE_CommandSC : public CommandSC +{ +public: + ALE_CommandSC() : CommandSC("ALE_CommandSC", { + ALLCOMMANDHOOK_ON_TRY_EXECUTE_COMMAND + }) { } + + bool OnTryExecuteCommand(ChatHandler& handler, std::string_view cmdStr) override + { + if (!sALE->OnCommand(handler, std::string(cmdStr).c_str())) + { + return false; + } + + return true; + } +}; + +class ALE_ALEScript : public ALEScript +{ +public: + ALE_ALEScript() : ALEScript("ALE_ALEScript") { } + + // Weather + void OnWeatherChange(Weather* weather, WeatherState state, float grade) override + { + sALE->OnChange(weather, weather->GetZone(), state, grade); + } + + // AreaTriger + bool CanAreaTrigger(Player* player, AreaTrigger const* trigger) override + { + if (sALE->OnAreaTrigger(player, trigger)) + return true; + + return false; + } +}; + +class ALE_GameEventScript : public GameEventScript +{ +public: + ALE_GameEventScript() : GameEventScript("ALE_GameEventScript", { + GAMEEVENTHOOK_ON_START, + GAMEEVENTHOOK_ON_STOP + }) { } + + void OnStart(uint16 eventID) override + { + sALE->OnGameEventStart(eventID); + } + + void OnStop(uint16 eventID) override + { + sALE->OnGameEventStop(eventID); + } +}; + +class ALE_GroupScript : public GroupScript +{ +public: + ALE_GroupScript() : GroupScript("ALE_GroupScript", { + GROUPHOOK_ON_ADD_MEMBER, + GROUPHOOK_ON_INVITE_MEMBER, + GROUPHOOK_ON_REMOVE_MEMBER, + GROUPHOOK_ON_CHANGE_LEADER, + GROUPHOOK_ON_DISBAND, + GROUPHOOK_ON_CREATE + }) { } + + void OnAddMember(Group* group, ObjectGuid guid) override + { + sALE->OnAddMember(group, guid); + } + + void OnInviteMember(Group* group, ObjectGuid guid) override + { + sALE->OnInviteMember(group, guid); + } + + void OnRemoveMember(Group* group, ObjectGuid guid, RemoveMethod method, ObjectGuid /* kicker */, const char* /* reason */) override + { + sALE->OnRemoveMember(group, guid, method); + } + + void OnChangeLeader(Group* group, ObjectGuid newLeaderGuid, ObjectGuid oldLeaderGuid) override + { + sALE->OnChangeLeader(group, newLeaderGuid, oldLeaderGuid); + } + + void OnDisband(Group* group) override + { + sALE->OnDisband(group); + } + + void OnCreate(Group* group, Player* leader) override + { + sALE->OnCreate(group, leader->GetGUID(), group->GetGroupType()); + } +}; + +class ALE_GuildScript : public GuildScript +{ +public: + ALE_GuildScript() : GuildScript("ALE_GuildScript", { + GUILDHOOK_ON_ADD_MEMBER, + GUILDHOOK_ON_REMOVE_MEMBER, + GUILDHOOK_ON_MOTD_CHANGED, + GUILDHOOK_ON_INFO_CHANGED, + GUILDHOOK_ON_CREATE, + GUILDHOOK_ON_DISBAND, + GUILDHOOK_ON_MEMBER_WITDRAW_MONEY, + GUILDHOOK_ON_MEMBER_DEPOSIT_MONEY, + GUILDHOOK_ON_ITEM_MOVE, + GUILDHOOK_ON_EVENT, + GUILDHOOK_ON_BANK_EVENT + }) { } + + void OnAddMember(Guild* guild, Player* player, uint8& plRank) override + { + sALE->OnAddMember(guild, player, plRank); + } + + void OnRemoveMember(Guild* guild, Player* player, bool isDisbanding, bool /*isKicked*/) override + { + sALE->OnRemoveMember(guild, player, isDisbanding); + } + + void OnMOTDChanged(Guild* guild, const std::string& newMotd) override + { + sALE->OnMOTDChanged(guild, newMotd); + } + + void OnInfoChanged(Guild* guild, const std::string& newInfo) override + { + sALE->OnInfoChanged(guild, newInfo); + } + + void OnCreate(Guild* guild, Player* leader, const std::string& name) override + { + sALE->OnCreate(guild, leader, name); + } + + void OnDisband(Guild* guild) override + { + sALE->OnDisband(guild); + } + + void OnMemberWitdrawMoney(Guild* guild, Player* player, uint32& amount, bool isRepair) override + { + sALE->OnMemberWitdrawMoney(guild, player, amount, isRepair); + } + + void OnMemberDepositMoney(Guild* guild, Player* player, uint32& amount) override + { + sALE->OnMemberDepositMoney(guild, player, amount); + } + + void OnItemMove(Guild* guild, Player* player, Item* pItem, bool isSrcBank, uint8 srcContainer, uint8 srcSlotId, + bool isDestBank, uint8 destContainer, uint8 destSlotId) override + { + sALE->OnItemMove(guild, player, pItem, isSrcBank, srcContainer, srcSlotId, isDestBank, destContainer, destSlotId); + } + + void OnEvent(Guild* guild, uint8 eventType, ObjectGuid::LowType playerGuid1, ObjectGuid::LowType playerGuid2, uint8 newRank) override + { + sALE->OnEvent(guild, eventType, playerGuid1, playerGuid2, newRank); + } + + void OnBankEvent(Guild* guild, uint8 eventType, uint8 tabId, ObjectGuid::LowType playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) override + { + sALE->OnBankEvent(guild, eventType, tabId, playerGuid, itemOrMoney, itemStackCount, destTabId); + } +}; + +class ALE_LootScript : public LootScript +{ +public: + ALE_LootScript() : LootScript("ALE_LootScript", { + LOOTHOOK_ON_LOOT_MONEY + }) { } + + void OnLootMoney(Player* player, uint32 gold) override + { + sALE->OnLootMoney(player, gold); + } +}; + +class ALE_MiscScript : public MiscScript +{ +public: + ALE_MiscScript() : MiscScript("ALE_MiscScript", { + MISCHOOK_GET_DIALOG_STATUS + }) { } + + void GetDialogStatus(Player* player, Object* questgiver) override + { + if (questgiver->GetTypeId() == TYPEID_GAMEOBJECT) + sALE->GetDialogStatus(player, questgiver->ToGameObject()); + else if (questgiver->GetTypeId() == TYPEID_UNIT) + sALE->GetDialogStatus(player, questgiver->ToCreature()); + } +}; + +class ALE_PetScript : public PetScript +{ +public: + ALE_PetScript() : PetScript("ALE_PetScript", { + PETHOOK_ON_PET_ADD_TO_WORLD + }) { } + + void OnPetAddToWorld(Pet* pet) override + { + sALE->OnPetAddedToWorld(pet->GetOwner(), pet); + } +}; + +class ALE_PlayerScript : public PlayerScript +{ +public: + ALE_PlayerScript() : PlayerScript("ALE_PlayerScript", { + PLAYERHOOK_ON_PLAYER_RESURRECT, + PLAYERHOOK_CAN_PLAYER_USE_CHAT, + PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT, + PLAYERHOOK_CAN_PLAYER_USE_GROUP_CHAT, + PLAYERHOOK_CAN_PLAYER_USE_GUILD_CHAT, + PLAYERHOOK_CAN_PLAYER_USE_CHANNEL_CHAT, + PLAYERHOOK_ON_LOOT_ITEM, + PLAYERHOOK_ON_PLAYER_LEARN_TALENTS, + PLAYERHOOK_CAN_USE_ITEM, + PLAYERHOOK_ON_EQUIP, + PLAYERHOOK_ON_PLAYER_ENTER_COMBAT, + PLAYERHOOK_ON_PLAYER_LEAVE_COMBAT, + PLAYERHOOK_CAN_REPOP_AT_GRAVEYARD, + PLAYERHOOK_ON_QUEST_ABANDON, + PLAYERHOOK_ON_MAP_CHANGED, + PLAYERHOOK_ON_GOSSIP_SELECT, + PLAYERHOOK_ON_GOSSIP_SELECT_CODE, + PLAYERHOOK_ON_PVP_KILL, + PLAYERHOOK_ON_CREATURE_KILL, + PLAYERHOOK_ON_PLAYER_KILLED_BY_CREATURE, + PLAYERHOOK_ON_LEVEL_CHANGED, + PLAYERHOOK_ON_FREE_TALENT_POINTS_CHANGED, + PLAYERHOOK_ON_TALENTS_RESET, + PLAYERHOOK_ON_MONEY_CHANGED, + PLAYERHOOK_ON_GIVE_EXP, + PLAYERHOOK_ON_REPUTATION_CHANGE, + PLAYERHOOK_ON_DUEL_REQUEST, + PLAYERHOOK_ON_DUEL_START, + PLAYERHOOK_ON_DUEL_END, + PLAYERHOOK_ON_EMOTE, + PLAYERHOOK_ON_TEXT_EMOTE, + PLAYERHOOK_ON_SPELL_CAST, + PLAYERHOOK_ON_LOGIN, + PLAYERHOOK_ON_LOGOUT, + PLAYERHOOK_ON_CREATE, + PLAYERHOOK_ON_SAVE, + PLAYERHOOK_ON_DELETE, + PLAYERHOOK_ON_BIND_TO_INSTANCE, + PLAYERHOOK_ON_UPDATE_AREA, + PLAYERHOOK_ON_UPDATE_ZONE, + PLAYERHOOK_ON_FIRST_LOGIN, + PLAYERHOOK_ON_LEARN_SPELL, + PLAYERHOOK_ON_ACHI_COMPLETE, + PLAYERHOOK_ON_FFA_PVP_STATE_UPDATE, + PLAYERHOOK_CAN_INIT_TRADE, + PLAYERHOOK_CAN_SEND_MAIL, + PLAYERHOOK_CAN_JOIN_LFG, + PLAYERHOOK_ON_QUEST_REWARD_ITEM, + PLAYERHOOK_ON_GROUP_ROLL_REWARD_ITEM, + PLAYERHOOK_ON_CREATE_ITEM, + PLAYERHOOK_ON_STORE_NEW_ITEM, + PLAYERHOOK_ON_PLAYER_COMPLETE_QUEST, + PLAYERHOOK_CAN_GROUP_INVITE, + PLAYERHOOK_ON_BATTLEGROUND_DESERTION, + PLAYERHOOK_ON_CREATURE_KILLED_BY_PET, + PLAYERHOOK_ON_CAN_UPDATE_SKILL, + PLAYERHOOK_ON_BEFORE_UPDATE_SKILL, + PLAYERHOOK_ON_UPDATE_SKILL, + PLAYERHOOK_CAN_RESURRECT, + PLAYERHOOK_ON_PLAYER_RELEASED_GHOST + }) { } + + void OnPlayerResurrect(Player* player, float /*restore_percent*/, bool& /*applySickness*/) override + { + sALE->OnResurrect(player); + } + + bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 lang, std::string& msg) override + { + if (type != CHAT_MSG_SAY && type != CHAT_MSG_YELL && type != CHAT_MSG_EMOTE) + return true; + + if (!sALE->OnChat(player, type, lang, msg)) + return false; + + return true; + } + + bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 lang, std::string& msg, Player* target) override + { + if (!sALE->OnChat(player, type, lang, msg, target)) + return false; + + return true; + } + + bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 lang, std::string& msg, Group* group) override + { + if (!sALE->OnChat(player, type, lang, msg, group)) + return false; + + return true; + } + + bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 lang, std::string& msg, Guild* guild) override + { + if (!sALE->OnChat(player, type, lang, msg, guild)) + return false; + + return true; + } + + bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 lang, std::string& msg, Channel* channel) override + { + if (!sALE->OnChat(player, type, lang, msg, channel)) + return false; + + return true; + } + + void OnPlayerLootItem(Player* player, Item* item, uint32 count, ObjectGuid lootguid) override + { + sALE->OnLootItem(player, item, count, lootguid); + } + + void OnPlayerLearnTalents(Player* player, uint32 talentId, uint32 talentRank, uint32 spellid) override + { + sALE->OnLearnTalents(player, talentId, talentRank, spellid); + } + + bool OnPlayerCanUseItem(Player* player, ItemTemplate const* proto, InventoryResult& result) override + { + result = sALE->OnCanUseItem(player, proto->ItemId); + return result != EQUIP_ERR_OK ? false : true; + } + + void OnPlayerEquip(Player* player, Item* it, uint8 bag, uint8 slot, bool /*update*/) override + { + sALE->OnEquip(player, it, bag, slot); + } + + void OnPlayerEnterCombat(Player* player, Unit* enemy) override + { + sALE->OnPlayerEnterCombat(player, enemy); + } + + void OnPlayerLeaveCombat(Player* player) override + { + sALE->OnPlayerLeaveCombat(player); + } + + bool OnPlayerCanRepopAtGraveyard(Player* player) override + { + sALE->OnRepop(player); + return true; + } + + void OnPlayerQuestAbandon(Player* player, uint32 questId) override + { + sALE->OnQuestAbandon(player, questId); + } + + void OnPlayerMapChanged(Player* player) override + { + sALE->OnMapChanged(player); + } + + void OnPlayerGossipSelect(Player* player, uint32 menu_id, uint32 sender, uint32 action) override + { + sALE->HandleGossipSelectOption(player, menu_id, sender, action, ""); + } + + void OnPlayerGossipSelectCode(Player* player, uint32 menu_id, uint32 sender, uint32 action, const char* code) override + { + sALE->HandleGossipSelectOption(player, menu_id, sender, action, code); + } + + void OnPlayerPVPKill(Player* killer, Player* killed) override + { + sALE->OnPVPKill(killer, killed); + } + + void OnPlayerCreatureKill(Player* killer, Creature* killed) override + { + sALE->OnCreatureKill(killer, killed); + } + + void OnPlayerKilledByCreature(Creature* killer, Player* killed) override + { + sALE->OnPlayerKilledByCreature(killer, killed); + } + + void OnPlayerLevelChanged(Player* player, uint8 oldLevel) override + { + sALE->OnLevelChanged(player, oldLevel); + } + + void OnPlayerFreeTalentPointsChanged(Player* player, uint32 points) override + { + sALE->OnFreeTalentPointsChanged(player, points); + } + + void OnPlayerTalentsReset(Player* player, bool noCost) override + { + sALE->OnTalentsReset(player, noCost); + } + + void OnPlayerMoneyChanged(Player* player, int32& amount) override + { + sALE->OnMoneyChanged(player, amount); + } + + void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource) override + { + sALE->OnGiveXP(player, amount, victim, xpSource); + } + + bool OnPlayerReputationChange(Player* player, uint32 factionID, int32& standing, bool incremental) override + { + return sALE->OnReputationChange(player, factionID, standing, incremental); + } + + void OnPlayerDuelRequest(Player* target, Player* challenger) override + { + sALE->OnDuelRequest(target, challenger); + } + + void OnPlayerDuelStart(Player* player1, Player* player2) override + { + sALE->OnDuelStart(player1, player2); + } + + void OnPlayerDuelEnd(Player* winner, Player* loser, DuelCompleteType type) override + { + sALE->OnDuelEnd(winner, loser, type); + } + + void OnPlayerEmote(Player* player, uint32 emote) override + { + sALE->OnEmote(player, emote); + } + + void OnPlayerTextEmote(Player* player, uint32 textEmote, uint32 emoteNum, ObjectGuid guid) override + { + sALE->OnTextEmote(player, textEmote, emoteNum, guid); + } + + void OnPlayerSpellCast(Player* player, Spell* spell, bool skipCheck) override + { + sALE->OnPlayerSpellCast(player, spell, skipCheck); + } + + void OnPlayerLogin(Player* player) override + { + sALE->OnLogin(player); + } + + void OnPlayerLogout(Player* player) override + { + sALE->OnLogout(player); + } + + void OnPlayerCreate(Player* player) override + { + sALE->OnCreate(player); + } + + void OnPlayerSave(Player* player) override + { + sALE->OnSave(player); + } + + void OnPlayerDelete(ObjectGuid guid, uint32 /*accountId*/) override + { + sALE->OnDelete(guid.GetCounter()); + } + + void OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent) override + { + sALE->OnBindToInstance(player, difficulty, mapid, permanent); + } + + void OnPlayerUpdateArea(Player* player, uint32 oldArea, uint32 newArea) override + { + sALE->OnUpdateArea(player, oldArea, newArea); + } + + void OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newArea) override + { + sALE->OnUpdateZone(player, newZone, newArea); + } + + void OnPlayerFirstLogin(Player* player) override + { + sALE->OnFirstLogin(player); + } + + void OnPlayerLearnSpell(Player* player, uint32 spellId) override + { + sALE->OnLearnSpell(player, spellId); + } + + void OnPlayerAchievementComplete(Player* player, AchievementEntry const* achievement) override + { + sALE->OnAchiComplete(player, achievement); + } + + void OnPlayerFfaPvpStateUpdate(Player* player, bool IsFlaggedForFfaPvp) override + { + sALE->OnFfaPvpStateUpdate(player, IsFlaggedForFfaPvp); + } + + bool OnPlayerCanInitTrade(Player* player, Player* target) override + { + return sALE->OnCanInitTrade(player, target); + } + + bool OnPlayerCanSendMail(Player* player, ObjectGuid receiverGuid, ObjectGuid mailbox, std::string& subject, std::string& body, uint32 money, uint32 cod, Item* item) override + { + return sALE->OnCanSendMail(player, receiverGuid, mailbox, subject, body, money, cod, item); + } + + bool OnPlayerCanJoinLfg(Player* player, uint8 roles, lfg::LfgDungeonSet& dungeons, const std::string& comment) override + { + return sALE->OnCanJoinLfg(player, roles, dungeons, comment); + } + + void OnPlayerQuestRewardItem(Player* player, Item* item, uint32 count) override + { + sALE->OnQuestRewardItem(player, item, count); + } + + void OnPlayerGroupRollRewardItem(Player* player, Item* item, uint32 count, RollVote voteType, Roll* roll) override + { + sALE->OnGroupRollRewardItem(player, item, count, voteType, roll); + } + + void OnPlayerCreateItem(Player* player, Item* item, uint32 count) override + { + sALE->OnCreateItem(player, item, count); + } + + void OnPlayerStoreNewItem(Player* player, Item* item, uint32 count) override + { + sALE->OnStoreNewItem(player, item, count); + } + + void OnPlayerCompleteQuest(Player* player, Quest const* quest) override + { + sALE->OnPlayerCompleteQuest(player, quest); + } + + bool OnPlayerCanGroupInvite(Player* player, std::string& memberName) override + { + return sALE->OnCanGroupInvite(player, memberName); + } + + void OnPlayerBattlegroundDesertion(Player* player, const BattlegroundDesertionType type) override + { + sALE->OnBattlegroundDesertion(player, type); + } + + void OnPlayerCreatureKilledByPet(Player* player, Creature* killed) override + { + sALE->OnCreatureKilledByPet(player, killed); + } + + bool OnPlayerCanUpdateSkill(Player* player, uint32 skill_id) override + { + return sALE->OnPlayerCanUpdateSkill(player, skill_id); + } + + void OnPlayerBeforeUpdateSkill(Player* player, uint32 skill_id, uint32& value, uint32 max, uint32 step) override + { + sALE->OnPlayerBeforeUpdateSkill(player, skill_id, value, max, step); + } + + void OnPlayerUpdateSkill(Player* player, uint32 skill_id, uint32 value, uint32 max, uint32 step, uint32 new_value) override + { + sALE->OnPlayerUpdateSkill(player, skill_id, value, max, step, new_value); + } + + bool OnPlayerCanResurrect(Player* player) override + { + return sALE->CanPlayerResurrect(player); + } + + void OnPlayerReleasedGhost(Player* player) override + { + sALE->OnPlayerReleasedGhost(player); + } +}; + +class ALE_ServerScript : public ServerScript +{ +public: + ALE_ServerScript() : ServerScript("ALE_ServerScript", { + SERVERHOOK_CAN_PACKET_SEND, + SERVERHOOK_CAN_PACKET_RECEIVE + }) { } + + bool CanPacketSend(WorldSession* session, WorldPacket const& packet) override + { + if (!sALE->OnPacketSend(session, packet)) + return false; + + return true; + } + + bool CanPacketReceive(WorldSession* session, WorldPacket const& packet) override + { + if (!sALE->OnPacketReceive(session, packet)) + return false; + + return true; + } +}; + +class ALE_SpellSC : public SpellSC +{ +public: + ALE_SpellSC() : SpellSC("ALE_SpellSC", { + ALLSPELLHOOK_ON_DUMMY_EFFECT_GAMEOBJECT, + ALLSPELLHOOK_ON_DUMMY_EFFECT_CREATURE, + ALLSPELLHOOK_ON_DUMMY_EFFECT_ITEM, + ALLSPELLHOOK_ON_CAST_CANCEL, + ALLSPELLHOOK_ON_CAST, + ALLSPELLHOOK_ON_PREPARE + }) { } + + void OnDummyEffect(WorldObject* caster, uint32 spellID, SpellEffIndex effIndex, GameObject* gameObjTarget) override + { + sALE->OnDummyEffect(caster, spellID, effIndex, gameObjTarget); + } + + void OnDummyEffect(WorldObject* caster, uint32 spellID, SpellEffIndex effIndex, Creature* creatureTarget) override + { + sALE->OnDummyEffect(caster, spellID, effIndex, creatureTarget); + } + + void OnDummyEffect(WorldObject* caster, uint32 spellID, SpellEffIndex effIndex, Item* itemTarget) override + { + sALE->OnDummyEffect(caster, spellID, effIndex, itemTarget); + } + + void OnSpellCastCancel(Spell* spell, Unit* caster, SpellInfo const* spellInfo, bool bySelf) override + { + sALE->OnSpellCastCancel(caster, spell, spellInfo, bySelf); + } + + void OnSpellCast(Spell* spell, Unit* caster, SpellInfo const* spellInfo, bool skipCheck) override + { + sALE->OnSpellCast(caster, spell, spellInfo, skipCheck); + } + + void OnSpellPrepare(Spell* spell, Unit* caster, SpellInfo const* spellInfo) override + { + sALE->OnSpellPrepare(caster, spell, spellInfo); + } +}; + +class ALE_VehicleScript : public VehicleScript +{ +public: + ALE_VehicleScript() : VehicleScript("ALE_VehicleScript") { } + + void OnInstall(Vehicle* veh) override + { + sALE->OnInstall(veh); + } + + void OnUninstall(Vehicle* veh) override + { + sALE->OnUninstall(veh); + } + + void OnInstallAccessory(Vehicle* veh, Creature* accessory) override + { + sALE->OnInstallAccessory(veh, accessory); + } + + void OnAddPassenger(Vehicle* veh, Unit* passenger, int8 seatId) override + { + sALE->OnAddPassenger(veh, passenger, seatId); + } + + void OnRemovePassenger(Vehicle* veh, Unit* passenger) override + { + sALE->OnRemovePassenger(veh, passenger); + } +}; + +class ALE_WorldObjectScript : public WorldObjectScript +{ +public: + ALE_WorldObjectScript() : WorldObjectScript("ALE_WorldObjectScript", { + WORLDOBJECTHOOK_ON_WORLD_OBJECT_DESTROY, + WORLDOBJECTHOOK_ON_WORLD_OBJECT_CREATE, + WORLDOBJECTHOOK_ON_WORLD_OBJECT_SET_MAP, + WORLDOBJECTHOOK_ON_WORLD_OBJECT_UPDATE + }) { } + + void OnWorldObjectDestroy(WorldObject* object) override + { + delete object->ALEEvents; + object->ALEEvents = nullptr; + } + + void OnWorldObjectCreate(WorldObject* object) override + { + object->ALEEvents = nullptr; + } + + void OnWorldObjectSetMap(WorldObject* object, Map* /*map*/) override + { + if (!object->ALEEvents) + object->ALEEvents = new ALEEventProcessor(&ALE::GALE, object); + } + + void OnWorldObjectUpdate(WorldObject* object, uint32 diff) override + { + object->ALEEvents->Update(diff); + } +}; + +class ALE_WorldScript : public WorldScript +{ +public: + ALE_WorldScript() : WorldScript("ALE_WorldScript", { + WORLDHOOK_ON_OPEN_STATE_CHANGE, + WORLDHOOK_ON_BEFORE_CONFIG_LOAD, + WORLDHOOK_ON_AFTER_CONFIG_LOAD, + WORLDHOOK_ON_SHUTDOWN_INITIATE, + WORLDHOOK_ON_SHUTDOWN_CANCEL, + WORLDHOOK_ON_UPDATE, + WORLDHOOK_ON_STARTUP, + WORLDHOOK_ON_SHUTDOWN, + WORLDHOOK_ON_AFTER_UNLOAD_ALL_MAPS, + WORLDHOOK_ON_BEFORE_WORLD_INITIALIZED + }) { } + + void OnOpenStateChange(bool open) override + { + sALE->OnOpenStateChange(open); + } + + void OnBeforeConfigLoad(bool reload) override + { + ALEConfig::GetInstance().Initialize(reload); + if (!reload) + { + ///- Initialize Lua Engine + LOG_INFO("ALE", "Initialize ALE Lua Engine..."); + ALE::Initialize(); + } + + sALE->OnConfigLoad(reload, true); + } + + void OnAfterConfigLoad(bool reload) override + { + sALE->OnConfigLoad(reload, false); + } + + void OnShutdownInitiate(ShutdownExitCode code, ShutdownMask mask) override + { + sALE->OnShutdownInitiate(code, mask); + } + + void OnShutdownCancel() override + { + sALE->OnShutdownCancel(); + } + + void OnUpdate(uint32 diff) override + { + sALE->OnWorldUpdate(diff); + } + + void OnStartup() override + { + sALE->OnStartup(); + } + + void OnShutdown() override + { + sALE->OnShutdown(); + } + + void OnAfterUnloadAllMaps() override + { + ALE::Uninitialize(); + } + + void OnBeforeWorldInitialized() override + { + ///- Run ALE scripts. + // in multithread foreach: run scripts + sALE->RunScripts(); + sALE->OnConfigLoad(false, false); // Must be done after ALE is initialized and scripts have run. + } +}; + +class ALE_TicketScript : public TicketScript +{ +public: + ALE_TicketScript() : TicketScript("ALE_TicketScript", { + TICKETHOOK_ON_TICKET_CREATE, + TICKETHOOK_ON_TICKET_UPDATE_LAST_CHANGE, + TICKETHOOK_ON_TICKET_CLOSE, + TICKETHOOK_ON_TICKET_RESOLVE + }) { } + + void OnTicketCreate(GmTicket* ticket) override + { + sALE->OnTicketCreate(ticket); + } + + void OnTicketUpdateLastChange(GmTicket* ticket) override + { + sALE->OnTicketUpdateLastChange(ticket); + } + + void OnTicketClose(GmTicket* ticket) override + { + sALE->OnTicketClose(ticket); + } + + void OnTicketResolve(GmTicket* ticket) override + { + sALE->OnTicketResolve(ticket); + } +}; + +class ALE_UnitScript : public UnitScript +{ +public: + ALE_UnitScript() : UnitScript("ALE_UnitScript") { } + + void OnAuraApply(Unit* unit, Aura* aura) override + { + if (!unit || !aura) return; + + if (unit->IsPlayer()) + sALE->OnPlayerAuraApply(unit->ToPlayer(), aura); + + if (unit->IsCreature()) + { + sALE->OnCreatureAuraApply(unit->ToCreature(), aura); + sALE->OnAllCreatureAuraApply(unit->ToCreature(), aura); + } + } + + void OnAuraRemove(Unit* unit, AuraApplication* aurApp, AuraRemoveMode mode) override + { + if (!unit || !aurApp->GetBase()) return; + + if (unit->IsPlayer()) + sALE->OnPlayerAuraRemove(unit->ToPlayer(), aurApp->GetBase(), mode); + + if (unit->IsCreature()) + { + sALE->OnCreatureAuraRemove(unit->ToCreature(), aurApp->GetBase(), mode); + sALE->OnAllCreatureAuraRemove(unit->ToCreature(), aurApp->GetBase(), mode); + } + } + + void OnHeal(Unit* healer, Unit* receiver, uint32& gain) override + { + if (!receiver || !healer) return; + + if (healer->IsPlayer()) + sALE->OnPlayerHeal(healer->ToPlayer(), receiver, gain); + + if (healer->IsCreature()) + { + sALE->OnCreatureHeal(healer->ToCreature(), receiver, gain); + sALE->OnAllCreatureHeal(healer->ToCreature(), receiver, gain); + } + } + + void OnDamage(Unit* attacker, Unit* receiver, uint32& damage) override + { + if (!attacker || !receiver) return; + + if (attacker->IsPlayer()) + sALE->OnPlayerDamage(attacker->ToPlayer(), receiver, damage); + + if (attacker->IsCreature()) + { + sALE->OnCreatureDamage(attacker->ToCreature(), receiver, damage); + sALE->OnAllCreatureDamage(attacker->ToCreature(), receiver, damage); + } + } + + void ModifyPeriodicDamageAurasTick(Unit* target, Unit* attacker, uint32& damage, SpellInfo const* spellInfo) override + { + if (!target || !attacker) return; + + if (attacker->IsPlayer()) + sALE->OnPlayerModifyPeriodicDamageAurasTick(attacker->ToPlayer(), target, damage, spellInfo); + + if (attacker->IsCreature()) + { + sALE->OnCreatureModifyPeriodicDamageAurasTick(attacker->ToCreature(), target, damage, spellInfo); + sALE->OnAllCreatureModifyPeriodicDamageAurasTick(attacker->ToCreature(), target, damage, spellInfo); + } + } + + void ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage) override + { + if (!target || !attacker) return; + + if (attacker->IsPlayer()) + sALE->OnPlayerModifyMeleeDamage(attacker->ToPlayer(), target, damage); + + if (attacker->IsCreature()) + { + sALE->OnCreatureModifyMeleeDamage(attacker->ToCreature(), target, damage); + sALE->OnAllCreatureModifyMeleeDamage(attacker->ToCreature(), target, damage); + } + } + + void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage, SpellInfo const* spellInfo) override + { + if (!target || !attacker) return; + + if (attacker->IsPlayer()) + sALE->OnPlayerModifySpellDamageTaken(attacker->ToPlayer(), target, damage, spellInfo); + + if (attacker->IsCreature()) + { + sALE->OnCreatureModifySpellDamageTaken(attacker->ToCreature(), target, damage, spellInfo); + sALE->OnAllCreatureModifySpellDamageTaken(attacker->ToCreature(), target, damage, spellInfo); + } + } + + void ModifyHealReceived(Unit* target, Unit* healer, uint32& heal, SpellInfo const* spellInfo) override + { + if (!target || !healer) return; + + if (healer->IsPlayer()) + sALE->OnPlayerModifyHealReceived(healer->ToPlayer(), target, heal, spellInfo); + + if (healer->IsCreature()) + { + sALE->OnCreatureModifyHealReceived(healer->ToCreature(), target, heal, spellInfo); + sALE->OnAllCreatureModifyHealReceived(healer->ToCreature(), target, heal, spellInfo); + } + } + + uint32 DealDamage(Unit* AttackerUnit, Unit* pVictim, uint32 damage, DamageEffectType damagetype) override + { + if (!AttackerUnit || !pVictim) return damage; + + if (AttackerUnit->IsPlayer()) + return sALE->OnPlayerDealDamage(AttackerUnit->ToPlayer(), pVictim, damage, damagetype); + + if (AttackerUnit->IsCreature()) + return sALE->OnCreatureDealDamage(AttackerUnit->ToCreature(), pVictim, damage, damagetype); + + return damage; + } +}; + +// Group all custom scripts +void AddSC_ALE() +{ + new ALE_AllCreatureScript(); + new ALE_AllGameObjectScript(); + new ALE_AllItemScript(); + new ALE_AllMapScript(); + new ALE_AuctionHouseScript(); + new ALE_BGScript(); + new ALE_CommandSC(); + new ALE_ALEScript(); + new ALE_GameEventScript(); + new ALE_GroupScript(); + new ALE_GuildScript(); + new ALE_LootScript(); + new ALE_MiscScript(); + new ALE_PetScript(); + new ALE_PlayerScript(); + new ALE_ServerScript(); + new ALE_SpellSC(); + new ALE_TicketScript(); + new ALE_VehicleScript(); + new ALE_WorldObjectScript(); + new ALE_WorldScript(); + new ALE_UnitScript(); +} diff --git a/modules/mod-ale/src/ALE_loader.cpp b/modules/mod-ale/src/ALE_loader.cpp new file mode 100644 index 0000000..38535ba --- /dev/null +++ b/modules/mod-ale/src/ALE_loader.cpp @@ -0,0 +1,25 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +// From SC +void AddSC_ALE(); + +// Add all +void Addmod_aleScripts() +{ + AddSC_ALE(); +} diff --git a/modules/mod-ale/src/LuaEngine/.editorconfig b/modules/mod-ale/src/LuaEngine/.editorconfig new file mode 100644 index 0000000..a34283a --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/.editorconfig @@ -0,0 +1,7 @@ +[*] +charset = utf-8 +indent_style = space +indent_size = 4 +tab_width = 4 +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/modules/mod-ale/src/LuaEngine/.github/workflows/build.yml b/modules/mod-ale/src/LuaEngine/.github/workflows/build.yml new file mode 100644 index 0000000..f2bbef4 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/.github/workflows/build.yml @@ -0,0 +1,63 @@ +name: build + +on: + push: + pull_request: + +jobs: + + AC-Eluna: + strategy: + fail-fast: false + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + repository: azerothcore/azerothcore-wotlk + ref: 'master' + - uses: actions/checkout@v2 + with: + submodules: false + repository: azerothcore/mod-eluna-lua-engine + path: modules/mod-eluna-lua-engine + - uses: actions/checkout@v2 + with: + path: modules/mod-eluna-lua-engine/LuaEngine + - name: Configure OS + run: | + # Copy paste of https://github.com/azerothcore/azerothcore-wotlk/blob/master/apps/ci/ci-install.sh + + cat >>conf/config.sh <> ./conf/config.sh + echo "CCOMPILERCXX=\"clang++-11\"" >> ./conf/config.sh + - name: Import db + run: source ./apps/ci/ci-import-db.sh + - name: Build + run: source ./apps/ci/ci-compile.sh + - name: Dry run + run: source ./apps/ci/ci-worldserver-dry-run.sh + - name: Check startup errors + run: source ./apps/ci/ci-error-check.sh diff --git a/modules/mod-ale/src/LuaEngine/.github/workflows/create-pr.sh b/modules/mod-ale/src/LuaEngine/.github/workflows/create-pr.sh new file mode 100644 index 0000000..3788970 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/.github/workflows/create-pr.sh @@ -0,0 +1,36 @@ +# Adapted from https://github.com/paygoc6/action-pull-request-another-repo + +CLONE_DIR=$(mktemp -d) + +echo "Setting git variables" +export GITHUB_TOKEN=$API_TOKEN_GITHUB +git config --global user.email "$USER_EMAIL" +git config --global user.name "$USER_NAME" + +echo "Cloning destination git repository" +git clone "https://$API_TOKEN_GITHUB@github.com/$DESTINATION_REPO.git" "$CLONE_DIR" +cd "$CLONE_DIR" +git checkout "$DESTINATION_BASE_BRANCH" +git pull origin "$DESTINATION_BASE_BRANCH" +git checkout -b "$DESTINATION_HEAD_BRANCH" + +echo "Copying contents to git repo" +mkdir -p "$CLONE_DIR/$DESTINATION_FOLDER" +cp -r "$SOURCE_FOLDER/." "$CLONE_DIR/$DESTINATION_FOLDER/" + +echo "Adding files" +git add . +if git status | grep -q "Changes to be committed" +then + echo "Adding git commit" + git commit -m "$COMMIT_MESSAGE" + echo "Pushing git commit" + git push -u origin "$DESTINATION_HEAD_BRANCH" + echo "Creating a pull request" + gh pr create -t "$PR_TITLE" \ + -B "$DESTINATION_BASE_BRANCH" \ + -b "" \ + -H "$DESTINATION_HEAD_BRANCH" +else + echo "No changes detected" +fi diff --git a/modules/mod-ale/src/LuaEngine/.github/workflows/documentation.yml b/modules/mod-ale/src/LuaEngine/.github/workflows/documentation.yml new file mode 100644 index 0000000..9cdcdbd --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/.github/workflows/documentation.yml @@ -0,0 +1,38 @@ +name: Documentation +on: + push: + branches: + - 'main' + - 'master' +jobs: + Push-Docs-To-AzerothCore-Website: + runs-on: ubuntu-latest + steps: + - name: Check out repository code + uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + architecture: 'x64' + - name: Install Python dependencies + run: pip install jinja2 typedecorator markdown + - name: Compile documentation + run: | + cd ${{ github.workspace }}/docs/ + python -m ElunaDoc + - name: Create pull request + run: | + chmod +x "${GITHUB_WORKSPACE}/.github/workflows/create-pr.sh" + "${GITHUB_WORKSPACE}/.github/workflows/create-pr.sh" + env: + API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }} + SOURCE_FOLDER: '${{ github.workspace }}/docs/build' + DESTINATION_REPO: 'azerothcore/azerothcore.github.io' + DESTINATION_FOLDER: 'pages/eluna' + DESTINATION_BASE_BRANCH: 'master' + DESTINATION_HEAD_BRANCH: 'eluna-docs' + PR_TITLE: 'chore: update eluna documentation' + COMMIT_MESSAGE: 'chore: update eluna documentation' + USER_EMAIL: 'ax.cocat@gmail.com' + USER_NAME: 'r-o-b-o-t-o' diff --git a/modules/mod-ale/src/LuaEngine/ALECompat.cpp b/modules/mod-ale/src/LuaEngine/ALECompat.cpp new file mode 100644 index 0000000..18535df --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALECompat.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010 - 2025 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "ALECompat.h" + +#if LUA_VERSION_NUM == 501 +const char* luaL_tolstring(lua_State* L, int idx, size_t* len) { + if (!luaL_callmeta(L, idx, "__tostring")) { + int t = lua_type(L, idx), tt = 0; + char const* name = NULL; + switch (t) { + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + case LUA_TSTRING: + case LUA_TNUMBER: + lua_pushvalue(L, idx); + break; + case LUA_TBOOLEAN: + if (lua_toboolean(L, idx)) + lua_pushliteral(L, "true"); + else + lua_pushliteral(L, "false"); + break; + default: + tt = luaL_getmetafield(L, idx, "__name"); + name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t); + lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx)); + if (tt != LUA_TNIL) + lua_replace(L, -2); + break; + } + } + else { + if (!lua_isstring(L, -1)) + luaL_error(L, "'__tostring' must return a string"); + } + return lua_tolstring(L, -1, len); +} + +int luaL_getsubtable(lua_State* L, int i, const char* name) { + int abs_i = lua_absindex(L, i); + luaL_checkstack(L, 3, "not enough stack slots"); + lua_pushstring(L, name); + lua_gettable(L, abs_i); + if (lua_istable(L, -1)) + return 1; + lua_pop(L, 1); + lua_newtable(L); + lua_pushstring(L, name); + lua_pushvalue(L, -2); + lua_settable(L, abs_i); + return 0; +} + +int lua_absindex(lua_State* L, int i) { + if (i < 0 && i > LUA_REGISTRYINDEX) + i += lua_gettop(L) + 1; + return i; +} + +#if !defined LUAJIT_VERSION +void* luaL_testudata(lua_State* L, int index, const char* tname) { + void* ud = lua_touserdata(L, index); + if (ud) + { + if (lua_getmetatable(L, index)) + { + luaL_getmetatable(L, tname); + if (!lua_rawequal(L, -1, -2)) + ud = NULL; + lua_pop(L, 2); + return ud; + } + } + return NULL; +} + +void luaL_setmetatable(lua_State* L, const char* tname) { + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_setmetatable(L, -2); +} +#endif +#endif diff --git a/modules/mod-ale/src/LuaEngine/ALECompat.h b/modules/mod-ale/src/LuaEngine/ALECompat.h new file mode 100644 index 0000000..42d783d --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALECompat.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 - 2025 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#ifndef ALECOMPAT_H +#define ALECOMPAT_H + +extern "C" +{ +#include "lua.h" +#include "lauxlib.h" +}; + +/* Compatibility layer for compiling with Lua 5.1 or LuaJIT */ +#if LUA_VERSION_NUM == 501 + int luaL_getsubtable(lua_State* L, int i, const char* name); + const char* luaL_tolstring(lua_State* L, int idx, size_t* len); + int lua_absindex(lua_State* L, int i); + #define lua_pushglobaltable(L) \ + lua_pushvalue((L), LUA_GLOBALSINDEX) + #define lua_rawlen(L, idx) \ + lua_objlen(L, idx) + #define lua_pushunsigned(L, u) \ + lua_pushinteger(L, u) + #define lua_load(L, buf_read, dec_buf, str, NULL) \ + lua_load(L, buf_read, dec_buf, str) + + #ifndef LUA_OK + #define LUA_OK 0 + #endif + +#if !defined LUAJIT_VERSION + void* luaL_testudata(lua_State* L, int index, const char* tname); + void luaL_setmetatable(lua_State* L, const char* tname); + #define luaL_setfuncs(L, l, n) luaL_register(L, NULL, l) +#endif +#endif + +#if LUA_VERSION_NUM > 502 + #define lua_dump(L, writer, data) \ + lua_dump(L, writer, data, 0) + #define lua_pushunsigned(L, u) \ + lua_pushinteger(L, u) +#endif +#endif diff --git a/modules/mod-ale/src/LuaEngine/ALEConfig.cpp b/modules/mod-ale/src/LuaEngine/ALEConfig.cpp new file mode 100644 index 0000000..e3eca21 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEConfig.cpp @@ -0,0 +1,30 @@ +#include "ALEConfig.h" + +ALEConfig& ALEConfig::GetInstance() +{ + static ALEConfig instance; + return instance; +} + +ALEConfig::ALEConfig() : ConfigValueCache(ALEConfigValues::CONFIG_VALUE_COUNT) +{ +} + +void ALEConfig::Initialize(bool reload) +{ + ConfigValueCache::Initialize(reload); +} + +void ALEConfig::BuildConfigCache() +{ + SetConfigValue(ALEConfigValues::ENABLED, "ALE.Enabled", "false"); + SetConfigValue(ALEConfigValues::TRACEBACK_ENABLED, "ALE.TraceBack", "false"); + SetConfigValue(ALEConfigValues::AUTORELOAD_ENABLED, "ALE.AutoReload", "false"); + SetConfigValue(ALEConfigValues::BYTECODE_CACHE_ENABLED, "ALE.BytecodeCache", "false"); + + SetConfigValue(ALEConfigValues::SCRIPT_PATH, "ALE.ScriptPath", "lua_scripts"); + SetConfigValue(ALEConfigValues::REQUIRE_PATH, "ALE.RequirePaths", ""); + SetConfigValue(ALEConfigValues::REQUIRE_CPATH, "ALE.RequireCPaths", ""); + + SetConfigValue(ALEConfigValues::AUTORELOAD_INTERVAL, "ALE.AutoReloadInterval", 1); +} diff --git a/modules/mod-ale/src/LuaEngine/ALEConfig.h b/modules/mod-ale/src/LuaEngine/ALEConfig.h new file mode 100644 index 0000000..b413b4a --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEConfig.h @@ -0,0 +1,53 @@ +#ifndef ALE_CONFIG_HPP +#define ALE_CONFIG_HPP + +#include "ConfigValueCache.h" + +enum class ALEConfigValues : uint32 +{ + // Boolean + ENABLED = 0, + TRACEBACK_ENABLED, + AUTORELOAD_ENABLED, + BYTECODE_CACHE_ENABLED, + + // String + SCRIPT_PATH, + REQUIRE_PATH, + REQUIRE_CPATH, + + // Number + AUTORELOAD_INTERVAL, + + CONFIG_VALUE_COUNT +}; + +class ALEConfig final : public ConfigValueCache +{ + public: + static ALEConfig& GetInstance(); + + void Initialize(bool reload = false); + + bool IsALEEnabled() const { return GetConfigValue(ALEConfigValues::ENABLED); } + bool IsTraceBackEnabled() const { return GetConfigValue(ALEConfigValues::TRACEBACK_ENABLED); } + bool IsAutoReloadEnabled() const { return GetConfigValue(ALEConfigValues::AUTORELOAD_ENABLED); } + bool IsByteCodeCacheEnabled() const { return GetConfigValue(ALEConfigValues::BYTECODE_CACHE_ENABLED); } + + std::string_view GetScriptPath() const { return GetConfigValue(ALEConfigValues::SCRIPT_PATH); } + std::string_view GetRequirePath() const { return GetConfigValue(ALEConfigValues::REQUIRE_PATH); } + std::string_view GetRequireCPath() const { return GetConfigValue(ALEConfigValues::REQUIRE_CPATH); } + + uint32 GetAutoReloadInterval() const { return GetConfigValue(ALEConfigValues::AUTORELOAD_INTERVAL); } + + protected: + void BuildConfigCache() override; + + private: + ALEConfig(); + ~ALEConfig() = default; + ALEConfig(const ALEConfig&) = delete; + ALEConfig& operator=(const ALEConfig&) = delete; +}; + +#endif // ALE_CONFIG_H \ No newline at end of file diff --git a/modules/mod-ale/src/LuaEngine/ALECreatureAI.h b/modules/mod-ale/src/LuaEngine/ALECreatureAI.h new file mode 100644 index 0000000..cb9ccda --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALECreatureAI.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2010 - 2025 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#ifndef _ALE_CREATURE_AI_H +#define _ALE_CREATURE_AI_H + +#include "LuaEngine.h" + +struct ScriptedAI; + +struct ALECreatureAI : ScriptedAI +{ + // used to delay the spawn hook triggering on AI creation + bool justSpawned; + // used to delay movementinform hook (WP hook) + std::vector< std::pair > movepoints; + + ALECreatureAI(Creature* creature) : ScriptedAI(creature), justSpawned(true) + { + } + ~ALECreatureAI() { } + + //Called at World update tick + void UpdateAI(uint32 diff) override + { + if (justSpawned) + { + justSpawned = false; + + JustRespawned(); + } + + if (!movepoints.empty()) + { + for (auto& point : movepoints) + { + if (!sALE->MovementInform(me, point.first, point.second)) + ScriptedAI::MovementInform(point.first, point.second); + } + movepoints.clear(); + } + + if (!sALE->UpdateAI(me, diff)) + { + if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC)) + ScriptedAI::UpdateAI(diff); + } + } + + // Called for reaction when initially engaged - this will always happen _after_ JustEnteredCombat + // Called at creature aggro either by MoveInLOS or Attack Start + void JustEngagedWith(Unit* target) override + { + if (!sALE->EnterCombat(me, target)) + ScriptedAI::JustEngagedWith(target); + } + + // Called at any Damage from any attacker (before damage apply) + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override + { + if (!sALE->DamageTaken(me, attacker, damage)) + { + ScriptedAI::DamageTaken(attacker, damage, damagetype, damageSchoolMask); + } + } + + //Called at creature death + void JustDied(Unit* killer) override + { + if (!sALE->JustDied(me, killer)) + ScriptedAI::JustDied(killer); + } + + //Called at creature killing another unit + void KilledUnit(Unit* victim) override + { + if (!sALE->KilledUnit(me, victim)) + ScriptedAI::KilledUnit(victim); + } + + // Called when the creature summon successfully other creature + void JustSummoned(Creature* summon) override + { + if (!sALE->JustSummoned(me, summon)) + ScriptedAI::JustSummoned(summon); + } + + // Called when a summoned creature is despawned + void SummonedCreatureDespawn(Creature* summon) override + { + if (!sALE->SummonedCreatureDespawn(me, summon)) + ScriptedAI::SummonedCreatureDespawn(summon); + } + + //Called at waypoint reached or PointMovement end + void MovementInform(uint32 type, uint32 id) override + { + // delayed since hook triggers before actually reaching the point + // and starting new movement would bug + movepoints.push_back(std::make_pair(type, id)); + } + + // Called before EnterCombat even before the creature is in combat. + void AttackStart(Unit* target) override + { + if (!sALE->AttackStart(me, target)) + ScriptedAI::AttackStart(target); + } + + // Called for reaction at stopping attack at no attackers or targets + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (!sALE->EnterEvadeMode(me)) + ScriptedAI::EnterEvadeMode(); + } + + // Called when creature is spawned or respawned (for reseting variables) + void JustRespawned() override + { + if (!sALE->JustRespawned(me)) + ScriptedAI::JustRespawned(); + } + + // Called at reaching home after evade + void JustReachedHome() override + { + if (!sALE->JustReachedHome(me)) + ScriptedAI::JustReachedHome(); + } + + // Called at text emote receive from player + void ReceiveEmote(Player* player, uint32 emoteId) override + { + if (!sALE->ReceiveEmote(me, player, emoteId)) + ScriptedAI::ReceiveEmote(player, emoteId); + } + + // called when the corpse of this creature gets removed + void CorpseRemoved(uint32& respawnDelay) override + { + if (!sALE->CorpseRemoved(me, respawnDelay)) + ScriptedAI::CorpseRemoved(respawnDelay); + } + + void MoveInLineOfSight(Unit* who) override + { + if (!sALE->MoveInLineOfSight(me, who)) + ScriptedAI::MoveInLineOfSight(who); + } + + // Called when hit by a spell + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (!sALE->SpellHit(me, caster, spell)) + ScriptedAI::SpellHit(caster, spell); + } + + // Called when spell hits a target + void SpellHitTarget(Unit* target, SpellInfo const* spell) override + { + if (!sALE->SpellHitTarget(me, target, spell)) + ScriptedAI::SpellHitTarget(target, spell); + } + + // Called when the creature is summoned successfully by other creature + void IsSummonedBy(WorldObject* summoner) override + { + if (!summoner->ToUnit() || !sALE->OnSummoned(me, summoner->ToUnit())) + ScriptedAI::IsSummonedBy(summoner); + } + + void SummonedCreatureDies(Creature* summon, Unit* killer) override + { + if (!sALE->SummonedCreatureDies(me, summon, killer)) + ScriptedAI::SummonedCreatureDies(summon, killer); + } + + // Called when owner takes damage + void OwnerAttackedBy(Unit* attacker) override + { + if (!sALE->OwnerAttackedBy(me, attacker)) + ScriptedAI::OwnerAttackedBy(attacker); + } + + // Called when owner attacks something + void OwnerAttacked(Unit* target) override + { + if (!sALE->OwnerAttacked(me, target)) + ScriptedAI::OwnerAttacked(target); + } +}; + +#endif diff --git a/modules/mod-ale/src/LuaEngine/ALEDBCRegistry.cpp b/modules/mod-ale/src/LuaEngine/ALEDBCRegistry.cpp new file mode 100644 index 0000000..4acc311 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEDBCRegistry.cpp @@ -0,0 +1,7 @@ +#include "ALEDBCRegistry.h" + +std::vector dbcRegistry = { + REGISTER_DBC(GemProperties, GemPropertiesEntry, sGemPropertiesStore), + REGISTER_DBC(Spell, SpellEntry, sSpellStore), +}; + diff --git a/modules/mod-ale/src/LuaEngine/ALEDBCRegistry.h b/modules/mod-ale/src/LuaEngine/ALEDBCRegistry.h new file mode 100644 index 0000000..b525ce8 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEDBCRegistry.h @@ -0,0 +1,38 @@ +#ifndef ALEDBCREGISTRY_H +#define ALEDBCREGISTRY_H + +#include +#include +#include +#include + +#include "DBCStores.h" +#include "LuaEngine.h" + +struct DBCDefinition +{ + std::string name; + void* storage; + const std::type_info& type; + std::function lookupFunction; + std::function pushFunction; +}; + +extern std::vector dbcRegistry; + +#define REGISTER_DBC(dbcName, entryType, store) \ + { \ + #dbcName, \ + reinterpret_cast(&store), \ + typeid(DBCStorage), \ + [](uint32 id) -> const void* { \ + return store.LookupEntry(id); \ + }, \ + [](lua_State* L, const void* entry) { \ + auto cast_entry = static_cast(entry); \ + ALE::Push(L, *cast_entry); \ + } \ + } + +#endif // ALEDBCREGISTRY_H + diff --git a/modules/mod-ale/src/LuaEngine/ALEEventMgr.cpp b/modules/mod-ale/src/LuaEngine/ALEEventMgr.cpp new file mode 100644 index 0000000..f90cee3 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEEventMgr.cpp @@ -0,0 +1,160 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#include "ALEEventMgr.h" +#include "LuaEngine.h" +#include "Object.h" + +extern "C" +{ +#include "lua.h" +#include "lauxlib.h" +}; + +ALEEventProcessor::ALEEventProcessor(ALE** _E, WorldObject* _obj) : m_time(0), obj(_obj), E(_E) +{ + // can be called from multiple threads + if (obj) + { + EventMgr::Guard guard((*E)->eventMgr->GetLock()); + (*E)->eventMgr->processors.insert(this); + } +} + +ALEEventProcessor::~ALEEventProcessor() +{ + // can be called from multiple threads + { + LOCK_ALE; + RemoveEvents_internal(); + } + + if (obj && ALE::IsInitialized()) + { + EventMgr::Guard guard((*E)->eventMgr->GetLock()); + (*E)->eventMgr->processors.erase(this); + } +} + +void ALEEventProcessor::Update(uint32 diff) +{ + m_time += diff; + for (EventList::iterator it = eventList.begin(); it != eventList.end() && it->first <= m_time; it = eventList.begin()) + { + LuaEvent* luaEvent = it->second; + eventList.erase(it); + + if (luaEvent->state != LUAEVENT_STATE_ERASE) + eventMap.erase(luaEvent->funcRef); + + if (luaEvent->state == LUAEVENT_STATE_RUN) + { + uint32 delay = luaEvent->delay; + bool remove = luaEvent->repeats == 1; + if (!remove) + AddEvent(luaEvent); // Reschedule before calling incase RemoveEvents used + + // Call the timed event + (*E)->OnTimedEvent(luaEvent->funcRef, delay, luaEvent->repeats ? luaEvent->repeats-- : luaEvent->repeats, obj); + + if (!remove) + continue; + } + + // Event should be deleted (executed last time or set to be aborted) + RemoveEvent(luaEvent); + } +} + +void ALEEventProcessor::SetStates(LuaEventState state) +{ + for (EventList::iterator it = eventList.begin(); it != eventList.end(); ++it) + it->second->SetState(state); + if (state == LUAEVENT_STATE_ERASE) + eventMap.clear(); +} + +void ALEEventProcessor::RemoveEvents_internal() +{ + //if (!final) + //{ + // for (EventList::iterator it = eventList.begin(); it != eventList.end(); ++it) + // it->second->to_Abort = true; + // return; + //} + + for (EventList::iterator it = eventList.begin(); it != eventList.end(); ++it) + RemoveEvent(it->second); + + eventList.clear(); + eventMap.clear(); +} + +void ALEEventProcessor::SetState(int eventId, LuaEventState state) +{ + if (eventMap.find(eventId) != eventMap.end()) + eventMap[eventId]->SetState(state); + if (state == LUAEVENT_STATE_ERASE) + eventMap.erase(eventId); +} + +void ALEEventProcessor::AddEvent(LuaEvent* luaEvent) +{ + luaEvent->GenerateDelay(); + eventList.insert(std::pair(m_time + luaEvent->delay, luaEvent)); + eventMap[luaEvent->funcRef] = luaEvent; +} + +void ALEEventProcessor::AddEvent(int funcRef, uint32 min, uint32 max, uint32 repeats) +{ + AddEvent(new LuaEvent(funcRef, min, max, repeats)); +} + +void ALEEventProcessor::RemoveEvent(LuaEvent* luaEvent) +{ + // Unreference if should and if ALE was not yet uninitialized and if the lua state still exists + if (luaEvent->state != LUAEVENT_STATE_ERASE && ALE::IsInitialized() && (*E)->HasLuaState()) + { + // Free lua function ref + luaL_unref((*E)->L, LUA_REGISTRYINDEX, luaEvent->funcRef); + } + delete luaEvent; +} + +EventMgr::EventMgr(ALE** _E) : globalProcessor(new ALEEventProcessor(_E, NULL)), E(_E) +{ +} + +EventMgr::~EventMgr() +{ + { + Guard guard(GetLock()); + if (!processors.empty()) + for (ProcessorSet::const_iterator it = processors.begin(); it != processors.end(); ++it) // loop processors + (*it)->RemoveEvents_internal(); + globalProcessor->RemoveEvents_internal(); + } + delete globalProcessor; + globalProcessor = NULL; +} + +void EventMgr::SetStates(LuaEventState state) +{ + Guard guard(GetLock()); + if (!processors.empty()) + for (ProcessorSet::const_iterator it = processors.begin(); it != processors.end(); ++it) // loop processors + (*it)->SetStates(state); + globalProcessor->SetStates(state); +} + +void EventMgr::SetState(int eventId, LuaEventState state) +{ + Guard guard(GetLock()); + if (!processors.empty()) + for (ProcessorSet::const_iterator it = processors.begin(); it != processors.end(); ++it) // loop processors + (*it)->SetState(eventId, state); + globalProcessor->SetState(eventId, state); +} diff --git a/modules/mod-ale/src/LuaEngine/ALEEventMgr.h b/modules/mod-ale/src/LuaEngine/ALEEventMgr.h new file mode 100644 index 0000000..5302ece --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEEventMgr.h @@ -0,0 +1,104 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ALE_EVENT_MGR_H +#define _ALE_EVENT_MGR_H + +#include "ALEUtility.h" +#include "Common.h" +#include "Util.h" +#include + +#include "Define.h" + +class ALE; +class EventMgr; +class ALEEventProcessor; +class WorldObject; + +enum LuaEventState +{ + LUAEVENT_STATE_RUN, // On next call run the function normally + LUAEVENT_STATE_ABORT, // On next call unregisters reffed function and erases the data + LUAEVENT_STATE_ERASE, // On next call just erases the data +}; + +struct LuaEvent +{ + LuaEvent(int _funcRef, uint32 _min, uint32 _max, uint32 _repeats) : + min(_min), max(_max), delay(0), repeats(_repeats), funcRef(_funcRef), state(LUAEVENT_STATE_RUN) + { + } + + void SetState(LuaEventState _state) + { + if (state != LUAEVENT_STATE_ERASE) + state = _state; + } + + void GenerateDelay() + { + delay = urand(min, max); + } + + uint32 min; // Minimum delay between event calls + uint32 max; // Maximum delay between event calls + uint32 delay; // The currently used waiting time + uint32 repeats; // Amount of repeats to make, 0 for infinite + int funcRef; // Lua function reference ID, also used as event ID + LuaEventState state; // State for next call +}; + +class ALEEventProcessor +{ + friend class EventMgr; + +public: + typedef std::multimap EventList; + typedef std::unordered_map EventMap; + + ALEEventProcessor(ALE** _E, WorldObject* _obj); + ~ALEEventProcessor(); + + void Update(uint32 diff); + // removes all timed events on next tick or at tick end + void SetStates(LuaEventState state); + // set the event to be removed when executing + void SetState(int eventId, LuaEventState state); + void AddEvent(int funcRef, uint32 min, uint32 max, uint32 repeats); + EventMap eventMap; + +private: + void RemoveEvents_internal(); + void AddEvent(LuaEvent* luaEvent); + void RemoveEvent(LuaEvent* luaEvent); + EventList eventList; + uint64 m_time; + WorldObject* obj; + ALE** E; +}; + +class EventMgr : public ALEUtil::Lockable +{ +public: + typedef std::unordered_set ProcessorSet; + ProcessorSet processors; + ALEEventProcessor* globalProcessor; + ALE** E; + + EventMgr(ALE** _E); + ~EventMgr(); + + // Set the state of all timed events + // Execute only in safe env + void SetStates(LuaEventState state); + + // Sets the eventId's state in all processors + // Execute only in safe env + void SetState(int eventId, LuaEventState state); +}; + +#endif diff --git a/modules/mod-ale/src/LuaEngine/ALEFileWatcher.cpp b/modules/mod-ale/src/LuaEngine/ALEFileWatcher.cpp new file mode 100644 index 0000000..aa3cd37 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEFileWatcher.cpp @@ -0,0 +1,216 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#include "ALEFileWatcher.h" +#include "LuaEngine.h" +#include "ALEUtility.h" +#include + +ALEFileWatcher::ALEFileWatcher() : running(false), checkInterval(1) +{ +} + +ALEFileWatcher::~ALEFileWatcher() +{ + StopWatching(); +} + +void ALEFileWatcher::StartWatching(const std::string& scriptPath, uint32 intervalSeconds) +{ + if (running.load()) + { + ALE_LOG_DEBUG("[ALEFileWatcher]: Already watching files"); + return; + } + + if (scriptPath.empty()) + { + ALE_LOG_ERROR("[ALEFileWatcher]: Cannot start watching - script path is empty"); + return; + } + + watchPath = scriptPath; + checkInterval = intervalSeconds; + running.store(true); + + ScanDirectory(watchPath); + + watcherThread = std::thread(&ALEFileWatcher::WatchLoop, this); + + ALE_LOG_INFO("[ALEFileWatcher]: Started watching '{}' (interval: {}s)", watchPath, checkInterval); +} + +void ALEFileWatcher::StopWatching() +{ + if (!running.load()) + return; + + running.store(false); + + if (watcherThread.joinable()) + watcherThread.join(); + + fileTimestamps.clear(); + + ALE_LOG_INFO("[ALEFileWatcher]: Stopped watching files"); +} + +void ALEFileWatcher::WatchLoop() +{ + while (running.load()) + { + try + { + CheckForChanges(); + } + catch (const std::exception& e) + { + ALE_LOG_ERROR("[ALEFileWatcher]: Error during file watching: {}", e.what()); + } + + std::this_thread::sleep_for(std::chrono::seconds(checkInterval)); + } +} + +bool ALEFileWatcher::IsWatchedFileType(const std::string& filename) { + return (filename.length() >= 4 && filename.substr(filename.length() - 4) == ".lua") || + (filename.length() >= 4 && filename.substr(filename.length() - 4) == ".ext") || + (filename.length() >= 5 && filename.substr(filename.length() - 5) == ".moon"); +} + +void ALEFileWatcher::ScanDirectory(const std::string& path) +{ + try + { + boost::filesystem::path dir(path); + + if (!boost::filesystem::exists(dir) || !boost::filesystem::is_directory(dir)) + return; + + boost::filesystem::directory_iterator end_iter; + + for (boost::filesystem::directory_iterator dir_iter(dir); dir_iter != end_iter; ++dir_iter) + { + std::string fullpath = dir_iter->path().generic_string(); + + if (boost::filesystem::is_directory(dir_iter->status())) + { + ScanDirectory(fullpath); + } + else if (boost::filesystem::is_regular_file(dir_iter->status())) + { + std::string filename = dir_iter->path().filename().generic_string(); + + if (IsWatchedFileType(filename)) + { + fileTimestamps[fullpath] = boost::filesystem::last_write_time(dir_iter->path()); + } + } + } + } + catch (const std::exception& e) + { + ALE_LOG_ERROR("[ALEFileWatcher]: Error scanning directory '{}': {}", path, e.what()); + } +} + +void ALEFileWatcher::CheckForChanges() +{ + bool hasChanges = false; + + try + { + boost::filesystem::path dir(watchPath); + + if (!boost::filesystem::exists(dir) || !boost::filesystem::is_directory(dir)) + return; + + boost::filesystem::directory_iterator end_iter; + + for (boost::filesystem::directory_iterator dir_iter(dir); dir_iter != end_iter; ++dir_iter) + { + if (ShouldReloadFile(dir_iter->path().generic_string())) + hasChanges = true; + } + + for (auto it = fileTimestamps.begin(); it != fileTimestamps.end();) + { + if (!boost::filesystem::exists(it->first)) + { + ALE_LOG_DEBUG("[ALEFileWatcher]: File deleted: {}", it->first); + it = fileTimestamps.erase(it); + hasChanges = true; + } + else + { + ++it; + } + } + } + catch (const std::exception& e) + { + ALE_LOG_ERROR("[ALEFileWatcher]: Error checking for changes: {}", e.what()); + return; + } + + if (hasChanges) + { + ALE_LOG_INFO("[ALEFileWatcher]: Lua script changes detected - triggering reload"); + ALE::ReloadALE(); + + ScanDirectory(watchPath); + } +} + +bool ALEFileWatcher::ShouldReloadFile(const std::string& filepath) +{ + try + { + boost::filesystem::path file(filepath); + + if (boost::filesystem::is_directory(file)) + { + boost::filesystem::directory_iterator end_iter; + + for (boost::filesystem::directory_iterator dir_iter(file); dir_iter != end_iter; ++dir_iter) + { + if (ShouldReloadFile(dir_iter->path().generic_string())) + return true; + } + return false; + } + + if (!boost::filesystem::is_regular_file(file)) + return false; + + std::string filename = file.filename().generic_string(); + + if (!IsWatchedFileType(filename)) return false; + + auto currentTime = boost::filesystem::last_write_time(file); + auto it = fileTimestamps.find(filepath); + + if (it == fileTimestamps.end()) + { + ALE_LOG_DEBUG("[ALEFileWatcher]: New file detected: {}", filepath); + fileTimestamps[filepath] = currentTime; + return true; + } + + if (it->second != currentTime) + { + ALE_LOG_DEBUG("[ALEFileWatcher]: File modified: {}", filepath); + it->second = currentTime; + return true; + } + } + catch (const std::exception& e) + { + ALE_LOG_ERROR("[ALEFileWatcher]: Error checking file '{}': {}", filepath, e.what()); + } + + return false; +} diff --git a/modules/mod-ale/src/LuaEngine/ALEFileWatcher.h b/modules/mod-ale/src/LuaEngine/ALEFileWatcher.h new file mode 100644 index 0000000..75364a6 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEFileWatcher.h @@ -0,0 +1,43 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef ALE_FILE_WATCHER_H +#define ALE_FILE_WATCHER_H + +#include +#include +#include +#include +#include +#include +#include "Common.h" + +class ALEFileWatcher +{ +public: + ALEFileWatcher(); + ~ALEFileWatcher(); + + void StartWatching(const std::string& scriptPath, uint32 intervalSeconds = 1); + void StopWatching(); + bool IsWatching() const { return running.load(); } + +private: + void WatchLoop(); + void ScanDirectory(const std::string& path); + void CheckForChanges(); + bool ShouldReloadFile(const std::string& filepath); + bool IsWatchedFileType(const std::string& filename); + + std::thread watcherThread; + std::atomic running; + std::string watchPath; + uint32 checkInterval; + + std::map fileTimestamps; +}; + +#endif diff --git a/modules/mod-ale/src/LuaEngine/ALEIncludes.h b/modules/mod-ale/src/LuaEngine/ALEIncludes.h new file mode 100644 index 0000000..bc38f08 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEIncludes.h @@ -0,0 +1,77 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ALE_INCLUDES_H +#define _ALE_INCLUDES_H + +// Required +#include "AccountMgr.h" +#include "AuctionHouseMgr.h" +#include "Cell.h" +#include "CellImpl.h" +#include "Chat.h" +#include "Channel.h" +#include "DBCStores.h" +#include "GameEventMgr.h" +#include "GossipDef.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "Group.h" +#include "Guild.h" +#include "GuildMgr.h" +#include "Language.h" +#include "Mail.h" +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "Opcodes.h" +#include "Player.h" +#include "Pet.h" +#include "ReputationMgr.h" +#include "ScriptMgr.h" +#include "Spell.h" +#include "SpellAuras.h" +#include "SpellInfo.h" +#include "SpellMgr.h" +#include "TemporarySummon.h" +#include "Transport.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "MapMgr.h" +#include "Config.h" +#include "GameEventMgr.h" +#include "GitRevision.h" +#include "GroupMgr.h" +#include "ScriptedCreature.h" +#include "WeatherMgr.h" +#include "Battleground.h" +#include "MotionMaster.h" +#include "DatabaseEnv.h" +#include "Bag.h" +#include "Vehicle.h" +#include "ArenaTeam.h" +#include "WorldSessionMgr.h" + +typedef Opcodes OpcodesList; + +/* + * Note: if you add or change a CORE_NAME or CORE_VERSION #define, + * please update LuaGlobalFunctions::GetCoreName or LuaGlobalFunctions::GetCoreVersion documentation example string. + */ +#define CORE_NAME "AzerothCore" + +#define CORE_VERSION (GitRevision::GetFullVersion()) +#define eWorldSessionMgr (sWorldSessionMgr) +#define eWorld (sWorld) +#define eMapMgr (sMapMgr) +#define eConfigMgr (sConfigMgr) +#define eGuildMgr (sGuildMgr) +#define eObjectMgr (sObjectMgr) +#define eAccountMgr (sAccountMgr) +#define eAuctionMgr (sAuctionMgr) +#define eGameEventMgr (sGameEventMgr) +#define eObjectAccessor() ObjectAccessor:: + +#endif // _ALE_INCLUDES_H diff --git a/modules/mod-ale/src/LuaEngine/ALEInstanceAI.cpp b/modules/mod-ale/src/LuaEngine/ALEInstanceAI.cpp new file mode 100644 index 0000000..ea32f2f --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEInstanceAI.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2010 - 2025 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#include "ALEInstanceAI.h" +#include "ALEUtility.h" +#include "lmarshal.h" + + +void ALEInstanceAI::Initialize() +{ + LOCK_ALE; + + ASSERT(!sALE->HasInstanceData(instance)); + + // Create a new table for instance data. + lua_State* L = sALE->L; + lua_newtable(L); + sALE->CreateInstanceData(instance); + + sALE->OnInitialize(this); +} + +void ALEInstanceAI::Load(const char* data) +{ + LOCK_ALE; + + // If we get passed NULL (i.e. `Reload` was called) then use + // the last known save data (or maybe just an empty string). + if (!data) + { + data = lastSaveData.c_str(); + } + else // Otherwise, copy the new data into our buffer. + { + lastSaveData.assign(data); + } + + if (data[0] == '\0') + { + ASSERT(!sALE->HasInstanceData(instance)); + + // Create a new table for instance data. + lua_State* L = sALE->L; + lua_newtable(L); + sALE->CreateInstanceData(instance); + + sALE->OnLoad(this); + // Stack: (empty) + return; + } + + size_t decodedLength; + const unsigned char* decodedData = ALEUtil::DecodeData(data, &decodedLength); + lua_State* L = sALE->L; + + if (decodedData) + { + // Stack: (empty) + + lua_pushcfunction(L, mar_decode); + lua_pushlstring(L, (const char*)decodedData, decodedLength); + // Stack: mar_decode, decoded_data + + // Call `mar_decode` and check for success. + if (lua_pcall(L, 1, 1, 0) == 0) + { + // Stack: data + // Only use the data if it's a table. + if (lua_istable(L, -1)) + { + sALE->CreateInstanceData(instance); + // Stack: (empty) + sALE->OnLoad(this); + // WARNING! lastSaveData might be different after `OnLoad` if the Lua code saved data. + } + else + { + ALE_LOG_ERROR("Error while loading instance data: Expected data to be a table (type 5), got type {} instead", lua_type(L, -1)); + lua_pop(L, 1); + // Stack: (empty) + + Initialize(); + } + } + else + { + // Stack: error_message + ALE_LOG_ERROR("Error while parsing instance data with lua-marshal: {}", lua_tostring(L, -1)); + lua_pop(L, 1); + // Stack: (empty) + + Initialize(); + } + + delete[] decodedData; + } + else + { + ALE_LOG_ERROR("Error while decoding instance data: Data is not valid base-64"); + + Initialize(); + } +} + +const char* ALEInstanceAI::Save() const +{ + LOCK_ALE; + lua_State* L = sALE->L; + // Stack: (empty) + + /* + * Need to cheat because this method actually does modify this instance, + * even though it's declared as `const`. + * + * Declaring virtual methods as `const` is BAD! + * Don't dictate to children that their methods must be pure. + */ + ALEInstanceAI* self = const_cast(this); + + lua_pushcfunction(L, mar_encode); + sALE->PushInstanceData(L, self, false); + // Stack: mar_encode, instance_data + + if (lua_pcall(L, 1, 1, 0) != 0) + { + // Stack: error_message + ALE_LOG_ERROR("Error while saving: {}", lua_tostring(L, -1)); + lua_pop(L, 1); + return NULL; + } + + // Stack: data + size_t dataLength; + const unsigned char* data = (const unsigned char*)lua_tolstring(L, -1, &dataLength); + ALEUtil::EncodeData(data, dataLength, self->lastSaveData); + + lua_pop(L, 1); + // Stack: (empty) + + return lastSaveData.c_str(); +} + +uint32 ALEInstanceAI::GetData(uint32 key) const +{ + LOCK_ALE; + lua_State* L = sALE->L; + // Stack: (empty) + + sALE->PushInstanceData(L, const_cast(this), false); + // Stack: instance_data + + ALE::Push(L, key); + // Stack: instance_data, key + + lua_gettable(L, -2); + // Stack: instance_data, value + + uint32 value = ALE::CHECKVAL(L, -1, 0); + lua_pop(L, 2); + // Stack: (empty) + + return value; +} + +void ALEInstanceAI::SetData(uint32 key, uint32 value) +{ + LOCK_ALE; + lua_State* L = sALE->L; + // Stack: (empty) + + sALE->PushInstanceData(L, this, false); + // Stack: instance_data + + ALE::Push(L, key); + ALE::Push(L, value); + // Stack: instance_data, key, value + + lua_settable(L, -3); + // Stack: instance_data + + lua_pop(L, 1); + // Stack: (empty) +} + +uint64 ALEInstanceAI::GetData64(uint32 key) const +{ + LOCK_ALE; + lua_State* L = sALE->L; + // Stack: (empty) + + sALE->PushInstanceData(L, const_cast(this), false); + // Stack: instance_data + + ALE::Push(L, key); + // Stack: instance_data, key + + lua_gettable(L, -2); + // Stack: instance_data, value + + uint64 value = ALE::CHECKVAL(L, -1, 0); + lua_pop(L, 2); + // Stack: (empty) + + return value; +} + +void ALEInstanceAI::SetData64(uint32 key, uint64 value) +{ + LOCK_ALE; + lua_State* L = sALE->L; + // Stack: (empty) + + sALE->PushInstanceData(L, this, false); + // Stack: instance_data + + ALE::Push(L, key); + ALE::Push(L, value); + // Stack: instance_data, key, value + + lua_settable(L, -3); + // Stack: instance_data + + lua_pop(L, 1); + // Stack: (empty) +} diff --git a/modules/mod-ale/src/LuaEngine/ALEInstanceAI.h b/modules/mod-ale/src/LuaEngine/ALEInstanceAI.h new file mode 100644 index 0000000..ebc5037 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEInstanceAI.h @@ -0,0 +1,133 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ALE_INSTANCE_DATA_H +#define _ALE_INSTANCE_DATA_H + +#include "LuaEngine.h" +#include "InstanceScript.h" + +/* + * This class is a small wrapper around `InstanceData`, + * allowing instances to be scripted with ALE. + * + * + * Note 1 + * ====== + * + * Instances of `ALEInstanceAI` are owned by the core, so they + * are not deleted when ALE is reloaded. Thus `Load` is only called + * by the core once, no matter how many times ALE is reloaded. + * + * However, when ALE reloads, all instance data in ALE is lost. + * So the solution is as follows: + * + * 1. Store the last save data in the member var `lastSaveData`. + * + * At first this is just the data given to us by the core when it calls `Load`, + * but later on once we start saving new data this is from ALE. + * + * 2. When retrieving instance data from ALE, check if it's missing. + * + * The data will be missing if ALE is reloaded, since a new Lua state is created. + * + * 3. If it *is* missing, call `Reload`. + * + * This reloads the last known instance save data into ALE, and calls the appropriate hooks. + * + * + * Note 2 + * ====== + * + * CMaNGOS expects some of these methods to be `const`. However, any of these + * methods are free to call `Save`, resulting in mutation of `lastSaveData`. + * + * Therefore, none of the hooks are `const`-safe, and `const_cast` is used + * to escape from these restrictions. + */ +class ALEInstanceAI : public InstanceData +{ +private: + // The last save data to pass through this class, + // either through `Load` or `Save`. + std::string lastSaveData; + +public: + ALEInstanceAI(Map* map) : InstanceData(map) + { + } + + void Initialize() override; + + /* + * These are responsible for serializing/deserializing the instance's + * data table to/from the core. + */ + void Load(const char* data) override; + // Simply calls Save, since the functions are a bit different in name and data types on different cores + std::string GetSaveData() override + { + return Save(); + } + const char* Save() const; + + + /* + * Calls `Load` with the last save data that was passed to + * or from ALE. + * + * See: big documentation blurb at the top of this class. + */ + void Reload() + { + Load(NULL); + } + + /* + * These methods allow non-Lua scripts (e.g. DB, C++) to get/set instance data. + */ + uint32 GetData(uint32 key) const override; + void SetData(uint32 key, uint32 value) override; + + uint64 GetData64(uint32 key) const override; + void SetData64(uint32 key, uint64 value) override; + + /* + * These methods are just thin wrappers around ALE. + */ + void Update(uint32 diff) override + { + // If ALE is reloaded, it will be missing our instance data. + // Reload here instead of waiting for the next hook call (possibly never). + // This avoids having to have an empty Update hook handler just to trigger the reload. + if (!sALE->HasInstanceData(instance)) + Reload(); + + sALE->OnUpdateInstance(this, diff); + } + + bool IsEncounterInProgress() const override + { + return sALE->OnCheckEncounterInProgress(const_cast(this)); + } + + void OnPlayerEnter(Player* player) override + { + sALE->OnPlayerEnterInstance(this, player); + } + + void OnGameObjectCreate(GameObject* gameobject) override + { + sALE->OnGameObjectCreate(this, gameobject); + } + + void OnCreatureCreate(Creature* creature) override + { + sALE->OnCreatureCreate(this, creature); + } +}; + +#endif // _ALE_INSTANCE_DATA_H diff --git a/modules/mod-ale/src/LuaEngine/ALETemplate.h b/modules/mod-ale/src/LuaEngine/ALETemplate.h new file mode 100644 index 0000000..a8d6fc4 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALETemplate.h @@ -0,0 +1,389 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ALE_TEMPLATE_H +#define _ALE_TEMPLATE_H + +extern "C" +{ +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +}; +#include "LuaEngine.h" +#include "ALECompat.h" +#include "ALEUtility.h" +#include "SharedDefines.h" + +class ALEGlobal +{ +public: + static int thunk(lua_State* L) + { + luaL_Reg* l = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + int top = lua_gettop(L); + int expected = l->func(L); + int args = lua_gettop(L) - top; + if (args < 0 || args > expected) + { + ALE_LOG_ERROR("[ALE]: {} returned unexpected amount of arguments {} out of {}. Report to devs", l->name, args, expected); + ASSERT(false); + } + lua_settop(L, top + expected); + return expected; + } + + static void SetMethods(ALE* E, luaL_Reg* methodTable) + { + ASSERT(E); + ASSERT(methodTable); + + lua_pushglobaltable(E->L); + + for (; methodTable && methodTable->name && methodTable->func; ++methodTable) + { + lua_pushstring(E->L, methodTable->name); + lua_pushlightuserdata(E->L, (void*)methodTable); + lua_pushcclosure(E->L, thunk, 1); + lua_rawset(E->L, -3); + } + + lua_remove(E->L, -1); + } +}; + +class ALEObject +{ +public: + template + ALEObject(T * obj, bool manageMemory); + + ~ALEObject() + { + } + + // Get wrapped object pointer + void* GetObj() const { return object; } + // Returns whether the object is valid or not + bool IsValid() const { return !callstackid || callstackid == sALE->GetCallstackId(); } + // Returns whether the object can be invalidated or not + bool CanInvalidate() const { return _invalidate; } + // Returns pointer to the wrapped object's type name + const char* GetTypeName() const { return type_name; } + + // Sets the object pointer that is wrapped + void SetObj(void* obj) + { + ASSERT(obj); + object = obj; + SetValid(true); + } + // Sets the object pointer to valid or invalid + void SetValid(bool valid) + { + ASSERT(!valid || (valid && object)); + if (valid) + if (CanInvalidate()) + callstackid = sALE->GetCallstackId(); + else + callstackid = 0; + else + callstackid = 1; + } + // Sets whether the pointer will be invalidated at end of calls + void SetValidation(bool invalidate) + { + _invalidate = invalidate; + } + // Invalidates the pointer if it should be invalidated + void Invalidate() + { + if (CanInvalidate()) + callstackid = 1; + } + +private: + uint64 callstackid; + bool _invalidate; + void* object; + const char* type_name; +}; + +template +struct ALERegister +{ + const char* name; + int(*mfunc)(lua_State*, T*); +}; + +template +class ALETemplate +{ +public: + static const char* tname; + static bool manageMemory; + + // name will be used as type name + // If gc is true, lua will handle the memory management for object pushed + // gc should be used if pushing for example WorldPacket, + // that will only be needed on lua side and will not be managed by TC/mangos/ + static void Register(ALE* E, const char* name, bool gc = false) + { + ASSERT(E); + ASSERT(name); + + // check that metatable isn't already there + lua_getglobal(E->L, name); + ASSERT(lua_isnoneornil(E->L, -1)); + + // pop nil + lua_pop(E->L, 1); + + tname = name; + manageMemory = gc; + + // create metatable for userdata of this type + luaL_newmetatable(E->L, tname); + int metatable = lua_gettop(E->L); + + // push methodtable to stack to be accessed and modified by users + lua_pushvalue(E->L, metatable); + lua_setglobal(E->L, tname); + + // tostring + lua_pushcfunction(E->L, ToString); + lua_setfield(E->L, metatable, "__tostring"); + + // garbage collecting + lua_pushcfunction(E->L, CollectGarbage); + lua_setfield(E->L, metatable, "__gc"); + + // make methods accessible through metatable + lua_pushvalue(E->L, metatable); + lua_setfield(E->L, metatable, "__index"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Add); + lua_setfield(E->L, metatable, "__add"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Substract); + lua_setfield(E->L, metatable, "__sub"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Multiply); + lua_setfield(E->L, metatable, "__mul"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Divide); + lua_setfield(E->L, metatable, "__div"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Mod); + lua_setfield(E->L, metatable, "__mod"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Pow); + lua_setfield(E->L, metatable, "__pow"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, UnaryMinus); + lua_setfield(E->L, metatable, "__unm"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Concat); + lua_setfield(E->L, metatable, "__concat"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Length); + lua_setfield(E->L, metatable, "__len"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Equal); + lua_setfield(E->L, metatable, "__eq"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Less); + lua_setfield(E->L, metatable, "__lt"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, LessOrEqual); + lua_setfield(E->L, metatable, "__le"); + + // make new indexes saved to methods + lua_pushcfunction(E->L, Call); + lua_setfield(E->L, metatable, "__call"); + + // special method to get the object type + lua_pushcfunction(E->L, GetType); + lua_setfield(E->L, metatable, "GetObjectType"); + + // special method to decide object invalidation at end of call + lua_pushcfunction(E->L, SetInvalidation); + lua_setfield(E->L, metatable, "SetInvalidation"); + + // pop metatable + lua_pop(E->L, 1); + } + + template + static void SetMethods(ALE* E, ALERegister* methodTable) + { + ASSERT(E); + ASSERT(tname); + ASSERT(methodTable); + + // get metatable + lua_pushstring(E->L, tname); + lua_rawget(E->L, LUA_REGISTRYINDEX); + ASSERT(lua_istable(E->L, -1)); + + for (; methodTable && methodTable->name && methodTable->mfunc; ++methodTable) + { + lua_pushstring(E->L, methodTable->name); + lua_pushlightuserdata(E->L, (void*)methodTable); + lua_pushcclosure(E->L, CallMethod, 1); + lua_rawset(E->L, -3); + } + + lua_pop(E->L, 1); + } + + static int Push(lua_State* L, T const* obj) + { + if (!obj) + { + lua_pushnil(L); + return 1; + } + + // Create new userdata + ALEObject** ptrHold = static_cast(lua_newuserdata(L, sizeof(ALEObject*))); + if (!ptrHold) + { + ALE_LOG_ERROR("{} could not create new userdata", tname); + lua_pushnil(L); + return 1; + } + *ptrHold = new ALEObject(const_cast(obj), manageMemory); + + // Set metatable for it + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); + if (!lua_istable(L, -1)) + { + ALE_LOG_ERROR("{} missing metatable", tname); + lua_pop(L, 2); + lua_pushnil(L); + return 1; + } + lua_setmetatable(L, -2); + return 1; + } + + static T* Check(lua_State* L, int narg, bool error = true) + { + ALEObject* ALEObj = ALE::CHECKTYPE(L, narg, tname, error); + if (!ALEObj) + return NULL; + + if (!ALEObj->IsValid()) + { + char buff[256]; + snprintf(buff, 256, "%s expected, got pointer to nonexisting (invalidated) object (%s). Check your code.", tname, luaL_typename(L, narg)); + if (error) + { + luaL_argerror(L, narg, buff); + } + else + { + ALE_LOG_ERROR("{}", buff); + } + return NULL; + } + return static_cast(ALEObj->GetObj()); + } + + static int GetType(lua_State* L) + { + lua_pushstring(L, tname); + return 1; + } + + static int SetInvalidation(lua_State* L) + { + ALEObject* ALEObj = ALE::CHECKOBJ(L, 1); + bool invalidate = ALE::CHECKVAL(L, 2); + + ALEObj->SetValidation(invalidate); + return 0; + } + + static int CallMethod(lua_State* L) + { + T* obj = ALE::CHECKOBJ(L, 1); // get self + if (!obj) + return 0; + ALERegister* l = static_cast*>(lua_touserdata(L, lua_upvalueindex(1))); + int top = lua_gettop(L); + int expected = l->mfunc(L, obj); + int args = lua_gettop(L) - top; + if (args < 0 || args > expected) + { + ALE_LOG_ERROR("[ALE]: {} returned unexpected amount of arguments {} out of {}. Report to devs", l->name, args, expected); + ASSERT(false); + } + lua_settop(L, top + expected); + return expected; + } + + // Metamethods ("virtual") + + // Remember special cases like ALETemplate::CollectGarbage + static int CollectGarbage(lua_State* L) + { + // Get object pointer (and check type, no error) + ALEObject* obj = ALE::CHECKOBJ(L, 1, false); + if (obj && manageMemory) + delete static_cast(obj->GetObj()); + delete obj; + return 0; + } + + static int ToString(lua_State* L) + { + T* obj = ALE::CHECKOBJ(L, 1, true); // get self + lua_pushfstring(L, "%s: %p", tname, obj); + return 1; + } + + static int ArithmeticError(lua_State* L) { return luaL_error(L, "attempt to perform arithmetic on a %s value", tname); } + static int CompareError(lua_State* L) { return luaL_error(L, "attempt to compare %s", tname); } + static int Add(lua_State* L) { return ArithmeticError(L); } + static int Substract(lua_State* L) { return ArithmeticError(L); } + static int Multiply(lua_State* L) { return ArithmeticError(L); } + static int Divide(lua_State* L) { return ArithmeticError(L); } + static int Mod(lua_State* L) { return ArithmeticError(L); } + static int Pow(lua_State* L) { return ArithmeticError(L); } + static int UnaryMinus(lua_State* L) { return ArithmeticError(L); } + static int Concat(lua_State* L) { return luaL_error(L, "attempt to concatenate a %s value", tname); } + static int Length(lua_State* L) { return luaL_error(L, "attempt to get length of a %s value", tname); } + static int Equal(lua_State* L) { ALE::Push(L, ALE::CHECKOBJ(L, 1) == ALE::CHECKOBJ(L, 2)); return 1; } + static int Less(lua_State* L) { return CompareError(L); } + static int LessOrEqual(lua_State* L) { return CompareError(L); } + static int Call(lua_State* L) { return luaL_error(L, "attempt to call a %s value", tname); } +}; + +template +ALEObject::ALEObject(T * obj, bool manageMemory) : callstackid(1), _invalidate(!manageMemory), object(obj), type_name(ALETemplate::tname) +{ + SetValid(true); +} + +template const char* ALETemplate::tname = NULL; +template bool ALETemplate::manageMemory = false; + +#endif diff --git a/modules/mod-ale/src/LuaEngine/ALEUtility.cpp b/modules/mod-ale/src/LuaEngine/ALEUtility.cpp new file mode 100644 index 0000000..9817465 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEUtility.cpp @@ -0,0 +1,188 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#include "ALEUtility.h" +#include "World.h" +#include "Object.h" +#include "Unit.h" +#include "GameObject.h" +#include "DBCStores.h" + +uint32 ALEUtil::GetCurrTime() +{ + return getMSTime(); +} + +uint32 ALEUtil::GetTimeDiff(uint32 oldMSTime) +{ + return GetMSTimeDiffToNow(oldMSTime); +} + +ALEUtil::ObjectGUIDCheck::ObjectGUIDCheck(ObjectGuid guid) : _guid(guid) +{ +} + +bool ALEUtil::ObjectGUIDCheck::operator()(WorldObject* object) +{ + return object->GET_GUID() == _guid; +} + +ALEUtil::ObjectDistanceOrderPred::ObjectDistanceOrderPred(WorldObject const* pRefObj, bool ascending) : m_refObj(pRefObj), m_ascending(ascending) +{ +} +bool ALEUtil::ObjectDistanceOrderPred::operator()(WorldObject const* pLeft, WorldObject const* pRight) const +{ + return m_ascending ? m_refObj->GetDistanceOrder(pLeft, pRight) : !m_refObj->GetDistanceOrder(pLeft, pRight); +} + +ALEUtil::WorldObjectInRangeCheck::WorldObjectInRangeCheck(bool nearest, WorldObject const* obj, float range, + uint16 typeMask, uint32 entry, uint32 hostile, uint32 dead) : + i_obj(obj), i_obj_unit(nullptr), i_obj_fact(nullptr), i_hostile(hostile), i_entry(entry), i_range(range), i_typeMask(typeMask), i_dead(dead), i_nearest(nearest) +{ + i_obj_unit = i_obj->ToUnit(); + if (!i_obj_unit) + if (GameObject const* go = i_obj->ToGameObject()) + i_obj_unit = go->GetOwner(); + if (!i_obj_unit) + i_obj_fact = sFactionTemplateStore.LookupEntry(14); +} +WorldObject const& ALEUtil::WorldObjectInRangeCheck::GetFocusObject() const +{ + return *i_obj; +} +bool ALEUtil::WorldObjectInRangeCheck::operator()(WorldObject* u) +{ + if (i_typeMask && !u->isType(TypeMask(i_typeMask))) + return false; + if (i_entry && u->GetEntry() != i_entry) + return false; + if (i_obj->GET_GUID() == u->GET_GUID()) + return false; + if (!i_obj->IsWithinDistInMap(u, i_range)) + return false; + Unit const* target = u->ToUnit(); + if (!target) + if (GameObject const* go = u->ToGameObject()) + target = go->GetOwner(); + if (target) + { + if (i_dead && (i_dead == 1) != target->IsAlive()) + return false; + + if (i_hostile) + { + if (!i_obj_unit) + { + if (i_obj_fact) + { + if ((i_obj_fact->IsHostileTo(*target->GetFactionTemplateEntry())) != (i_hostile == 1)) + return false; + } + else if (i_hostile == 1) + return false; + } + else if ((i_hostile == 1) != i_obj_unit->IsHostileTo(target)) + return false; + } + } + if (i_nearest) + i_range = i_obj->GetDistance(u); + return true; +} + +static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/'}; +static char decoding_table[256]; +static int mod_table[] = {0, 2, 1}; + +static void build_decoding_table() +{ + for (int i = 0; i < 64; i++) + decoding_table[(unsigned char)encoding_table[i]] = i; +} + +void ALEUtil::EncodeData(const unsigned char* data, size_t input_length, std::string& output) +{ + size_t output_length = 4 * ((input_length + 2) / 3); + char* buffer = new char[output_length]; + + for (size_t i = 0, j = 0; i < input_length;) + { + uint32 octet_a = i < input_length ? (unsigned char)data[i++] : 0; + uint32 octet_b = i < input_length ? (unsigned char)data[i++] : 0; + uint32 octet_c = i < input_length ? (unsigned char)data[i++] : 0; + + uint32 triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + buffer[j++] = encoding_table[(triple >> (3 * 6)) & 0x3F]; + buffer[j++] = encoding_table[(triple >> (2 * 6)) & 0x3F]; + buffer[j++] = encoding_table[(triple >> (1 * 6)) & 0x3F]; + buffer[j++] = encoding_table[(triple >> (0 * 6)) & 0x3F]; + } + + for (int i = 0; i < mod_table[input_length % 3]; i++) + buffer[output_length - 1 - i] = '='; + + output.assign(buffer, output_length); // Need length because `buffer` is not terminated! + delete[] buffer; +} + +unsigned char* ALEUtil::DecodeData(const char *data, size_t *output_length) +{ + if (decoding_table[(unsigned char)'B'] == 0) + build_decoding_table(); + + size_t input_length = strlen(data); + + if (input_length % 4 != 0) + return NULL; + + // Make sure there's no invalid characters in the data. + for (size_t i = 0; i < input_length; ++i) + { + unsigned char byte = data[i]; + + if (byte == '=') + continue; + + // Every invalid character (and 'A') will map to 0 (due to `calloc`). + if (decoding_table[byte] == 0 && byte != 'A') + return NULL; + } + + *output_length = input_length / 4 * 3; + if (data[input_length - 1] == '=') (*output_length)--; + if (data[input_length - 2] == '=') (*output_length)--; + + unsigned char *decoded_data = new unsigned char[*output_length]; + if (!decoded_data) + return NULL; + + for (size_t i = 0, j = 0; i < input_length;) + { + uint32 sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]]; + uint32 sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]]; + uint32 sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]]; + uint32 sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[(unsigned char)data[i++]]; + + uint32 triple = (sextet_a << (3 * 6)) + + (sextet_b << (2 * 6)) + + (sextet_c << (1 * 6)) + + (sextet_d << (0 * 6)); + + if (j < *output_length) decoded_data[j++] = (triple >> (2 * 8)) & 0xFF; + if (j < *output_length) decoded_data[j++] = (triple >> (1 * 8)) & 0xFF; + if (j < *output_length) decoded_data[j++] = (triple >> (0 * 8)) & 0xFF; + } + + return decoded_data; +} diff --git a/modules/mod-ale/src/LuaEngine/ALEUtility.h b/modules/mod-ale/src/LuaEngine/ALEUtility.h new file mode 100644 index 0000000..5af4ad8 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/ALEUtility.h @@ -0,0 +1,135 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _ALE_UTIL_H +#define _ALE_UTIL_H + +#include +#include +#include +#include +#include "Common.h" +#include "SharedDefines.h" +#include "ObjectGuid.h" +#include "Database/QueryResult.h" +#include "Log.h" + +typedef QueryResult ALEQuery; +#define GET_GUID GetGUID +#define HIGHGUID_PLAYER HighGuid::Player +#define HIGHGUID_UNIT HighGuid::Unit +#define HIGHGUID_ITEM HighGuid::Item +#define HIGHGUID_GAMEOBJECT HighGuid::GameObject +#define HIGHGUID_PET HighGuid::Pet +#define HIGHGUID_TRANSPORT HighGuid::Transport +#define HIGHGUID_VEHICLE HighGuid::Vehicle +#define HIGHGUID_CONTAINER HighGuid::Container +#define HIGHGUID_DYNAMICOBJECT HighGuid::DynamicObject +#define HIGHGUID_CORPSE HighGuid::Corpse +#define HIGHGUID_MO_TRANSPORT HighGuid::Mo_Transport +#define HIGHGUID_INSTANCE HighGuid::Instance +#define HIGHGUID_GROUP HighGuid::Group + +#define ALE_LOG_INFO(...) LOG_INFO("ALE", __VA_ARGS__); +#define ALE_LOG_ERROR(...) LOG_ERROR("ALE", __VA_ARGS__); +#define ALE_LOG_DEBUG(...) LOG_DEBUG("ALE", __VA_ARGS__); + +#ifndef MAKE_NEW_GUID +#define MAKE_NEW_GUID(l, e, h) ObjectGuid(h, e, l) +#endif +#ifndef GUID_ENPART +#define GUID_ENPART(guid) ObjectGuid(guid).GetEntry() +#endif +#ifndef GUID_LOPART +#define GUID_LOPART(guid) ObjectGuid(guid).GetCounter() +#endif +#ifndef GUID_HIPART +#define GUID_HIPART(guid) ObjectGuid(guid).GetHigh() +#endif + +class Unit; +class WorldObject; +struct FactionTemplateEntry; + +namespace ALEUtil +{ + uint32 GetCurrTime(); + + uint32 GetTimeDiff(uint32 oldMSTime); + + class ObjectGUIDCheck + { + public: + ObjectGUIDCheck(ObjectGuid guid); + bool operator()(WorldObject* object); + + ObjectGuid _guid; + }; + + // Binary predicate to sort WorldObjects based on the distance to a reference WorldObject + class ObjectDistanceOrderPred + { + public: + ObjectDistanceOrderPred(WorldObject const* pRefObj, bool ascending = true); + bool operator()(WorldObject const* pLeft, WorldObject const* pRight) const; + + WorldObject const* m_refObj; + const bool m_ascending; + }; + + // Doesn't get self + class WorldObjectInRangeCheck + { + public: + WorldObjectInRangeCheck(bool nearest, WorldObject const* obj, float range, + uint16 typeMask = 0, uint32 entry = 0, uint32 hostile = 0, uint32 dead = 0); + WorldObject const& GetFocusObject() const; + bool operator()(WorldObject* u); + + WorldObject const* const i_obj; + Unit const* i_obj_unit; + FactionTemplateEntry const* i_obj_fact; + uint32 const i_hostile; // 0 both, 1 hostile, 2 friendly + uint32 const i_entry; + float i_range; + uint16 const i_typeMask; + uint32 const i_dead; // 0 both, 1 alive, 2 dead + bool const i_nearest; + }; + + /* + * Usage: + * Inherit this class, then when needing lock, use + * Guard guard(GetLock()); + * + * The lock is automatically released at end of scope + */ + class Lockable + { + public: + typedef std::mutex LockType; + typedef std::lock_guard Guard; + + LockType& GetLock() { return _lock; } + + private: + LockType _lock; + }; + + /* + * Encodes `data` in Base-64 and store the result in `output`. + */ + void EncodeData(const unsigned char* data, size_t input_length, std::string& output); + + /* + * Decodes `data` from Base-64 and returns a pointer to the result, or `NULL` on error. + * + * The returned result buffer must be `delete[]`ed by the caller. + */ + unsigned char* DecodeData(const char* data, size_t *output_length); +}; + +#endif diff --git a/modules/mod-ale/src/LuaEngine/BindingMap.h b/modules/mod-ale/src/LuaEngine/BindingMap.h new file mode 100644 index 0000000..c14ce6d --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/BindingMap.h @@ -0,0 +1,375 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _BINDING_MAP_H +#define _BINDING_MAP_H + +#include +#include "Common.h" +#include "ALEUtility.h" +#include + +extern "C" +{ +#include "lua.h" +#include "lauxlib.h" +}; + + +/* + * A set of bindings from keys of type `K` to Lua references. + */ +template +class BindingMap : public ALEUtil::Lockable +{ +private: + lua_State* L; + uint64 maxBindingID; + + struct Binding + { + uint64 id; + lua_State* L; + uint32 remainingShots; + int functionReference; + + Binding(lua_State* L, uint64 id, int functionReference, uint32 remainingShots) : + id(id), + L(L), + remainingShots(remainingShots), + functionReference(functionReference) + { } + + ~Binding() + { + luaL_unref(L, LUA_REGISTRYINDEX, functionReference); + } + }; + + typedef std::vector< std::unique_ptr > BindingList; + + std::unordered_map bindings; + /* + * This table is for fast removal of bindings by ID. + * + * Instead of having to look through (potentially) every BindingList to find + * the Binding with the right ID, this allows you to go directly to the + * BindingList that might have the Binding with that ID. + * + * However, you must be careful not to store pointers to BindingLists + * that no longer exist (see `void Clear(const K& key)` implementation). + */ + std::unordered_map id_lookup_table; + +public: + BindingMap(lua_State* L) : + L(L), + maxBindingID(0) + { } + + /* + * Insert a new binding from `key` to `ref`, which lasts for `shots`-many pushes. + * + * If `shots` is 0, it will never automatically expire, but can still be + * removed with `Clear` or `Remove`. + */ + uint64 Insert(const K& key, int ref, uint32 shots) + { + Guard guard(GetLock()); + + uint64 id = (++maxBindingID); + BindingList& list = bindings[key]; + list.push_back(std::unique_ptr(new Binding(L, id, ref, shots))); + id_lookup_table[id] = &list; + return id; + } + + /* + * Clear all bindings for `key`. + */ + void Clear(const K& key) + { + Guard guard(GetLock()); + + if (bindings.empty()) + return; + + auto iter = bindings.find(key); + if (iter == bindings.end()) + return; + + BindingList& list = iter->second; + + // Remove all pointers to `list` from `id_lookup_table`. + for (auto i = list.begin(); i != list.end(); ++i) + { + std::unique_ptr& binding = *i; + id_lookup_table.erase(binding->id); + } + + bindings.erase(key); + } + + /* + * Clear all bindings for all keys. + */ + void Clear() + { + Guard guard(GetLock()); + + if (bindings.empty()) + return; + + id_lookup_table.clear(); + bindings.clear(); + } + + /* + * Remove a specific binding identified by `id`. + * + * If `id` in invalid, nothing is removed. + */ + void Remove(uint64 id) + { + Guard guard(GetLock()); + + auto iter = id_lookup_table.find(id); + if (iter == id_lookup_table.end()) + return; + + BindingList* list = iter->second; + auto i = list->begin(); + + for (; i != list->end(); ++i) + { + std::unique_ptr& binding = *i; + if (binding->id == id) + break; + } + + if (i != list->end()) + list->erase(i); + + // Unconditionally erase the ID in the lookup table because + // it was either already invalid, or it's no longer valid. + id_lookup_table.erase(id); + } + + /* + * Check whether `key` has any bindings. + */ + bool HasBindingsFor(const K& key) + { + Guard guard(GetLock()); + + if (bindings.empty()) + return false; + + auto result = bindings.find(key); + if (result == bindings.end()) + return false; + + BindingList& list = result->second; + return !list.empty(); + } + + /* + * Push all Lua references for `key` onto the stack. + */ + void PushRefsFor(const K& key) + { + Guard guard(GetLock()); + + if (bindings.empty()) + return; + + auto result = bindings.find(key); + if (result == bindings.end()) + return; + + BindingList& list = result->second; + for (auto i = list.begin(); i != list.end();) + { + std::unique_ptr& binding = (*i); + auto i_prev = (i++); + + lua_rawgeti(L, LUA_REGISTRYINDEX, binding->functionReference); + + if (binding->remainingShots > 0) + { + binding->remainingShots -= 1; + + if (binding->remainingShots == 0) + { + id_lookup_table.erase(binding->id); + list.erase(i_prev); + } + } + } + } +}; + + +/* + * A `BindingMap` key type for simple event ID bindings + * (ServerEvents, GuildEvents, etc.). + */ +template +struct EventKey +{ + T event_id; + + EventKey(T event_id) : + event_id(event_id) + { } +}; + +/* + * A `BindingMap` key type for event ID/Object entry ID bindings + * (CreatureEvents, GameObjectEvents, etc.). + */ +template +struct EntryKey +{ + T event_id; + uint32 entry; + + EntryKey(T event_id, uint32 entry) : + event_id(event_id), + entry(entry) + { } +}; + +/* + * A `BindingMap` key type for event ID/unique Object bindings + * (currently just CreatureEvents). + */ +template +struct UniqueObjectKey +{ + T event_id; + ObjectGuid guid; + uint32 instance_id; + + UniqueObjectKey(T event_id, ObjectGuid guid, uint32 instance_id) : + event_id(event_id), + guid(guid), + instance_id(instance_id) + { } +}; + +class hash_helper +{ +public: + typedef std::size_t result_type; + + template + static inline result_type hash(T1 const & t1, T2 const & t2, T const &... t) + { + result_type seed = 0; + _hash_combine(seed, t1, t2, t...); + return seed; + } + + template ::value>::type* = nullptr> + static inline result_type hash(T const & t) + { + return std::hash::type>()(t); + } + + template ::value>::type* = nullptr> + static inline result_type hash(T const & t) + { + return std::hash()(t); + } + +private: + template + static inline void _hash_combine(result_type& seed, T const & v) + { + // from http://www.boost.org/doc/libs/1_40_0/boost/functional/hash/hash.hpp + seed ^= hash(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + + template + static inline void _hash_combine(result_type& seed, H const & h, T1 const & t1, T const &... t) + { + _hash_combine(seed, h); + _hash_combine(seed, t1, t...); + } +}; + +/* + * Implementations of various std functions on the above key types, + * so that they can be used within an unordered_map. + */ +namespace std +{ + template + struct equal_to < EventKey > + { + bool operator()(EventKey const& lhs, EventKey const& rhs) const + { + return lhs.event_id == rhs.event_id; + } + }; + + template + struct equal_to < EntryKey > + { + bool operator()(EntryKey const& lhs, EntryKey const& rhs) const + { + return lhs.event_id == rhs.event_id + && lhs.entry == rhs.entry; + } + }; + + template + struct equal_to < UniqueObjectKey > + { + bool operator()(UniqueObjectKey const& lhs, UniqueObjectKey const& rhs) const + { + return lhs.event_id == rhs.event_id + && lhs.guid == rhs.guid + && lhs.instance_id == rhs.instance_id; + } + }; + + template + struct hash < EventKey > + { + typedef EventKey argument_type; + + hash_helper::result_type operator()(argument_type const& k) const + { + return hash_helper::hash(k.event_id); + } + }; + + template + struct hash < EntryKey > + { + typedef EntryKey argument_type; + + hash_helper::result_type operator()(argument_type const& k) const + { + return hash_helper::hash(k.event_id, k.entry); + } + }; + + template + struct hash < UniqueObjectKey > + { + typedef UniqueObjectKey argument_type; + + hash_helper::result_type operator()(argument_type const& k) const + { + return hash_helper::hash(k.event_id, k.instance_id, k.guid.GetRawValue()); + } + }; +} + +#endif // _BINDING_MAP_H diff --git a/modules/mod-ale/src/LuaEngine/HookHelpers.h b/modules/mod-ale/src/LuaEngine/HookHelpers.h new file mode 100644 index 0000000..557f753 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/HookHelpers.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2010 - 2025 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#ifndef _HOOK_HELPERS_H +#define _HOOK_HELPERS_H + +#include "LuaEngine.h" +#include "ALEUtility.h" + +/* + * Sets up the stack so that event handlers can be called. + * + * Returns the number of functions that were pushed onto the stack. + */ +template +int ALE::SetupStack(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, int number_of_arguments) +{ + ASSERT(number_of_arguments == this->push_counter); + ASSERT(key1.event_id == key2.event_id); + // Stack: [arguments] + + Push(key1.event_id); + this->push_counter = 0; + ++number_of_arguments; + // Stack: [arguments], event_id + + int arguments_top = lua_gettop(L); + int first_argument_index = arguments_top - number_of_arguments + 1; + ASSERT(arguments_top >= number_of_arguments); + + lua_insert(L, first_argument_index); + // Stack: event_id, [arguments] + + bindings1->PushRefsFor(key1); + if (bindings2) + bindings2->PushRefsFor(key2); + // Stack: event_id, [arguments], [functions] + + int number_of_functions = lua_gettop(L) - arguments_top; + return number_of_functions; +} + +/* + * Replace one of the arguments pushed before `SetupStack` with a new value. + */ +template +void ALE::ReplaceArgument(T value, uint8 index) +{ + ASSERT(index < lua_gettop(L) && index > 0); + // Stack: event_id, [arguments], [functions], [results] + + ALE::Push(L, value); + // Stack: event_id, [arguments], [functions], [results], value + + lua_replace(L, index + 1); + // Stack: event_id, [arguments and value], [functions], [results] +} + +/* + * Call all event handlers registered to the event ID/entry combination and ignore any results. + */ +template +void ALE::CallAllFunctions(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2) +{ + int number_of_arguments = this->push_counter; + // Stack: [arguments] + + int number_of_functions = SetupStack(bindings1, bindings2, key1, key2, number_of_arguments); + // Stack: event_id, [arguments], [functions] + + while (number_of_functions > 0) + { + CallOneFunction(number_of_functions, number_of_arguments, 0); + --number_of_functions; + // Stack: event_id, [arguments], [functions - 1] + } + // Stack: event_id, [arguments] + + CleanUpStack(number_of_arguments); + // Stack: (empty) +} + +/* + * Call all event handlers registered to the event ID/entry combination, + * and returns `default_value` if ALL event handlers returned `default_value`, + * otherwise returns the opposite of `default_value`. + */ +template +bool ALE::CallAllFunctionsBool(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, bool default_value/* = false*/) +{ + bool result = default_value; + // Note: number_of_arguments here does not count in eventID, which is pushed in SetupStack + int number_of_arguments = this->push_counter; + // Stack: [arguments] + + int number_of_functions = SetupStack(bindings1, bindings2, key1, key2, number_of_arguments); + // Stack: event_id, [arguments], [functions] + + while (number_of_functions > 0) + { + int r = CallOneFunction(number_of_functions, number_of_arguments, 1); + --number_of_functions; + // Stack: event_id, [arguments], [functions - 1], result + + if (lua_isboolean(L, r) && (lua_toboolean(L, r) == 1) != default_value) + result = !default_value; + + lua_pop(L, 1); + // Stack: event_id, [arguments], [functions - 1] + } + // Stack: event_id, [arguments] + + CleanUpStack(number_of_arguments); + // Stack: (empty) + return result; +} + +#endif // _HOOK_HELPERS_H diff --git a/modules/mod-ale/src/LuaEngine/Hooks.h b/modules/mod-ale/src/LuaEngine/Hooks.h new file mode 100644 index 0000000..f83dd99 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/Hooks.h @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2010 - 2025 Eluna Lua Engine + * This program is free software licensed under GPL version 3 + * Please see the included DOCS/LICENSE.md for more information + */ + +#ifndef _HOOKS_H +#define _HOOKS_H + +/* + * A hook should be written in one of the following forms: + * + * A. If results will be IGNORED: + * + * // Return early if there are no bindings. + * if (!WhateverBindings->HasBindingsFor(SOME_EVENT_TYPE)) + * return; + * + * // Lock out any other threads. + * LOCK_ALE; + * + * // Push extra arguments, if any. + * Push(a); + * Push(b); + * Push(c); + * + * // Call all event handlers. + * CallAllFunctions(WhateverBindings, SOME_EVENT_TYPE); + * + * + * B. If results will be USED: + * + * // Return early if there are no bindings. + * if (!WhateverBindings->HasBindingsFor(SOME_EVENT_TYPE)) + * return; + * + * // Lock out any other threads. + * LOCK_ALE; + * + * // Push extra arguments, if any. + * Push(a); + * Push(b); + * Push(c); + * + * // Setup the stack and get the number of functions pushed. + * // Last argument is 3 because we did 3 Pushes. + * int n = SetupStack(WhateverBindings, SOME_EVENT_TYPE, 3); + * + * // Call each event handler in order and check results. + * while (n > 0) + * { + * // Call an event handler and decrement the function counter afterward. + * // Second-last argument is 3 because we did 3 Pushes. + * // Last argument is 2 because we want 2 results. + * int r = CallOneFunction(n--, 3, 2); + * + * // Results can be popped using `r`. + * int first = CHECKVAL(L, r + 0); + * int second = CHECKVAL(L, r + 1); + * + * // Pop the results off the stack. + * lua_pop(L, 2); + * } + * + * // Clean-up the stack. Argument is 3 because we did 3 Pushes. + * CleanUpStack(3); + */ + +namespace Hooks +{ + enum RegisterTypes + { + REGTYPE_PACKET, + REGTYPE_SERVER, + REGTYPE_PLAYER, + REGTYPE_GUILD, + REGTYPE_GROUP, + REGTYPE_CREATURE, + REGTYPE_VEHICLE, + REGTYPE_CREATURE_GOSSIP, + REGTYPE_GAMEOBJECT, + REGTYPE_GAMEOBJECT_GOSSIP, + REGTYPE_ITEM, + REGTYPE_ITEM_GOSSIP, + REGTYPE_PLAYER_GOSSIP, + REGTYPE_BG, + REGTYPE_MAP, + REGTYPE_INSTANCE, + REGTYPE_TICKET, + REGTYPE_SPELL, + REGTYPE_ALL_CREATURE, + REGTYPE_COUNT + }; + + enum PacketEvents + { + PACKET_EVENT_ON_PACKET_RECEIVE = 5, // (event, packet, player) - Player only if accessible. Can return false, newPacket + PACKET_EVENT_ON_PACKET_RECEIVE_UNKNOWN = 6, // Not Implemented + PACKET_EVENT_ON_PACKET_SEND = 7, // (event, packet, player) - Player only if accessible. Can return false, newPacket + + PACKET_EVENT_COUNT + }; + + enum ServerEvents + { + // Server + SERVER_EVENT_ON_NETWORK_START = 1, // Not Implemented + SERVER_EVENT_ON_NETWORK_STOP = 2, // Not Implemented + SERVER_EVENT_ON_SOCKET_OPEN = 3, // Not Implemented + SERVER_EVENT_ON_SOCKET_CLOSE = 4, // Not Implemented + SERVER_EVENT_ON_PACKET_RECEIVE = 5, // (event, packet, player) - Player only if accessible. Can return false, newPacket + SERVER_EVENT_ON_PACKET_RECEIVE_UNKNOWN = 6, // Not Implemented + SERVER_EVENT_ON_PACKET_SEND = 7, // (event, packet, player) - Player only if accessible. Can return false, newPacket + + // World + WORLD_EVENT_ON_OPEN_STATE_CHANGE = 8, // (event, open) - Needs core support on Mangos + WORLD_EVENT_ON_CONFIG_LOAD = 9, // (event, reload) + // UNUSED = 10, + WORLD_EVENT_ON_SHUTDOWN_INIT = 11, // (event, code, mask) + WORLD_EVENT_ON_SHUTDOWN_CANCEL = 12, // (event) + WORLD_EVENT_ON_UPDATE = 13, // (event, diff) + WORLD_EVENT_ON_STARTUP = 14, // (event) + WORLD_EVENT_ON_SHUTDOWN = 15, // (event) + + // ALE + ALE_EVENT_ON_LUA_STATE_CLOSE = 16, // (event) - triggers just before shutting down ALE (on shutdown and restart) + + // Map + MAP_EVENT_ON_CREATE = 17, // (event, map) + MAP_EVENT_ON_DESTROY = 18, // (event, map) + MAP_EVENT_ON_GRID_LOAD = 19, // Not Implemented + MAP_EVENT_ON_GRID_UNLOAD = 20, // Not Implemented + MAP_EVENT_ON_PLAYER_ENTER = 21, // (event, map, player) + MAP_EVENT_ON_PLAYER_LEAVE = 22, // (event, map, player) + MAP_EVENT_ON_UPDATE = 23, // (event, map, diff) + + // Area trigger + TRIGGER_EVENT_ON_TRIGGER = 24, // (event, player, triggerId) - Can return true + + // Weather + WEATHER_EVENT_ON_CHANGE = 25, // (event, zoneId, state, grade) + + // Auction house + AUCTION_EVENT_ON_ADD = 26, // (event, auctionId, owner, item, expireTime, buyout, startBid, currentBid, bidderGUIDLow) + AUCTION_EVENT_ON_REMOVE = 27, // (event, auctionId, owner, item, expireTime, buyout, startBid, currentBid, bidderGUIDLow) + AUCTION_EVENT_ON_SUCCESSFUL = 28, // (event, auctionId, owner, item, expireTime, buyout, startBid, currentBid, bidderGUIDLow) + AUCTION_EVENT_ON_EXPIRE = 29, // (event, auctionId, owner, item, expireTime, buyout, startBid, currentBid, bidderGUIDLow) + + // AddOns + ADDON_EVENT_ON_MESSAGE = 30, // (event, sender, type, prefix, msg, target) - target can be nil/whisper_target/guild/group/channel. Can return false + + WORLD_EVENT_ON_DELETE_CREATURE = 31, // (event, creature) + WORLD_EVENT_ON_DELETE_GAMEOBJECT = 32, // (event, gameobject) + + // ALE + ALE_EVENT_ON_LUA_STATE_OPEN = 33, // (event) - triggers after all scripts are loaded + + GAME_EVENT_START = 34, // (event, gameeventid) + GAME_EVENT_STOP = 35, // (event, gameeventid) + + SERVER_EVENT_COUNT + }; + + enum PlayerEvents + { + PLAYER_EVENT_ON_CHARACTER_CREATE = 1, // (event, player) + PLAYER_EVENT_ON_CHARACTER_DELETE = 2, // (event, guid) + PLAYER_EVENT_ON_LOGIN = 3, // (event, player) + PLAYER_EVENT_ON_LOGOUT = 4, // (event, player) + PLAYER_EVENT_ON_SPELL_CAST = 5, // (event, player, spell, skipCheck) + PLAYER_EVENT_ON_KILL_PLAYER = 6, // (event, killer, killed) + PLAYER_EVENT_ON_KILL_CREATURE = 7, // (event, killer, killed) + PLAYER_EVENT_ON_KILLED_BY_CREATURE = 8, // (event, killer, killed) + PLAYER_EVENT_ON_DUEL_REQUEST = 9, // (event, target, challenger) + PLAYER_EVENT_ON_DUEL_START = 10, // (event, player1, player2) + PLAYER_EVENT_ON_DUEL_END = 11, // (event, winner, loser, type) + PLAYER_EVENT_ON_GIVE_XP = 12, // (event, player, amount, victim, source) - Can return new XP amount + PLAYER_EVENT_ON_LEVEL_CHANGE = 13, // (event, player, oldLevel) + PLAYER_EVENT_ON_MONEY_CHANGE = 14, // (event, player, amount) - Can return new money amount + PLAYER_EVENT_ON_REPUTATION_CHANGE = 15, // (event, player, factionId, standing, incremental) - Can return new standing -> if standing == -1, it will prevent default action (rep gain) + PLAYER_EVENT_ON_TALENTS_CHANGE = 16, // (event, player, points) + PLAYER_EVENT_ON_TALENTS_RESET = 17, // (event, player, noCost) + PLAYER_EVENT_ON_CHAT = 18, // (event, player, msg, Type, lang) - Can return false, newMessage + PLAYER_EVENT_ON_WHISPER = 19, // (event, player, msg, Type, lang, receiver) - Can return false, newMessage + PLAYER_EVENT_ON_GROUP_CHAT = 20, // (event, player, msg, Type, lang, group) - Can return false, newMessage + PLAYER_EVENT_ON_GUILD_CHAT = 21, // (event, player, msg, Type, lang, guild) - Can return false, newMessage + PLAYER_EVENT_ON_CHANNEL_CHAT = 22, // (event, player, msg, Type, lang, channel) - channel is negative for custom channels. Can return false, newMessage + PLAYER_EVENT_ON_EMOTE = 23, // (event, player, emote) - Not triggered on any known emote + PLAYER_EVENT_ON_TEXT_EMOTE = 24, // (event, player, textEmote, emoteNum, guid) + PLAYER_EVENT_ON_SAVE = 25, // (event, player) + PLAYER_EVENT_ON_BIND_TO_INSTANCE = 26, // (event, player, difficulty, mapid, permanent) + PLAYER_EVENT_ON_UPDATE_ZONE = 27, // (event, player, newZone, newArea) + PLAYER_EVENT_ON_MAP_CHANGE = 28, // (event, player) + + // Custom + PLAYER_EVENT_ON_EQUIP = 29, // (event, player, item, bag, slot) + PLAYER_EVENT_ON_FIRST_LOGIN = 30, // (event, player) + PLAYER_EVENT_ON_CAN_USE_ITEM = 31, // (event, player, itemEntry) - Can return InventoryResult enum value + PLAYER_EVENT_ON_LOOT_ITEM = 32, // (event, player, item, count) + PLAYER_EVENT_ON_ENTER_COMBAT = 33, // (event, player, enemy) + PLAYER_EVENT_ON_LEAVE_COMBAT = 34, // (event, player) + PLAYER_EVENT_ON_REPOP = 35, // (event, player) + PLAYER_EVENT_ON_RESURRECT = 36, // (event, player) + PLAYER_EVENT_ON_LOOT_MONEY = 37, // (event, player, amount) + PLAYER_EVENT_ON_QUEST_ABANDON = 38, // (event, player, questId) + PLAYER_EVENT_ON_LEARN_TALENTS = 39, // (event, player, talentId, talentRank, spellid) + // UNUSED = 40, // (event, player) + // UNUSED = 41, // (event, player) + PLAYER_EVENT_ON_COMMAND = 42, // (event, player, command, chatHandler) - player is nil if command used from console. Can return false + PLAYER_EVENT_ON_PET_ADDED_TO_WORLD = 43, // (event, player, pet) + PLAYER_EVENT_ON_LEARN_SPELL = 44, // (event, player, spellId) + PLAYER_EVENT_ON_ACHIEVEMENT_COMPLETE = 45, // (event, player, achievement) + PLAYER_EVENT_ON_FFAPVP_CHANGE = 46, // (event, player, hasFfaPvp) + PLAYER_EVENT_ON_UPDATE_AREA = 47, // (event, player, oldArea, newArea) + PLAYER_EVENT_ON_CAN_INIT_TRADE = 48, // (event, player, target) - Can return false to prevent the trade + PLAYER_EVENT_ON_CAN_SEND_MAIL = 49, // (event, player, receiverGuid, mailbox, subject, body, money, cod, item) - Can return false to prevent sending the mail + PLAYER_EVENT_ON_CAN_JOIN_LFG = 50, // (event, player, roles, dungeons, comment) - Can return false to prevent queueing + PLAYER_EVENT_ON_QUEST_REWARD_ITEM = 51, // (event, player, item, count) + PLAYER_EVENT_ON_CREATE_ITEM = 52, // (event, player, item, count) + PLAYER_EVENT_ON_STORE_NEW_ITEM = 53, // (event, player, item, count) + PLAYER_EVENT_ON_COMPLETE_QUEST = 54, // (event, player, quest) + PLAYER_EVENT_ON_CAN_GROUP_INVITE = 55, // (event, player, memberName) - Can return false to prevent inviting + PLAYER_EVENT_ON_GROUP_ROLL_REWARD_ITEM = 56, // (event, player, item, count, voteType, roll) + PLAYER_EVENT_ON_BG_DESERTION = 57, // (event, player, type) + PLAYER_EVENT_ON_PET_KILL = 58, // (event, player, killer) + PLAYER_EVENT_ON_CAN_RESURRECT = 59, // (event, player) + PLAYER_EVENT_ON_CAN_UPDATE_SKILL = 60, // (event, player, skill_id) - Can return true or false + PLAYER_EVENT_ON_BEFORE_UPDATE_SKILL = 61, // (event, player, skill_id, value, max, step) -- Can return new amount + PLAYER_EVENT_ON_UPDATE_SKILL = 62, // (event, player, skill_id, value, max, step, new_value) + PLAYER_EVENT_ON_QUEST_ACCEPT = 63, // (event, player, quest) + PLAYER_EVENT_ON_AURA_APPLY = 64, // (event, player, aura) + PLAYER_EVENT_ON_HEAL = 65, // (event, player, target, gain) - Can return new heal amount + PLAYER_EVENT_ON_DAMAGE = 66, // (event, player, target, damage) - Can return new damage amount + PLAYER_EVENT_ON_AURA_REMOVE = 67, // (event, player, aura, remove_mode) + PLAYER_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK = 68, // (event, player, target, damage, spellInfo) - Can return new damage amount + PLAYER_EVENT_ON_MODIFY_MELEE_DAMAGE = 69, // (event, player, target, damage) - Can return new damage amount + PLAYER_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN = 70, // (event, player, target, damage, spellInfo) - Can return new damage amount + PLAYER_EVENT_ON_MODIFY_HEAL_RECEIVED = 71, // (event, player, target, heal, spellInfo) - Can return new heal amount + PLAYER_EVENT_ON_DEAL_DAMAGE = 72, // (event, player, target, damage, damagetype) - Can return new damage amount + PLAYER_EVENT_ON_RELEASED_GHOST = 73, // (event, player) + + PLAYER_EVENT_COUNT + }; + + enum GuildEvents + { + // Guild + GUILD_EVENT_ON_ADD_MEMBER = 1, // (event, guild, player, rank) + GUILD_EVENT_ON_REMOVE_MEMBER = 2, // (event, guild, player, isDisbanding) + GUILD_EVENT_ON_MOTD_CHANGE = 3, // (event, guild, newMotd) + GUILD_EVENT_ON_INFO_CHANGE = 4, // (event, guild, newInfo) + GUILD_EVENT_ON_CREATE = 5, // (event, guild, leader, name) // Not on TC + GUILD_EVENT_ON_DISBAND = 6, // (event, guild) + GUILD_EVENT_ON_MONEY_WITHDRAW = 7, // (event, guild, player, amount, isRepair) - Can return new money amount + GUILD_EVENT_ON_MONEY_DEPOSIT = 8, // (event, guild, player, amount) - Can return new money amount + GUILD_EVENT_ON_ITEM_MOVE = 9, // (event, guild, player, item, isSrcBank, srcContainer, srcSlotId, isDestBank, destContainer, destSlotId) // TODO + GUILD_EVENT_ON_EVENT = 10, // (event, guild, eventType, plrGUIDLow1, plrGUIDLow2, newRank) // TODO + GUILD_EVENT_ON_BANK_EVENT = 11, // (event, guild, eventType, tabId, playerGUIDLow, itemOrMoney, itemStackCount, destTabId) + + GUILD_EVENT_COUNT + }; + + enum GroupEvents + { + // Group + GROUP_EVENT_ON_MEMBER_ADD = 1, // (event, group, guid) + GROUP_EVENT_ON_MEMBER_INVITE = 2, // (event, group, guid) + GROUP_EVENT_ON_MEMBER_REMOVE = 3, // (event, group, guid, method, kicker, reason) + GROUP_EVENT_ON_LEADER_CHANGE = 4, // (event, group, newLeaderGuid, oldLeaderGuid) + GROUP_EVENT_ON_DISBAND = 5, // (event, group) + GROUP_EVENT_ON_CREATE = 6, // (event, group, leaderGuid, groupType) + + GROUP_EVENT_COUNT + }; + + enum VehicleEvents + { + VEHICLE_EVENT_ON_INSTALL = 1, // (event, vehicle) + VEHICLE_EVENT_ON_UNINSTALL = 2, // (event, vehicle) + // UNUSED = 3, // (event, vehicle) + VEHICLE_EVENT_ON_INSTALL_ACCESSORY = 4, // (event, vehicle, creature) + VEHICLE_EVENT_ON_ADD_PASSENGER = 5, // (event, vehicle, unit, seatId) + VEHICLE_EVENT_ON_REMOVE_PASSENGER = 6, // (event, vehicle, unit) + + VEHICLE_EVENT_COUNT + }; + + enum CreatureEvents + { + CREATURE_EVENT_ON_ENTER_COMBAT = 1, // (event, creature, target) - Can return true to stop normal action + CREATURE_EVENT_ON_LEAVE_COMBAT = 2, // (event, creature) - Can return true to stop normal action + CREATURE_EVENT_ON_TARGET_DIED = 3, // (event, creature, victim) - Can return true to stop normal action + CREATURE_EVENT_ON_DIED = 4, // (event, creature, killer) - Can return true to stop normal action + CREATURE_EVENT_ON_SPAWN = 5, // (event, creature) - Can return true to stop normal action + CREATURE_EVENT_ON_REACH_WP = 6, // (event, creature, type, id) - Can return true to stop normal action + CREATURE_EVENT_ON_AIUPDATE = 7, // (event, creature, diff) - Can return true to stop normal action + CREATURE_EVENT_ON_RECEIVE_EMOTE = 8, // (event, creature, player, emoteid) - Can return true to stop normal action + CREATURE_EVENT_ON_DAMAGE_TAKEN = 9, // (event, creature, attacker, damage) - Can return true to stop normal action, can return new damage as second return value. + CREATURE_EVENT_ON_PRE_COMBAT = 10, // (event, creature, target) - Can return true to stop normal action + // UNUSED + CREATURE_EVENT_ON_OWNER_ATTACKED = 12, // (event, creature, target) - Can return true to stop normal action // Not on mangos + CREATURE_EVENT_ON_OWNER_ATTACKED_AT = 13, // (event, creature, attacker) - Can return true to stop normal action // Not on mangos + CREATURE_EVENT_ON_HIT_BY_SPELL = 14, // (event, creature, caster, spellid) - Can return true to stop normal action + CREATURE_EVENT_ON_SPELL_HIT_TARGET = 15, // (event, creature, target, spellid) - Can return true to stop normal action + // UNUSED = 16, // (event, creature) + // UNUSED = 17, // (event, creature) + // UNUSED = 18, // (event, creature) + CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE = 19, // (event, creature, summon) - Can return true to stop normal action + CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN = 20, // (event, creature, summon) - Can return true to stop normal action + CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED = 21, // (event, creature, summon, killer) - Can return true to stop normal action // Not on mangos + CREATURE_EVENT_ON_SUMMONED = 22, // (event, creature, summoner) - Can return true to stop normal action + CREATURE_EVENT_ON_RESET = 23, // (event, creature) + CREATURE_EVENT_ON_REACH_HOME = 24, // (event, creature) - Can return true to stop normal action + // UNUSED = 25, // (event, creature) + CREATURE_EVENT_ON_CORPSE_REMOVED = 26, // (event, creature, respawndelay) - Can return true to stop normal action, can return new respawndelay as second return value + CREATURE_EVENT_ON_MOVE_IN_LOS = 27, // (event, creature, unit) - Can return true to stop normal action. Does not actually check LOS, just uses the sight range + // UNUSED = 28, // (event, creature) + // UNUSED = 29, // (event, creature) + CREATURE_EVENT_ON_DUMMY_EFFECT = 30, // (event, caster, spellid, effindex, creature) + CREATURE_EVENT_ON_QUEST_ACCEPT = 31, // (event, player, creature, quest) - Can return true + // UNUSED = 32, // (event, creature) + // UNUSED = 33, // (event, creature) + CREATURE_EVENT_ON_QUEST_REWARD = 34, // (event, player, creature, quest, opt) - Can return true + CREATURE_EVENT_ON_DIALOG_STATUS = 35, // (event, player, creature) + CREATURE_EVENT_ON_ADD = 36, // (event, creature) + CREATURE_EVENT_ON_REMOVE = 37, // (event, creature) + CREATURE_EVENT_ON_AURA_APPLY = 38, // (event, creature, aura) + CREATURE_EVENT_ON_HEAL = 39, // (event, creature, target, gain) - Can return new heal amount + CREATURE_EVENT_ON_DAMAGE = 40, // (event, creature, target, damage) - Can return new damage amount + CREATURE_EVENT_ON_AURA_REMOVE = 41, // (event, creature, aura, remove_mode) + CREATURE_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK = 42, // (event, creature, target, damage, spellInfo) - Can return new damage amount + CREATURE_EVENT_ON_MODIFY_MELEE_DAMAGE = 43, // (event, creature, target, damage) - Can return new damage amount + CREATURE_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN = 44, // (event, creature, target, damage, spellInfo) - Can return new damage amount + CREATURE_EVENT_ON_MODIFY_HEAL_RECEIVED = 45, // (event, creature, target, heal, spellInfo) - Can return new heal amount + CREATURE_EVENT_ON_DEAL_DAMAGE = 46, // (event, creature, target, damage, damagetype) - Can return new damage amount + CREATURE_EVENT_COUNT + }; + + enum GameObjectEvents + { + GAMEOBJECT_EVENT_ON_AIUPDATE = 1, // (event, go, diff) + GAMEOBJECT_EVENT_ON_SPAWN = 2, // (event, go) + GAMEOBJECT_EVENT_ON_DUMMY_EFFECT = 3, // (event, caster, spellid, effindex, go) - Can return true to stop normal action + GAMEOBJECT_EVENT_ON_QUEST_ACCEPT = 4, // (event, player, go, quest) - Can return true to stop normal action + GAMEOBJECT_EVENT_ON_QUEST_REWARD = 5, // (event, player, go, quest, opt) - Can return true to stop normal action + GAMEOBJECT_EVENT_ON_DIALOG_STATUS = 6, // (event, player, go) + GAMEOBJECT_EVENT_ON_DESTROYED = 7, // (event, go, attacker) + GAMEOBJECT_EVENT_ON_DAMAGED = 8, // (event, go, attacker) + GAMEOBJECT_EVENT_ON_LOOT_STATE_CHANGE = 9, // (event, go, state) + GAMEOBJECT_EVENT_ON_GO_STATE_CHANGED = 10, // (event, go, state) + // UNUSED = 11, // (event, gameobject) + GAMEOBJECT_EVENT_ON_ADD = 12, // (event, gameobject) + GAMEOBJECT_EVENT_ON_REMOVE = 13, // (event, gameobject) + GAMEOBJECT_EVENT_ON_USE = 14, // (event, go, player) - Can return true to stop normal action + GAMEOBJECT_EVENT_COUNT + }; + + enum ItemEvents + { + ITEM_EVENT_ON_DUMMY_EFFECT = 1, // (event, caster, spellid, effindex, item) + ITEM_EVENT_ON_USE = 2, // (event, player, item, target) - Can return false to stop the spell casting + ITEM_EVENT_ON_QUEST_ACCEPT = 3, // (event, player, item, quest) - Can return true + ITEM_EVENT_ON_EXPIRE = 4, // (event, player, itemid) - Can return true + ITEM_EVENT_ON_REMOVE = 5, // (event, player, item) - Can return true + ITEM_EVENT_COUNT + }; + + enum GossipEvents + { + GOSSIP_EVENT_ON_HELLO = 1, // (event, player, object) - Object is the Creature/GameObject/Item. Can return false to do default action. For item gossip can return false to stop spell casting. + GOSSIP_EVENT_ON_SELECT = 2, // (event, player, object, sender, intid, code, menu_id) - Object is the Creature/GameObject/Item/Player, menu_id is only for player gossip. Can return false to do default action. + GOSSIP_EVENT_COUNT + }; + + enum BGEvents + { + BG_EVENT_ON_START = 1, // (event, bg, bgId, instanceId) - Needs to be added to TC + BG_EVENT_ON_END = 2, // (event, bg, bgId, instanceId, winner) - Needs to be added to TC + BG_EVENT_ON_CREATE = 3, // (event, bg, bgId, instanceId) - Needs to be added to TC + BG_EVENT_ON_PRE_DESTROY = 4, // (event, bg, bgId, instanceId) - Needs to be added to TC + BG_EVENT_COUNT + }; + + enum InstanceEvents + { + INSTANCE_EVENT_ON_INITIALIZE = 1, // (event, instance_data, map) + INSTANCE_EVENT_ON_LOAD = 2, // (event, instance_data, map) + INSTANCE_EVENT_ON_UPDATE = 3, // (event, instance_data, map, diff) + INSTANCE_EVENT_ON_PLAYER_ENTER = 4, // (event, instance_data, map, player) + INSTANCE_EVENT_ON_CREATURE_CREATE = 5, // (event, instance_data, map, creature) + INSTANCE_EVENT_ON_GAMEOBJECT_CREATE = 6, // (event, instance_data, map, go) + INSTANCE_EVENT_ON_CHECK_ENCOUNTER_IN_PROGRESS = 7, // (event, instance_data, map) + INSTANCE_EVENT_COUNT + }; + + enum TicketEvents + { + TICKET_EVENT_ON_CREATE = 1, // (event, ticket) + TICKET_EVENT_UPDATE_LAST_CHANGE = 2, // (event, ticket, message) + TICKET_EVENT_ON_CLOSE = 3, // (event, ticket) + TICKET_EVENT_ON_RESOLVE = 4, // (event, ticket) + TICKET_EVENT_COUNT + }; + + enum SpellEvents + { + SPELL_EVENT_ON_PREPARE = 1, // (event, caster, spell) + SPELL_EVENT_ON_CAST = 2, // (event, caster, spell, skipCheck) + SPELL_EVENT_ON_CAST_CANCEL = 3, // (event, caster, spell, bySelf) + SPELL_EVENT_COUNT + }; + + enum AllCreatureEvents + { + ALL_CREATURE_EVENT_ON_ADD = 1, // (event, creature) + ALL_CREATURE_EVENT_ON_REMOVE = 2, // (event, creature) + ALL_CREATURE_EVENT_ON_SELECT_LEVEL = 3, // (event, creature_template, creature) + ALL_CREATURE_EVENT_ON_BEFORE_SELECT_LEVEL = 4, // (event, creature_template, creature, level) - Can return the new level + ALL_CREATURE_EVENT_ON_AURA_APPLY = 5, // (event, creature, aura) + ALL_CREATURE_EVENT_ON_HEAL = 6, // (event, creature, target, gain) - Can return new heal amount + ALL_CREATURE_EVENT_ON_DAMAGE = 7, // (event, creature, target, damage) - Can return new damage amount + ALL_CREATURE_EVENT_ON_AURA_REMOVE = 8, // (event, creature, aura, remove_mode) + ALL_CREATURE_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK = 9, // (event, creature, target, damage, spellInfo) - Can return new damage amount + ALL_CREATURE_EVENT_ON_MODIFY_MELEE_DAMAGE = 10, // (event, creature, target, damage) - Can return new damage amount + ALL_CREATURE_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN = 11, // (event, creature, target, damage, spellInfo) - Can return new damage amount + ALL_CREATURE_EVENT_ON_MODIFY_HEAL_RECEIVED = 12, // (event, creature, target, heal, spellInfo) - Can return new heal amount + ALL_CREATURE_EVENT_ON_DEAL_DAMAGE = 13, // (event, creature, target, damage, damagetype) - Can return new damage amount + ALL_CREATURE_EVENT_COUNT + }; +}; + +#endif // _HOOKS_H diff --git a/modules/mod-ale/src/LuaEngine/HttpManager.cpp b/modules/mod-ale/src/LuaEngine/HttpManager.cpp new file mode 100644 index 0000000..f7079cf --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/HttpManager.cpp @@ -0,0 +1,275 @@ +#include +extern "C" +{ +#include "lua.h" +#include "lauxlib.h" +}; + +#define CPPHTTPLIB_OPENSSL_SUPPORT + +#include "libs/httplib.h" +#include "HttpManager.h" +#include "LuaEngine.h" + +HttpWorkItem::HttpWorkItem(int funcRef, const std::string& httpVerb, const std::string& url, const std::string& body, const std::string& contentType, const httplib::Headers& headers) + : funcRef(funcRef), + httpVerb(httpVerb), + url(url), + body(body), + contentType(contentType), + headers(headers) +{ } + +HttpResponse::HttpResponse(int funcRef, int statusCode, const std::string& body, const httplib::Headers& headers) + : funcRef(funcRef), + statusCode(statusCode), + body(body), + headers(headers) +{ } + +HttpManager::HttpManager() + : workQueue(16), + responseQueue(16), + startedWorkerThread(false), + cancelationToken(false), + condVar(), + condVarMutex(), + parseUrlRegex("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?") +{ + StartHttpWorker(); +} + +HttpManager::~HttpManager() +{ + StopHttpWorker(); +} + +void HttpManager::PushRequest(HttpWorkItem* item) +{ + std::unique_lock lock(condVarMutex); + workQueue.push(item); + condVar.notify_one(); +} + +void HttpManager::StartHttpWorker() +{ + ClearQueues(); + + if (!startedWorkerThread) + { + cancelationToken.store(false); + workerThread = std::thread(&HttpManager::HttpWorkerThread, this); + startedWorkerThread = true; + } +} + +void HttpManager::ClearQueues() +{ + while (workQueue.front()) + { + HttpWorkItem* item = *workQueue.front(); + if (item != nullptr) + { + delete item; + } + workQueue.pop(); + } + + while (responseQueue.front()) + { + HttpResponse* item = *responseQueue.front(); + if (item != nullptr) + { + delete item; + } + responseQueue.pop(); + } +} + +void HttpManager::StopHttpWorker() +{ + if (!startedWorkerThread) + { + return; + } + + cancelationToken.store(true); + condVar.notify_one(); + workerThread.join(); + ClearQueues(); + startedWorkerThread = false; +} + +void HttpManager::HttpWorkerThread() +{ + while (true) + { + { + std::unique_lock lock(condVarMutex); + condVar.wait(lock, [&] { return workQueue.front() != nullptr || cancelationToken.load(); }); + } + + if (cancelationToken.load()) + { + break; + } + if (!workQueue.front()) + { + continue; + } + + HttpWorkItem* req = *workQueue.front(); + workQueue.pop(); + if (!req) + { + continue; + } + + try + { + std::string host; + std::string path; + + if (!ParseUrl(req->url, host, path)) { + ALE_LOG_ERROR("[ALE]: Could not parse URL {}", req->url); + continue; + } + + httplib::Client cli(host); + cli.set_connection_timeout(0, 3000000); // 3 seconds + cli.set_read_timeout(5, 0); // 5 seconds + cli.set_write_timeout(5, 0); // 5 seconds + + httplib::Result res = DoRequest(cli, req, path); + httplib::Error err = res.error(); + if (err != httplib::Error::Success) + { + ALE_LOG_ERROR("[ALE]: HTTP request error: {}", httplib::to_string(err)); + continue; + } + + if (res->status == 301) + { + std::string location = res->get_header_value("Location"); + std::string host; + std::string path; + + if (!ParseUrl(location, host, path)) + { + ALE_LOG_ERROR("[ALE]: Could not parse URL after redirect: {}", location); + continue; + } + httplib::Client cli2(host); + cli2.set_connection_timeout(0, 3000000); // 3 seconds + cli2.set_read_timeout(5, 0); // 5 seconds + cli2.set_write_timeout(5, 0); // 5 seconds + res = DoRequest(cli2, req, path); + } + + responseQueue.push(new HttpResponse(req->funcRef, res->status, res->body, res->headers)); + } + catch (const std::exception& ex) + { + ALE_LOG_ERROR("[ALE]: HTTP request error: {}", ex.what()); + } + + delete req; + } +} + +httplib::Result HttpManager::DoRequest(httplib::Client& client, HttpWorkItem* req, const std::string& urlPath) +{ + const char* path = urlPath.c_str(); + if (req->httpVerb == "GET") + { + return client.Get(path, req->headers); + } + if (req->httpVerb == "HEAD") + { + return client.Head(path, req->headers); + } + if (req->httpVerb == "POST") + { + return client.Post(path, req->headers, req->body, req->contentType.c_str()); + } + if (req->httpVerb == "PUT") + { + return client.Put(path, req->headers, req->body, req->contentType.c_str()); + } + if (req->httpVerb == "PATCH") + { + return client.Patch(path, req->headers, req->body, req->contentType.c_str()); + } + if (req->httpVerb == "DELETE") + { + return client.Delete(path, req->headers); + } + if (req->httpVerb == "OPTIONS") + { + return client.Options(path, req->headers); + } + + ALE_LOG_ERROR("[ALE]: HTTP request error: invalid HTTP verb {}", req->httpVerb); + return client.Get(path, req->headers); +} + +bool HttpManager::ParseUrl(const std::string& url, std::string& host, std::string& path) +{ + std::smatch matches; + + if (!std::regex_search(url, matches, parseUrlRegex)) + { + return false; + } + + std::string scheme = matches[2]; + std::string authority = matches[4]; + std::string query = matches[7]; + host = scheme + "://" + authority; + path = matches[5]; + if (path.empty()) + { + path = "/"; + } + path += (query.empty() ? "" : "?") + query; + + return true; +} + +void HttpManager::HandleHttpResponses() +{ + while (!responseQueue.empty()) + { + HttpResponse* res = *responseQueue.front(); + responseQueue.pop(); + + if (res == nullptr) + { + continue; + } + + LOCK_ALE; + + lua_State* L = ALE::GALE->L; + + // Get function + lua_rawgeti(L, LUA_REGISTRYINDEX, res->funcRef); + + // Push parameters + ALE::Push(L, res->statusCode); + ALE::Push(L, res->body); + lua_newtable(L); + for (const auto& item : res->headers) { + ALE::Push(L, item.first); + ALE::Push(L, item.second); + lua_settable(L, -3); + } + + // Call function + ALE::GALE->ExecuteCall(3, 0); + + luaL_unref(L, LUA_REGISTRYINDEX, res->funcRef); + + delete res; + } +} diff --git a/modules/mod-ale/src/LuaEngine/HttpManager.h b/modules/mod-ale/src/LuaEngine/HttpManager.h new file mode 100644 index 0000000..cab17a4 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/HttpManager.h @@ -0,0 +1,61 @@ +#ifndef ALE_HTTP_MANAGER_H +#define ALE_HTTP_MANAGER_H + +#include + +#include "libs/httplib.h" +#include "libs/rigtorp/SPSCQueue.h" + +struct HttpWorkItem +{ +public: + HttpWorkItem(int funcRef, const std::string& httpVerb, const std::string& url, const std::string& body, const std::string &contentType, const httplib::Headers& headers); + + int funcRef; + std::string httpVerb; + std::string url; + std::string body; + std::string contentType; + httplib::Headers headers; +}; + +struct HttpResponse +{ +public: + HttpResponse(int funcRef, int statusCode, const std::string& body, const httplib::Headers& headers); + + int funcRef; + int statusCode; + std::string body; + httplib::Headers headers; +}; + + +class HttpManager +{ +public: + HttpManager(); + ~HttpManager(); + + void StartHttpWorker(); + void StopHttpWorker(); + void PushRequest(HttpWorkItem* item); + void HandleHttpResponses(); + +private: + void ClearQueues(); + void HttpWorkerThread(); + bool ParseUrl(const std::string& url, std::string& host, std::string& path); + httplib::Result DoRequest(httplib::Client& client, HttpWorkItem* req, const std::string& path); + + rigtorp::SPSCQueue workQueue; + rigtorp::SPSCQueue responseQueue; + std::thread workerThread; + bool startedWorkerThread; + std::atomic_bool cancelationToken; + std::condition_variable condVar; + std::mutex condVarMutex; + std::regex parseUrlRegex; +}; + +#endif // #ifndef ALE_HTTP_MANAGER_H diff --git a/modules/mod-ale/src/LuaEngine/LuaEngine.cpp b/modules/mod-ale/src/LuaEngine/LuaEngine.cpp new file mode 100644 index 0000000..c647d68 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/LuaEngine.cpp @@ -0,0 +1,1722 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#include "Hooks.h" +#include "LuaEngine.h" +#include "BindingMap.h" +#include "Chat.h" +#include "ALECompat.h" +#include "ALEEventMgr.h" +#include "ALEIncludes.h" +#include "ALETemplate.h" +#include "ALEUtility.h" +#include "ALECreatureAI.h" +#include "ALEInstanceAI.h" + +#if AC_PLATFORM == AC_PLATFORM_WINDOWS +#define ALE_WINDOWS +#endif + +// Some dummy includes containing BOOST_VERSION: +// ObjectAccessor.h Config.h Log.h +#define USING_BOOST + +#include +#include +#include +#include +#include +#include + +extern "C" +{ +// Base lua libraries +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" + +// Additional lua libraries +}; + +ALE::ScriptList ALE::lua_scripts; +ALE::ScriptList ALE::lua_extensions; +std::string ALE::lua_folderpath; +std::string ALE::lua_requirepath; +std::string ALE::lua_requirecpath; +ALE* ALE::GALE = NULL; +bool ALE::reload = false; +bool ALE::initialized = false; +ALE::LockType ALE::lock; +std::unique_ptr ALE::fileWatcher; + +// Global bytecode cache that survives ALE reloads +static std::unordered_map globalBytecodeCache; +static std::unordered_map timestampCache; +static std::mutex globalCacheMutex; + +extern void RegisterFunctions(ALE* E); + +void ALE::Initialize() +{ + LOCK_ALE; + ASSERT(!IsInitialized()); + + // For instance data the data column needs to be able to hold more than 255 characters (tinytext) + // so we change it to TEXT automatically on startup + CharacterDatabase.DirectExecute("ALTER TABLE `instance` CHANGE COLUMN `data` `data` TEXT NOT NULL"); + + LoadScriptPaths(); + + // Must be before creating GALE + // This is checked on ALE creation + initialized = true; + + // Create global ALE + GALE = new ALE(); + + // Start file watcher if enabled + if (ALEConfig::GetInstance().IsAutoReloadEnabled()) + { + uint32 watchInterval = eConfigMgr->GetOption("ALE.AutoReloadInterval", 1); + fileWatcher = std::make_unique(); + fileWatcher->StartWatching(lua_folderpath, watchInterval); + } +} + +void ALE::Uninitialize() +{ + LOCK_ALE; + ASSERT(IsInitialized()); + + // Stop file watcher + if (fileWatcher) + { + fileWatcher->StopWatching(); + fileWatcher.reset(); + } + + delete GALE; + GALE = NULL; + + lua_scripts.clear(); + lua_extensions.clear(); + + // Clear global cache on shutdown + ClearGlobalCache(); + + initialized = false; +} + +void ALE::LoadScriptPaths() +{ + uint32 oldMSTime = ALEUtil::GetCurrTime(); + + lua_scripts.clear(); + lua_extensions.clear(); + + lua_folderpath = ALEConfig::GetInstance().GetScriptPath(); + const std::string& lua_path_extra = static_cast(ALEConfig::GetInstance().GetRequirePath()); + const std::string& lua_cpath_extra = static_cast(ALEConfig::GetInstance().GetRequireCPath()); + +#ifndef ALE_WINDOWS + if (lua_folderpath[0] == '~') + if (const char* home = getenv("HOME")) + lua_folderpath.replace(0, 1, home); +#endif + ALE_LOG_INFO("[ALE]: Searching scripts from `{}`", lua_folderpath); + + // clear all cache variables + lua_requirepath.clear(); + lua_requirecpath.clear(); + + GetScripts(lua_folderpath); + + // append our custom require paths and cpaths if the config variables are not empty + if (!lua_path_extra.empty()) + lua_requirepath += lua_path_extra; + + if (!lua_cpath_extra.empty()) + lua_requirecpath += lua_cpath_extra; + + // Erase last ; + if (!lua_requirepath.empty()) + lua_requirepath.erase(lua_requirepath.end() - 1); + + if (!lua_requirecpath.empty()) + lua_requirecpath.erase(lua_requirecpath.end() - 1); + + ALE_LOG_DEBUG("[ALE]: Loaded {} scripts in {} ms", lua_scripts.size() + lua_extensions.size(), ALEUtil::GetTimeDiff(oldMSTime)); +} + +void ALE::_ReloadALE() +{ + LOCK_ALE; + ASSERT(IsInitialized()); + + if (eConfigMgr->GetOption("ALE.PlayerAnnounceReload", false)) + eWorldSessionMgr->SendServerMessage(SERVER_MSG_STRING, "Reloading ALE..."); + else + ChatHandler(nullptr).SendGMText(SERVER_MSG_STRING, "Reloading ALE..."); + + // Remove all timed events + sALE->eventMgr->SetStates(LUAEVENT_STATE_ERASE); + + // Close lua + sALE->CloseLua(); + + // Reload script paths + LoadScriptPaths(); + + // Open new lua and libaraies + sALE->OpenLua(); + + // Run scripts from laoded paths + sALE->RunScripts(); + + reload = false; +} + +ALE::ALE() : +event_level(0), +push_counter(0), + +L(NULL), +eventMgr(NULL), +httpManager(), +queryProcessor(), + +ServerEventBindings(NULL), +PlayerEventBindings(NULL), +GuildEventBindings(NULL), +GroupEventBindings(NULL), +VehicleEventBindings(NULL), +BGEventBindings(NULL), +AllCreatureEventBindings(NULL), + +PacketEventBindings(NULL), +CreatureEventBindings(NULL), +CreatureGossipBindings(NULL), +GameObjectEventBindings(NULL), +GameObjectGossipBindings(NULL), +ItemEventBindings(NULL), +ItemGossipBindings(NULL), +PlayerGossipBindings(NULL), +MapEventBindings(NULL), +InstanceEventBindings(NULL), +TicketEventBindings(NULL), +SpellEventBindings(NULL), + +CreatureUniqueBindings(NULL) +{ + ASSERT(IsInitialized()); + + OpenLua(); + + // Replace this with map insert if making multithread version + + // Set event manager. Must be after setting sALE + // on multithread have a map of state pointers and here insert this pointer to the map and then save a pointer of that pointer to the EventMgr + eventMgr = new EventMgr(&ALE::GALE); +} + +ALE::~ALE() +{ + ASSERT(IsInitialized()); + + CloseLua(); + + delete eventMgr; + eventMgr = NULL; +} + +void ALE::CloseLua() +{ + OnLuaStateClose(); + + DestroyBindStores(); + + // Must close lua state after deleting stores and mgr + if (L) + lua_close(L); + L = NULL; + + instanceDataRefs.clear(); + continentDataRefs.clear(); +} + +void ALE::OpenLua() +{ + if (!ALEConfig::GetInstance().IsALEEnabled()) + { + ALE_LOG_INFO("[ALE]: ALE is disabled in config"); + return; + } + + L = luaL_newstate(); + + lua_pushlightuserdata(L, this); + lua_setfield(L, LUA_REGISTRYINDEX, ALE_STATE_PTR); + + CreateBindStores(); + + // open base lua libraries + luaL_openlibs(L); + + // open additional lua libraries + + // Register methods and functions + RegisterFunctions(this); + + // Set lua require folder paths (scripts folder structure) + lua_getglobal(L, "package"); + lua_pushstring(L, GetRequirePath().c_str()); + lua_setfield(L, -2, "path"); + lua_pushstring(L, GetRequireCPath().c_str()); + lua_setfield(L, -2, "cpath"); + + // Set package.loaders loader for precompiled scripts + lua_getfield(L, -1, "loaders"); + if (lua_isnil(L, -1)) { + // Lua 5.2+ uses searchers instead of loaders + lua_pop(L, 1); + lua_getfield(L, -1, "searchers"); + } + + lua_pop(L, 1); +} + +void ALE::CreateBindStores() +{ + DestroyBindStores(); + + ServerEventBindings = new BindingMap< EventKey >(L); + PlayerEventBindings = new BindingMap< EventKey >(L); + GuildEventBindings = new BindingMap< EventKey >(L); + GroupEventBindings = new BindingMap< EventKey >(L); + VehicleEventBindings = new BindingMap< EventKey >(L); + BGEventBindings = new BindingMap< EventKey >(L); + TicketEventBindings = new BindingMap< EventKey >(L); + AllCreatureEventBindings = new BindingMap< EventKey >(L); + + PacketEventBindings = new BindingMap< EntryKey >(L); + CreatureEventBindings = new BindingMap< EntryKey >(L); + CreatureGossipBindings = new BindingMap< EntryKey >(L); + GameObjectEventBindings = new BindingMap< EntryKey >(L); + GameObjectGossipBindings = new BindingMap< EntryKey >(L); + ItemEventBindings = new BindingMap< EntryKey >(L); + ItemGossipBindings = new BindingMap< EntryKey >(L); + PlayerGossipBindings = new BindingMap< EntryKey >(L); + MapEventBindings = new BindingMap< EntryKey >(L); + InstanceEventBindings = new BindingMap< EntryKey >(L); + SpellEventBindings = new BindingMap< EntryKey >(L); + + CreatureUniqueBindings = new BindingMap< UniqueObjectKey >(L); +} + +void ALE::DestroyBindStores() +{ + delete ServerEventBindings; + delete PlayerEventBindings; + delete GuildEventBindings; + delete GroupEventBindings; + delete VehicleEventBindings; + delete AllCreatureEventBindings; + + delete PacketEventBindings; + delete CreatureEventBindings; + delete CreatureGossipBindings; + delete GameObjectEventBindings; + delete GameObjectGossipBindings; + delete ItemEventBindings; + delete ItemGossipBindings; + delete PlayerGossipBindings; + delete BGEventBindings; + delete MapEventBindings; + delete InstanceEventBindings; + delete SpellEventBindings; + + delete CreatureUniqueBindings; + + ServerEventBindings = NULL; + PlayerEventBindings = NULL; + GuildEventBindings = NULL; + GroupEventBindings = NULL; + VehicleEventBindings = NULL; + AllCreatureEventBindings = NULL; + + PacketEventBindings = NULL; + CreatureEventBindings = NULL; + CreatureGossipBindings = NULL; + GameObjectEventBindings = NULL; + GameObjectGossipBindings = NULL; + ItemEventBindings = NULL; + ItemGossipBindings = NULL; + PlayerGossipBindings = NULL; + BGEventBindings = NULL; + MapEventBindings = NULL; + InstanceEventBindings = NULL; + SpellEventBindings = NULL; + + CreatureUniqueBindings = NULL; +} + +void ALE::AddScriptPath(std::string filename, const std::string& fullpath) +{ + ALE_LOG_DEBUG("[ALE]: AddScriptPath Checking file `{}`", fullpath); + + // split file name + std::size_t extDot = filename.find_last_of('.'); + if (extDot == std::string::npos) + return; + std::string ext = filename.substr(extDot); + filename = filename.substr(0, extDot); + + // check extension and add path to scripts to load + if (ext != ".lua" && ext != ".dll" && ext != ".so" && ext != ".ext" && ext !=".moon" && ext != ".out") + return; + bool extension = ext == ".ext"; + + LuaScript script; + script.fileext = ext; + script.filename = filename; + script.filepath = fullpath; + script.modulepath = fullpath.substr(0, fullpath.length() - filename.length() - ext.length()); + if (extension) + lua_extensions.push_back(script); + else + lua_scripts.push_back(script); + ALE_LOG_DEBUG("[ALE]: AddScriptPath add path `{}`", fullpath); +} + +std::time_t ALE::GetFileModTime(const std::string& filepath) +{ + struct stat fileInfo; + if (stat(filepath.c_str(), &fileInfo) == 0) + return fileInfo.st_mtime; + return 0; +} + +std::time_t ALE::GetFileModTimeWithCache(const std::string& filepath) +{ + auto it = timestampCache.find(filepath); + if (it != timestampCache.end()) + return it->second; + + std::time_t modTime = GetFileModTime(filepath); + timestampCache[filepath] = modTime; + return modTime; +} + +bool ALE::CompileScriptToGlobalCache(const std::string& filepath) +{ + std::lock_guard lock(globalCacheMutex); + + lua_State* tempL = luaL_newstate(); + if (!tempL) + return false; + + int result = luaL_loadfile(tempL, filepath.c_str()); + if (result != LUA_OK) + { + lua_close(tempL); + return false; + } + + std::time_t modTime = GetFileModTime(filepath); + + auto& cacheEntry = globalBytecodeCache[filepath]; + cacheEntry.last_modified = modTime; + cacheEntry.filepath = filepath; + cacheEntry.bytecode.clear(); + cacheEntry.bytecode.reserve(1024); + + struct BytecodeWriter { + BytecodeBuffer* buffer; + static int writer(lua_State*, const void* p, size_t sz, void* ud) { + BytecodeWriter* w = static_cast(ud); + const uint8* bytes = static_cast(p); + w->buffer->insert(w->buffer->end(), bytes, bytes + sz); + return 0; + } + }; + + BytecodeWriter writer; + writer.buffer = &cacheEntry.bytecode; + + int dumpResult = lua_dump(tempL, BytecodeWriter::writer, &writer); + if (dumpResult != LUA_OK || cacheEntry.bytecode.empty()) + { + globalBytecodeCache.erase(filepath); + lua_close(tempL); + return false; + } + + lua_close(tempL); + return true; +} + +bool ALE::CompileMoonScriptToGlobalCache(const std::string& filepath) +{ + std::lock_guard lock(globalCacheMutex); + + lua_State* tempL = luaL_newstate(); + if (!tempL) + return false; + + luaL_openlibs(tempL); + + std::string moonscriptLoader = "return require('moonscript').loadfile([[" + filepath + "]])"; + int result = luaL_loadstring(tempL, moonscriptLoader.c_str()); + if (result != LUA_OK) + { + lua_close(tempL); + return false; + } + + result = lua_pcall(tempL, 0, 1, 0); + if (result != LUA_OK) + { + lua_close(tempL); + return false; + } + + std::time_t modTime = GetFileModTime(filepath); + + auto& cacheEntry = globalBytecodeCache[filepath]; + cacheEntry.last_modified = modTime; + cacheEntry.filepath = filepath; + cacheEntry.bytecode.clear(); + cacheEntry.bytecode.reserve(2048); + + struct BytecodeWriter { + BytecodeBuffer* buffer; + static int writer(lua_State*, const void* p, size_t sz, void* ud) { + BytecodeWriter* w = static_cast(ud); + const uint8* bytes = static_cast(p); + w->buffer->insert(w->buffer->end(), bytes, bytes + sz); + return 0; + } + }; + + BytecodeWriter writer; + writer.buffer = &cacheEntry.bytecode; + + int dumpResult = lua_dump(tempL, BytecodeWriter::writer, &writer); + if (dumpResult != LUA_OK || cacheEntry.bytecode.empty()) + { + globalBytecodeCache.erase(filepath); + lua_close(tempL); + return false; + } + + lua_close(tempL); + return true; +} + +int ALE::TryLoadFromGlobalCache(lua_State* L, const std::string& filepath) +{ + std::lock_guard lock(globalCacheMutex); + + auto it = globalBytecodeCache.find(filepath); + if (it == globalBytecodeCache.end() || it->second.bytecode.empty()) + return LUA_ERRFILE; + + std::time_t currentModTime = GetFileModTimeWithCache(filepath); + if (it->second.last_modified != currentModTime || currentModTime == 0) + return LUA_ERRFILE; + + return luaL_loadbuffer(L, reinterpret_cast(it->second.bytecode.data()), it->second.bytecode.size(), filepath.c_str()); +} + +int ALE::LoadScriptWithCache(lua_State* L, const std::string& filepath, bool isMoonScript, uint32* compiledCount, uint32* cachedCount) +{ + bool cacheEnabled = ALEConfig::GetInstance().IsByteCodeCacheEnabled(); + + if (cacheEnabled) + { + int result = TryLoadFromGlobalCache(L, filepath); + if (result == LUA_OK) + { + if (cachedCount) (*cachedCount)++; + return LUA_OK; + } + + bool compileSuccess = isMoonScript ? + CompileMoonScriptToGlobalCache(filepath) : + CompileScriptToGlobalCache(filepath); + + if (compileSuccess) + { + if (compiledCount) (*compiledCount)++; + std::lock_guard lock(globalCacheMutex); + auto it = globalBytecodeCache.find(filepath); + if (it != globalBytecodeCache.end() && !it->second.bytecode.empty()) + { + result = luaL_loadbuffer(L, reinterpret_cast(it->second.bytecode.data()), it->second.bytecode.size(), filepath.c_str()); + if (result == LUA_OK) + return LUA_OK; + } + } + } + + if (isMoonScript) + { + std::string str = "return require('moonscript').loadfile([[" + filepath + "]])"; + int result = luaL_loadstring(L, str.c_str()); + if (result != LUA_OK) + return result; + return lua_pcall(L, 0, LUA_MULTRET, 0); + } + else + { + return luaL_loadfile(L, filepath.c_str()); + } +} + +void ALE::ClearGlobalCache() +{ + std::lock_guard lock(globalCacheMutex); + globalBytecodeCache.clear(); + timestampCache.clear(); + ALE_LOG_INFO("[ALE]: Global bytecode cache cleared"); +} + +void ALE::ClearTimestampCache() +{ + std::lock_guard lock(globalCacheMutex); + timestampCache.clear(); +} + +size_t ALE::GetGlobalCacheSize() +{ + std::lock_guard lock(globalCacheMutex); + return globalBytecodeCache.size(); +} + +int ALE::LoadCompiledScript(lua_State* L, const std::string& filepath) +{ + std::ifstream file(filepath, std::ios::binary); + if (!file.is_open()) + return LUA_ERRFILE; + + file.seekg(0, std::ios::end); + size_t fileSize = file.tellg(); + file.seekg(0, std::ios::beg); + + std::vector buffer(fileSize); + file.read(buffer.data(), fileSize); + file.close(); + + return luaL_loadbuffer(L, buffer.data(), fileSize, filepath.c_str()); +} + +// Finds lua script files from given path (including subdirectories) and pushes them to scripts +void ALE::GetScripts(std::string path) +{ + ALE_LOG_DEBUG("[ALE]: GetScripts from path `{}`", path); + + boost::filesystem::path someDir(path); + boost::filesystem::directory_iterator end_iter; + + if (boost::filesystem::exists(someDir) && boost::filesystem::is_directory(someDir)) + { + lua_requirepath += + path + "/?.lua;" + + path + "/?.moon;" + + path + "/?.ext;"; + + lua_requirecpath += + path + "/?.dll;" + + path + "/?.so;"; + + for (boost::filesystem::directory_iterator dir_iter(someDir); dir_iter != end_iter; ++dir_iter) + { + std::string fullpath = dir_iter->path().generic_string(); + + // Check if file is hidden +#ifdef ALE_WINDOWS + DWORD dwAttrib = GetFileAttributes(fullpath.c_str()); + if (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_HIDDEN)) + continue; +#else + std::string name = dir_iter->path().filename().generic_string().c_str(); + if (name[0] == '.') + continue; +#endif + + // load subfolder + if (boost::filesystem::is_directory(dir_iter->status())) + { + GetScripts(fullpath); + continue; + } + + if (boost::filesystem::is_regular_file(dir_iter->status())) + { + // was file, try add + std::string filename = dir_iter->path().filename().generic_string(); + AddScriptPath(filename, fullpath); + } + } + } +} + +static bool ScriptPathComparator(const LuaScript& first, const LuaScript& second) +{ + return first.filepath < second.filepath; +} + +void ALE::RunScripts() +{ + LOCK_ALE; + if (!ALEConfig::GetInstance().IsALEEnabled()) + return; + + uint32 oldMSTime = ALEUtil::GetCurrTime(); + uint32 count = 0; + uint32 compiledCount = 0; + uint32 cachedCount = 0; + uint32 precompiledCount = 0; + bool cacheEnabled = eConfigMgr->GetOption("ALE.BytecodeCache", true); + + if (cacheEnabled) + ClearTimestampCache(); + + ScriptList scripts; + lua_extensions.sort(ScriptPathComparator); + lua_scripts.sort(ScriptPathComparator); + scripts.insert(scripts.end(), lua_extensions.begin(), lua_extensions.end()); + scripts.insert(scripts.end(), lua_scripts.begin(), lua_scripts.end()); + + std::unordered_map loaded; // filename, path + + lua_getglobal(L, "package"); + // Stack: package + luaL_getsubtable(L, -1, "loaded"); + // Stack: package, modules + int modules = lua_gettop(L); + for (ScriptList::iterator it = scripts.begin(); it != scripts.end(); ++it) + { + // Check that no duplicate names exist + if (loaded.find(it->filename) != loaded.end()) + { + ALE_LOG_ERROR("[ALE]: Error loading `{}`. File with same name already loaded from `{}`, rename either file", it->filepath, loaded[it->filename]); + continue; + } + loaded[it->filename] = it->filepath; + + lua_getfield(L, modules, it->filename.c_str()); + // Stack: package, modules, module + if (!lua_isnoneornil(L, -1)) + { + lua_pop(L, 1); + ALE_LOG_DEBUG("[ALE]: `{}` was already loaded or required", it->filepath); + continue; + } + lua_pop(L, 1); + // Stack: package, modules + + if (it->fileext == ".moon") + { + if (LoadScriptWithCache(L, it->filepath, true, &compiledCount, &cachedCount)) + { + // Stack: package, modules, errmsg + ALE_LOG_ERROR("[ALE]: Error loading MoonScript `{}`", it->filepath); + Report(L); + // Stack: package, modules + continue; + } + } + else if (it->fileext == ".out") + { + if (LoadCompiledScript(L, it->filepath)) + { + // Stack: package, modules, errmsg + ALE_LOG_ERROR("[ALE]: Error loading compiled script `{}`", it->filepath); + Report(L); + // Stack: package, modules + continue; + } + precompiledCount++; + } + else if (it->fileext == ".lua" || it->fileext == ".ext") + { + if (LoadScriptWithCache(L, it->filepath, false, &compiledCount, &cachedCount)) + { + // Stack: package, modules, errmsg + ALE_LOG_ERROR("[ALE]: Error loading `{}`", it->filepath); + Report(L); + // Stack: package, modules + continue; + } + } + else + { + if (luaL_loadfile(L, it->filepath.c_str())) + { + // Stack: package, modules, errmsg + ALE_LOG_ERROR("[ALE]: Error loading `{}`", it->filepath); + Report(L); + // Stack: package, modules + continue; + } + } + + // Stack: package, modules, filefunc + if (ExecuteCall(0, 1)) + { + // Stack: package, modules, result + if (lua_isnoneornil(L, -1) || (lua_isboolean(L, -1) && !lua_toboolean(L, -1))) + { + // if result evaluates to false, change it to true + lua_pop(L, 1); + Push(L, true); + } + lua_setfield(L, modules, it->filename.c_str()); + // Stack: package, modules + + // successfully loaded and ran file + ALE_LOG_DEBUG("[ALE]: Successfully loaded `{}`", it->filepath); + ++count; + continue; + } + } + // Stack: package, modules + lua_pop(L, 2); + + std::string details = ""; + if (cacheEnabled && (compiledCount > 0 || cachedCount > 0 || precompiledCount > 0)) + { + details = fmt::format("({} compiled, {} cached, {} pre-compiled)", compiledCount, cachedCount, precompiledCount); + } + ALE_LOG_INFO("[ALE]: Executed {} Lua scripts in {} ms {}", count, ALEUtil::GetTimeDiff(oldMSTime), details); + + OnLuaStateOpen(); +} + +void ALE::InvalidateObjects() +{ + ++callstackid; + ASSERT(callstackid && "Callstackid overflow"); +} + +void ALE::Report(lua_State* _L) +{ + const char* msg = lua_tostring(_L, -1); + ALE_LOG_ERROR("{}", msg); + lua_pop(_L, 1); +} + +// Borrowed from http://stackoverflow.com/questions/12256455/print-stacktrace-from-c-code-with-embedded-lua +int ALE::StackTrace(lua_State *_L) +{ + // Stack: errmsg + if (!lua_isstring(_L, -1)) /* 'message' not a string? */ + return 1; /* keep it intact */ + // Stack: errmsg, debug + lua_getglobal(_L, "debug"); + if (!lua_istable(_L, -1)) + { + lua_pop(_L, 1); + return 1; + } + // Stack: errmsg, debug, traceback + lua_getfield(_L, -1, "traceback"); + if (!lua_isfunction(_L, -1)) + { + lua_pop(_L, 2); + return 1; + } + lua_pushvalue(_L, -3); /* pass error message */ + lua_pushinteger(_L, 1); /* skip this function and traceback */ + // Stack: errmsg, debug, traceback, errmsg, 2 + lua_call(_L, 2, 1); /* call debug.traceback */ + + // dirty stack? + // Stack: errmsg, debug, tracemsg + sALE->OnError(std::string(lua_tostring(_L, -1))); + return 1; +} + +bool ALE::ExecuteCall(int params, int res) +{ + int top = lua_gettop(L); + int base = top - params; + + // Expected: function, [parameters] + ASSERT(base > 0); + + // Check function type + if (!lua_isfunction(L, base)) + { + ALE_LOG_ERROR("[ALE]: Cannot execute call: registered value is {}, not a function.", luaL_tolstring(L, base, NULL)); + ASSERT(false); // stack probably corrupt + } + + bool usetrace = ALEConfig::GetInstance().IsTraceBackEnabled(); + if (usetrace) + { + lua_pushcfunction(L, &StackTrace); + // Stack: function, [parameters], traceback + lua_insert(L, base); + // Stack: traceback, function, [parameters] + } + + // Objects are invalidated when event_level hits 0 + ++event_level; + int result = lua_pcall(L, params, res, usetrace ? base : 0); + --event_level; + + if (usetrace) + { + // Stack: traceback, [results or errmsg] + lua_remove(L, base); + } + // Stack: [results or errmsg] + + // lua_pcall returns 0 on success. + // On error print the error and push nils for expected amount of returned values + if (result) + { + // Stack: errmsg + Report(L); + + // Force garbage collect + lua_gc(L, LUA_GCCOLLECT, 0); + + // Push nils for expected amount of results + for (int i = 0; i < res; ++i) + lua_pushnil(L); + // Stack: [nils] + return false; + } + + // Stack: [results] + return true; +} + +void ALE::Push(lua_State* luastate) +{ + lua_pushnil(luastate); +} +void ALE::Push(lua_State* luastate, const long long l) +{ + ALETemplate::Push(luastate, new long long(l)); +} +void ALE::Push(lua_State* luastate, const unsigned long long l) +{ + ALETemplate::Push(luastate, new unsigned long long(l)); +} +void ALE::Push(lua_State* luastate, const long l) +{ + Push(luastate, static_cast(l)); +} +void ALE::Push(lua_State* luastate, const unsigned long l) +{ + Push(luastate, static_cast(l)); +} +void ALE::Push(lua_State* luastate, const int i) +{ + lua_pushinteger(luastate, i); +} +void ALE::Push(lua_State* luastate, const unsigned int u) +{ + lua_pushunsigned(luastate, u); +} +void ALE::Push(lua_State* luastate, const double d) +{ + lua_pushnumber(luastate, d); +} +void ALE::Push(lua_State* luastate, const float f) +{ + lua_pushnumber(luastate, f); +} +void ALE::Push(lua_State* luastate, const bool b) +{ + lua_pushboolean(luastate, b); +} +void ALE::Push(lua_State* luastate, const std::string& str) +{ + lua_pushstring(luastate, str.c_str()); +} +void ALE::Push(lua_State* luastate, const char* str) +{ + lua_pushstring(luastate, str); +} +void ALE::Push(lua_State* luastate, Pet const* pet) +{ + Push(luastate, pet); +} +void ALE::Push(lua_State* luastate, TempSummon const* summon) +{ + Push(luastate, summon); +} +void ALE::Push(lua_State* luastate, Unit const* unit) +{ + if (!unit) + { + Push(luastate); + return; + } + switch (unit->GetTypeId()) + { + case TYPEID_UNIT: + Push(luastate, unit->ToCreature()); + break; + case TYPEID_PLAYER: + Push(luastate, unit->ToPlayer()); + break; + default: + ALETemplate::Push(luastate, unit); + } +} +void ALE::Push(lua_State* luastate, WorldObject const* obj) +{ + if (!obj) + { + Push(luastate); + return; + } + switch (obj->GetTypeId()) + { + case TYPEID_UNIT: + Push(luastate, obj->ToCreature()); + break; + case TYPEID_PLAYER: + Push(luastate, obj->ToPlayer()); + break; + case TYPEID_GAMEOBJECT: + Push(luastate, obj->ToGameObject()); + break; + case TYPEID_CORPSE: + Push(luastate, obj->ToCorpse()); + break; + default: + ALETemplate::Push(luastate, obj); + } +} +void ALE::Push(lua_State* luastate, Object const* obj) +{ + if (!obj) + { + Push(luastate); + return; + } + switch (obj->GetTypeId()) + { + case TYPEID_UNIT: + Push(luastate, obj->ToCreature()); + break; + case TYPEID_PLAYER: + Push(luastate, obj->ToPlayer()); + break; + case TYPEID_GAMEOBJECT: + Push(luastate, obj->ToGameObject()); + break; + case TYPEID_CORPSE: + Push(luastate, obj->ToCorpse()); + break; + default: + ALETemplate::Push(luastate, obj); + } +} +void ALE::Push(lua_State* luastate, ObjectGuid const guid) +{ + ALETemplate::Push(luastate, new unsigned long long(guid.GetRawValue())); +} + +void ALE::Push(lua_State* luastate, GemPropertiesEntry const& gemProperties) +{ + Push(luastate, &gemProperties); +} + +void ALE::Push(lua_State* luastate, SpellEntry const& spell) +{ + Push(luastate, &spell); +} + +void ALE::Push(lua_State* luastate, CreatureTemplate const* creatureTemplate) +{ + Push(luastate, creatureTemplate); +} + +std::string ALE::FormatQuery(lua_State* L, const char* query) +{ + int numArgs = lua_gettop(L); + std::string formattedQuery = query; + + size_t position = 0; + for (int i = 2; i <= numArgs; ++i) + { + std::string arg; + + if (lua_isnumber(L, i)) + { + arg = std::to_string(lua_tonumber(L, i)); + } + else if (lua_isstring(L, i)) + { + std::string value = lua_tostring(L, i); + for (size_t pos = 0; (pos = value.find('\'', pos)) != std::string::npos; pos += 2) + { + value.insert(pos, "'"); + } + arg = "'" + value + "'"; + } + else + { + luaL_error(L, "Unsupported argument type. Only numbers and strings are supported."); + return ""; + } + + position = formattedQuery.find("?", position); + if (position == std::string::npos) + { + luaL_error(L, "Mismatch between placeholders and arguments."); + return ""; + } + formattedQuery.replace(position, 1, arg); + position += arg.length(); + } + + return formattedQuery; +} + +static int CheckIntegerRange(lua_State* luastate, int narg, int min, int max) +{ + double value = luaL_checknumber(luastate, narg); + char error_buffer[64]; + + if (value > max) + { + snprintf(error_buffer, 64, "value must be less than or equal to %i", max); + return luaL_argerror(luastate, narg, error_buffer); + } + + if (value < min) + { + snprintf(error_buffer, 64, "value must be greater than or equal to %i", min); + return luaL_argerror(luastate, narg, error_buffer); + } + + return static_cast(value); +} + +static unsigned int CheckUnsignedRange(lua_State* luastate, int narg, unsigned int max) +{ + double value = luaL_checknumber(luastate, narg); + + if (value < 0) + return luaL_argerror(luastate, narg, "value must be greater than or equal to 0"); + + if (value > max) + { + char error_buffer[64]; + snprintf(error_buffer, 64, "value must be less than or equal to %u", max); + return luaL_argerror(luastate, narg, error_buffer); + } + + return static_cast(value); +} + +template<> bool ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return lua_toboolean(luastate, narg) != 0; +} +template<> float ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return static_cast(luaL_checknumber(luastate, narg)); +} +template<> double ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return luaL_checknumber(luastate, narg); +} +template<> signed char ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return CheckIntegerRange(luastate, narg, SCHAR_MIN, SCHAR_MAX); +} +template<> unsigned char ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return CheckUnsignedRange(luastate, narg, UCHAR_MAX); +} +template<> short ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return CheckIntegerRange(luastate, narg, SHRT_MIN, SHRT_MAX); +} +template<> unsigned short ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return CheckUnsignedRange(luastate, narg, USHRT_MAX); +} +template<> int ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return CheckIntegerRange(luastate, narg, INT_MIN, INT_MAX); +} +template<> unsigned int ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return CheckUnsignedRange(luastate, narg, UINT_MAX); +} +template<> const char* ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return luaL_checkstring(luastate, narg); +} +template<> std::string ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return luaL_checkstring(luastate, narg); +} +template<> long long ALE::CHECKVAL(lua_State* luastate, int narg) +{ + if (lua_isnumber(luastate, narg)) + return static_cast(CHECKVAL(luastate, narg)); + return *(ALE::CHECKOBJ(luastate, narg, true)); +} +template<> unsigned long long ALE::CHECKVAL(lua_State* luastate, int narg) +{ + if (lua_isnumber(luastate, narg)) + return static_cast(CHECKVAL(luastate, narg)); + return *(ALE::CHECKOBJ(luastate, narg, true)); +} +template<> long ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return static_cast(CHECKVAL(luastate, narg)); +} +template<> unsigned long ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return static_cast(CHECKVAL(luastate, narg)); +} +template<> ObjectGuid ALE::CHECKVAL(lua_State* luastate, int narg) +{ + return ObjectGuid(uint64((CHECKVAL(luastate, narg)))); +} + +template<> Object* ALE::CHECKOBJ(lua_State* luastate, int narg, bool error) +{ + Object* obj = CHECKOBJ(luastate, narg, false); + if (!obj) + obj = CHECKOBJ(luastate, narg, false); + if (!obj) + obj = ALETemplate::Check(luastate, narg, error); + return obj; +} +template<> WorldObject* ALE::CHECKOBJ(lua_State* luastate, int narg, bool error) +{ + WorldObject* obj = CHECKOBJ(luastate, narg, false); + if (!obj) + obj = CHECKOBJ(luastate, narg, false); + if (!obj) + obj = CHECKOBJ(luastate, narg, false); + if (!obj) + obj = ALETemplate::Check(luastate, narg, error); + return obj; +} +template<> Unit* ALE::CHECKOBJ(lua_State* luastate, int narg, bool error) +{ + Unit* obj = CHECKOBJ(luastate, narg, false); + if (!obj) + obj = CHECKOBJ(luastate, narg, false); + if (!obj) + obj = ALETemplate::Check(luastate, narg, error); + return obj; +} + +template<> ALEObject* ALE::CHECKOBJ(lua_State* luastate, int narg, bool error) +{ + return CHECKTYPE(luastate, narg, NULL, error); +} + +ALEObject* ALE::CHECKTYPE(lua_State* luastate, int narg, const char* tname, bool error) +{ + if (lua_islightuserdata(luastate, narg)) + { + if (error) + luaL_argerror(luastate, narg, "bad argument : userdata expected, got lightuserdata"); + return NULL; + } + + ALEObject** ptrHold = static_cast(lua_touserdata(luastate, narg)); + + if (!ptrHold || (tname && (*ptrHold)->GetTypeName() != tname)) + { + if (error) + { + char buff[256]; + snprintf(buff, 256, "bad argument : %s expected, got %s", tname ? tname : "ALEObject", ptrHold ? (*ptrHold)->GetTypeName() : luaL_typename(luastate, narg)); + luaL_argerror(luastate, narg, buff); + } + return NULL; + } + return *ptrHold; +} + +template +static int cancelBinding(lua_State *L) +{ + uint64 bindingID = ALE::CHECKVAL(L, lua_upvalueindex(1)); + + BindingMap* bindings = (BindingMap*)lua_touserdata(L, lua_upvalueindex(2)); + ASSERT(bindings != NULL); + + bindings->Remove(bindingID); + + return 0; +} + +template +static void createCancelCallback(lua_State* L, uint64 bindingID, BindingMap* bindings) +{ + ALE::Push(L, bindingID); + lua_pushlightuserdata(L, bindings); + // Stack: bindingID, bindings + + lua_pushcclosure(L, &cancelBinding, 2); + // Stack: cancel_callback +} + +// Saves the function reference ID given to the register type's store for given entry under the given event +int ALE::Register(lua_State* L, uint8 regtype, uint32 entry, ObjectGuid guid, uint32 instanceId, uint32 event_id, int functionRef, uint32 shots) +{ + uint64 bindingID; + + switch (regtype) + { + case Hooks::REGTYPE_SERVER: + if (event_id < Hooks::SERVER_EVENT_COUNT) + { + auto key = EventKey((Hooks::ServerEvents)event_id); + bindingID = ServerEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, ServerEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_PLAYER: + if (event_id < Hooks::PLAYER_EVENT_COUNT) + { + auto key = EventKey((Hooks::PlayerEvents)event_id); + bindingID = PlayerEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, PlayerEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_GUILD: + if (event_id < Hooks::GUILD_EVENT_COUNT) + { + auto key = EventKey((Hooks::GuildEvents)event_id); + bindingID = GuildEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, GuildEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_GROUP: + if (event_id < Hooks::GROUP_EVENT_COUNT) + { + auto key = EventKey((Hooks::GroupEvents)event_id); + bindingID = GroupEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, GroupEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_VEHICLE: + if (event_id < Hooks::VEHICLE_EVENT_COUNT) + { + auto key = EventKey((Hooks::VehicleEvents)event_id); + bindingID = VehicleEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, VehicleEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_BG: + if (event_id < Hooks::BG_EVENT_COUNT) + { + auto key = EventKey((Hooks::BGEvents)event_id); + bindingID = BGEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, BGEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_PACKET: + if (event_id < Hooks::PACKET_EVENT_COUNT) + { + if (entry >= NUM_MSG_TYPES) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a creature with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + + auto key = EntryKey((Hooks::PacketEvents)event_id, entry); + bindingID = PacketEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, PacketEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_CREATURE: + if (event_id < Hooks::CREATURE_EVENT_COUNT) + { + if (entry != 0) + { + if (!eObjectMgr->GetCreatureTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a creature with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + + auto key = EntryKey((Hooks::CreatureEvents)event_id, entry); + bindingID = CreatureEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, CreatureEventBindings); + } + else + { + if (guid.IsEmpty()) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "guid was 0!"); + return 0; // Stack: (empty) + } + + auto key = UniqueObjectKey((Hooks::CreatureEvents)event_id, guid, instanceId); + bindingID = CreatureUniqueBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, CreatureUniqueBindings); + } + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_CREATURE_GOSSIP: + if (event_id < Hooks::GOSSIP_EVENT_COUNT) + { + if (!eObjectMgr->GetCreatureTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a creature with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + + auto key = EntryKey((Hooks::GossipEvents)event_id, entry); + bindingID = CreatureGossipBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, CreatureGossipBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_GAMEOBJECT: + if (event_id < Hooks::GAMEOBJECT_EVENT_COUNT) + { + if (!eObjectMgr->GetGameObjectTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a gameobject with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + + auto key = EntryKey((Hooks::GameObjectEvents)event_id, entry); + bindingID = GameObjectEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, GameObjectEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_GAMEOBJECT_GOSSIP: + if (event_id < Hooks::GOSSIP_EVENT_COUNT) + { + if (!eObjectMgr->GetGameObjectTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a gameobject with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + + auto key = EntryKey((Hooks::GossipEvents)event_id, entry); + bindingID = GameObjectGossipBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, GameObjectGossipBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_ITEM: + if (event_id < Hooks::ITEM_EVENT_COUNT) + { + if (!eObjectMgr->GetItemTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a item with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + + auto key = EntryKey((Hooks::ItemEvents)event_id, entry); + bindingID = ItemEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, ItemEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_ITEM_GOSSIP: + if (event_id < Hooks::GOSSIP_EVENT_COUNT) + { + if (!eObjectMgr->GetItemTemplate(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a item with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + + auto key = EntryKey((Hooks::GossipEvents)event_id, entry); + bindingID = ItemGossipBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, ItemGossipBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_PLAYER_GOSSIP: + if (event_id < Hooks::GOSSIP_EVENT_COUNT) + { + auto key = EntryKey((Hooks::GossipEvents)event_id, entry); + bindingID = PlayerGossipBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, PlayerGossipBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_MAP: + if (event_id < Hooks::INSTANCE_EVENT_COUNT) + { + auto key = EntryKey((Hooks::InstanceEvents)event_id, entry); + bindingID = MapEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, MapEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_INSTANCE: + if (event_id < Hooks::INSTANCE_EVENT_COUNT) + { + auto key = EntryKey((Hooks::InstanceEvents)event_id, entry); + bindingID = InstanceEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, InstanceEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_TICKET: + if (event_id < Hooks::TICKET_EVENT_COUNT) + { + auto key = EventKey((Hooks::TicketEvents)event_id); + bindingID = TicketEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, TicketEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_SPELL: + if (event_id < Hooks::SPELL_EVENT_COUNT) + { + if (!sSpellMgr->GetSpellInfo(entry)) + { + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + luaL_error(L, "Couldn't find a spell with (ID: %d)!", entry); + return 0; // Stack: (empty) + } + + auto key = EntryKey((Hooks::SpellEvents)event_id, entry); + bindingID = SpellEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, SpellEventBindings); + return 1; // Stack: callback + } + break; + + case Hooks::REGTYPE_ALL_CREATURE: + if (event_id < Hooks::ALL_CREATURE_EVENT_COUNT) + { + auto key = EventKey((Hooks::AllCreatureEvents)event_id); + bindingID = AllCreatureEventBindings->Insert(key, functionRef, shots); + createCancelCallback(L, bindingID, AllCreatureEventBindings); + return 1; // Stack: callback + } + break; + } + luaL_unref(L, LUA_REGISTRYINDEX, functionRef); + std::ostringstream oss; + oss << "regtype " << static_cast(regtype) << ", event " << event_id << ", entry " << entry << ", guid " << guid.GetRawValue() << ", instance " << instanceId; + luaL_error(L, "Unknown event type (%s)", oss.str().c_str()); + return 0; +} + +/* + * Cleans up the stack, effectively undoing all Push calls and the Setup call. + */ +void ALE::CleanUpStack(int number_of_arguments) +{ + // Stack: event_id, [arguments] + + lua_pop(L, number_of_arguments + 1); // Add 1 because the caller doesn't know about `event_id`. + // Stack: (empty) + + if (event_level == 0) + InvalidateObjects(); +} + +/* + * Call a single event handler that was put on the stack with `Setup` and removes it from the stack. + * + * The caller is responsible for keeping track of how many times this should be called. + */ +int ALE::CallOneFunction(int number_of_functions, int number_of_arguments, int number_of_results) +{ + ++number_of_arguments; // Caller doesn't know about `event_id`. + ASSERT(number_of_functions > 0 && number_of_arguments > 0 && number_of_results >= 0); + // Stack: event_id, [arguments], [functions] + + int functions_top = lua_gettop(L); + int first_function_index = functions_top - number_of_functions + 1; + int arguments_top = first_function_index - 1; + int first_argument_index = arguments_top - number_of_arguments + 1; + + // Copy the arguments from the bottom of the stack to the top. + for (int argument_index = first_argument_index; argument_index <= arguments_top; ++argument_index) + { + lua_pushvalue(L, argument_index); + } + // Stack: event_id, [arguments], [functions], event_id, [arguments] + + ExecuteCall(number_of_arguments, number_of_results); + --functions_top; + // Stack: event_id, [arguments], [functions - 1], [results] + + return functions_top + 1; // Return the location of the first result (if any exist). +} + +CreatureAI* ALE::GetAI(Creature* creature) +{ + if (!ALEConfig::GetInstance().IsALEEnabled()) + return NULL; + + for (int i = 1; i < Hooks::CREATURE_EVENT_COUNT; ++i) + { + Hooks::CreatureEvents event_id = (Hooks::CreatureEvents)i; + + auto entryKey = EntryKey(event_id, creature->GetEntry()); + auto uniqueKey = UniqueObjectKey(event_id, creature->GET_GUID(), creature->GetInstanceId()); + + if (CreatureEventBindings->HasBindingsFor(entryKey) || + CreatureUniqueBindings->HasBindingsFor(uniqueKey)) + return new ALECreatureAI(creature); + } + + return NULL; +} + +InstanceData* ALE::GetInstanceData(Map* map) +{ + if (!ALEConfig::GetInstance().IsALEEnabled()) + return NULL; + + for (int i = 1; i < Hooks::INSTANCE_EVENT_COUNT; ++i) + { + Hooks::InstanceEvents event_id = (Hooks::InstanceEvents)i; + + auto key = EntryKey(event_id, map->GetId()); + + if (MapEventBindings->HasBindingsFor(key) || + InstanceEventBindings->HasBindingsFor(key)) + return new ALEInstanceAI(map); + } + + return NULL; +} + +bool ALE::HasInstanceData(Map const* map) +{ + if (!map->Instanceable()) + return continentDataRefs.find(map->GetId()) != continentDataRefs.end(); + else + return instanceDataRefs.find(map->GetInstanceId()) != instanceDataRefs.end(); +} + +void ALE::CreateInstanceData(Map const* map) +{ + ASSERT(lua_istable(L, -1)); + int ref = luaL_ref(L, LUA_REGISTRYINDEX); + + if (!map->Instanceable()) + { + uint32 mapId = map->GetId(); + + // If there's another table that was already stored for the map, unref it. + auto mapRef = continentDataRefs.find(mapId); + if (mapRef != continentDataRefs.end()) + { + luaL_unref(L, LUA_REGISTRYINDEX, mapRef->second); + } + + continentDataRefs[mapId] = ref; + } + else + { + uint32 instanceId = map->GetInstanceId(); + + // If there's another table that was already stored for the instance, unref it. + auto instRef = instanceDataRefs.find(instanceId); + if (instRef != instanceDataRefs.end()) + { + luaL_unref(L, LUA_REGISTRYINDEX, instRef->second); + } + + instanceDataRefs[instanceId] = ref; + } +} + +/* + * Unrefs the instanceId related events and data + * Does all required actions for when an instance is freed. + */ +void ALE::FreeInstanceId(uint32 instanceId) +{ + LOCK_ALE; + + if (!ALEConfig::GetInstance().IsALEEnabled()) + return; + + for (int i = 1; i < Hooks::INSTANCE_EVENT_COUNT; ++i) + { + auto key = EntryKey((Hooks::InstanceEvents)i, instanceId); + + if (MapEventBindings->HasBindingsFor(key)) + MapEventBindings->Clear(key); + + if (InstanceEventBindings->HasBindingsFor(key)) + InstanceEventBindings->Clear(key); + + if (instanceDataRefs.find(instanceId) != instanceDataRefs.end()) + { + luaL_unref(L, LUA_REGISTRYINDEX, instanceDataRefs[instanceId]); + instanceDataRefs.erase(instanceId); + } + } +} + +void ALE::PushInstanceData(lua_State* L, ALEInstanceAI* ai, bool incrementCounter) +{ + // Check if the instance data is missing (i.e. someone reloaded ALE). + if (!HasInstanceData(ai->instance)) + ai->Reload(); + + // Get the instance data table from the registry. + if (!ai->instance->Instanceable()) + lua_rawgeti(L, LUA_REGISTRYINDEX, continentDataRefs[ai->instance->GetId()]); + else + lua_rawgeti(L, LUA_REGISTRYINDEX, instanceDataRefs[ai->instance->GetInstanceId()]); + + ASSERT(lua_istable(L, -1)); + + if (incrementCounter) + ++push_counter; +} diff --git a/modules/mod-ale/src/LuaEngine/LuaEngine.h b/modules/mod-ale/src/LuaEngine/LuaEngine.h new file mode 100644 index 0000000..a21640d --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/LuaEngine.h @@ -0,0 +1,627 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +#ifndef _LUA_ENGINE_H +#define _LUA_ENGINE_H + +#include "Common.h" +#include "SharedDefines.h" +#include "DBCEnums.h" + +#include "Group.h" +#include "Item.h" +#include "Chat.h" +#include "Player.h" +#include "Weather.h" +#include "World.h" +#include "Hooks.h" +#include "LFG.h" +#include "ALEUtility.h" +#include "HttpManager.h" +#include "EventEmitter.h" +#include "TicketMgr.h" +#include "LootMgr.h" +#include "ALEFileWatcher.h" +#include "ALEConfig.h" +#include +#include +#include +#include +#include + +extern "C" +{ +#include +}; + +struct ItemTemplate; +typedef BattlegroundTypeId BattleGroundTypeId; +typedef AreaTrigger AreaTriggerEntry; +class AuctionHouseObject; +struct AuctionEntry; +class Battleground; +typedef Battleground BattleGround; +class Channel; +class Corpse; +class Creature; +class CreatureAI; +class GameObject; +class GameObjectAI; +class Guild; +class Group; +class InstanceScript; +typedef InstanceScript InstanceData; +class ALEInstanceAI; +class Item; +class Pet; +class Player; +class Quest; +class Spell; +class SpellCastTargets; +class TempSummon; +// class Transport; +class Unit; +class Weather; +class WorldPacket; +class Vehicle; + +struct lua_State; +class EventMgr; +class ALEObject; +template class ALETemplate; + +template class BindingMap; +template struct EventKey; +template struct EntryKey; +template struct UniqueObjectKey; + +// Type definition for bytecode buffer +typedef std::vector BytecodeBuffer; + +// Global bytecode cache entry +struct GlobalCacheEntry +{ + BytecodeBuffer bytecode; + std::time_t last_modified; + std::string filepath; + + GlobalCacheEntry() : last_modified(0) {} + GlobalCacheEntry(const BytecodeBuffer& code, std::time_t modTime, const std::string& path) + : bytecode(code), last_modified(modTime), filepath(path) {} +}; + +struct LuaScript +{ + std::string fileext; + std::string filename; + std::string filepath; + std::string modulepath; + LuaScript() {} +}; + +#define ALE_STATE_PTR "ALE State Ptr" +#define LOCK_ALE ALE::Guard __guard(ALE::GetLock()) + +#define ALE_GAME_API AC_GAME_API + +class ALE_GAME_API ALE +{ +public: + typedef std::list ScriptList; + + typedef std::recursive_mutex LockType; + typedef std::lock_guard Guard; + + const std::string& GetRequirePath() const { return lua_requirepath; } + const std::string& GetRequireCPath() const { return lua_requirecpath; } + +private: + static bool reload; + static bool initialized; + static LockType lock; + static std::unique_ptr fileWatcher; + + // Lua script locations + static ScriptList lua_scripts; + static ScriptList lua_extensions; + + // Lua script folder path + static std::string lua_folderpath; + // lua path variable for require() function + static std::string lua_requirepath; + static std::string lua_requirecpath; + + // A counter for lua event stacks that occur (see event_level). + // This is used to determine whether an object belongs to the current call stack or not. + // 0 is reserved for always belonging to the call stack + // 1 is reserved for a non valid callstackid + uint64 callstackid = 2; + // A counter for the amount of nested events. When the event_level + // reaches 0 we are about to return back to C++. At this point the + // objects used during the event stack are invalidated. + uint32 event_level; + // When a hook pushes arguments to be passed to event handlers, + // this is used to keep track of how many arguments were pushed. + uint8 push_counter; + + // Map from instance ID -> Lua table ref + std::unordered_map instanceDataRefs; + // Map from map ID -> Lua table ref + std::unordered_map continentDataRefs; + + ALE(); + ~ALE(); + + // Prevent copy + ALE(ALE const&) = delete; + ALE& operator=(const ALE&) = delete; + + void OpenLua(); + void CloseLua(); + void DestroyBindStores(); + void CreateBindStores(); + void InvalidateObjects(); + + // Use ReloadALE() to make ALE reload + // This is called on world update to reload ALE + static void _ReloadALE(); + static void LoadScriptPaths(); + static void GetScripts(std::string path); + static void AddScriptPath(std::string filename, const std::string& fullpath); + static int LoadCompiledScript(lua_State* L, const std::string& filepath); + static std::time_t GetFileModTime(const std::string& filepath); + static std::time_t GetFileModTimeWithCache(const std::string& filepath); + + // Global cache management + static bool CompileScriptToGlobalCache(const std::string& filepath); + static bool CompileMoonScriptToGlobalCache(const std::string& filepath); + static int TryLoadFromGlobalCache(lua_State* L, const std::string& filepath); + static int LoadScriptWithCache(lua_State* L, const std::string& filepath, bool isMoonScript, uint32* compiledCount = nullptr, uint32* cachedCount = nullptr); + static void ClearGlobalCache(); + static void ClearTimestampCache(); + static size_t GetGlobalCacheSize(); + + static int StackTrace(lua_State *_L); + static void Report(lua_State* _L); + + // Some helpers for hooks to call event handlers. + // The bodies of the templates are in HookHelpers.h, so if you want to use them you need to #include "HookHelpers.h". + template int SetupStack(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, int number_of_arguments); + int CallOneFunction(int number_of_functions, int number_of_arguments, int number_of_results); + void CleanUpStack(int number_of_arguments); + template void ReplaceArgument(T value, uint8 index); + template void CallAllFunctions(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2); + template bool CallAllFunctionsBool(BindingMap* bindings1, BindingMap* bindings2, const K1& key1, const K2& key2, bool default_value = false); + + // Same as above but for only one binding instead of two. + // `key` is passed twice because there's no NULL for references, but it's not actually used if `bindings2` is NULL. + template int SetupStack(BindingMap* bindings, const K& key, int number_of_arguments) + { + return SetupStack(bindings, NULL, key, key, number_of_arguments); + } + template void CallAllFunctions(BindingMap* bindings, const K& key) + { + CallAllFunctions(bindings, NULL, key, key); + } + template bool CallAllFunctionsBool(BindingMap* bindings, const K& key, bool default_value = false) + { + return CallAllFunctionsBool(bindings, NULL, key, key, default_value); + } + + // Non-static pushes, to be used in hooks. + // These just call the correct static version with the main thread's Lua state. + void Push() { Push(L); ++push_counter; } + void Push(const long long value) { Push(L, value); ++push_counter; } + void Push(const unsigned long long value) { Push(L, value); ++push_counter; } + void Push(const long value) { Push(L, value); ++push_counter; } + void Push(const unsigned long value) { Push(L, value); ++push_counter; } + void Push(const int value) { Push(L, value); ++push_counter; } + void Push(const unsigned int value) { Push(L, value); ++push_counter; } + void Push(const bool value) { Push(L, value); ++push_counter; } + void Push(const float value) { Push(L, value); ++push_counter; } + void Push(const double value) { Push(L, value); ++push_counter; } + void Push(const std::string& value) { Push(L, value); ++push_counter; } + void Push(const char* value) { Push(L, value); ++push_counter; } + void Push(ObjectGuid const value) { Push(L, value); ++push_counter; } + void Push(const CreatureTemplate* value) { Push(L, value); ++push_counter; } + template + void Push(T const* ptr) { Push(L, ptr); ++push_counter; } + +public: + static ALE* GALE; + + lua_State* L; + EventMgr* eventMgr; + HttpManager httpManager; + QueryCallbackProcessor queryProcessor; + EventEmitter OnError; + + BindingMap< EventKey >* ServerEventBindings; + BindingMap< EventKey >* PlayerEventBindings; + BindingMap< EventKey >* GuildEventBindings; + BindingMap< EventKey >* GroupEventBindings; + BindingMap< EventKey >* VehicleEventBindings; + BindingMap< EventKey >* BGEventBindings; + BindingMap< EventKey >* AllCreatureEventBindings; + + BindingMap< EntryKey >* PacketEventBindings; + BindingMap< EntryKey >* CreatureEventBindings; + BindingMap< EntryKey >* CreatureGossipBindings; + BindingMap< EntryKey >* GameObjectEventBindings; + BindingMap< EntryKey >* GameObjectGossipBindings; + BindingMap< EntryKey >* ItemEventBindings; + BindingMap< EntryKey >* ItemGossipBindings; + BindingMap< EntryKey >* PlayerGossipBindings; + BindingMap< EntryKey >* MapEventBindings; + BindingMap< EntryKey >* InstanceEventBindings; + BindingMap< EventKey >* TicketEventBindings; + BindingMap< EntryKey >* SpellEventBindings; + + BindingMap< UniqueObjectKey >* CreatureUniqueBindings; + + static void Initialize(); + static void Uninitialize(); + // This function is used to make ALE reload + static void ReloadALE() { LOCK_ALE; reload = true; } + static LockType& GetLock() { return lock; }; + static bool IsInitialized() { return initialized; } + // Never returns nullptr + static ALE* GetALE(lua_State* L) + { + lua_pushstring(L, ALE_STATE_PTR); + lua_rawget(L, LUA_REGISTRYINDEX); + ASSERT(lua_islightuserdata(L, -1)); + ALE* E = static_cast(lua_touserdata(L, -1)); + lua_pop(L, 1); + ASSERT(E); + return E; + } + + // Static pushes, can be used by anything, including methods. + static void Push(lua_State* luastate); // nil + static void Push(lua_State* luastate, const long long); + static void Push(lua_State* luastate, const unsigned long long); + static void Push(lua_State* luastate, const long); + static void Push(lua_State* luastate, const unsigned long); + static void Push(lua_State* luastate, const int); + static void Push(lua_State* luastate, const unsigned int); + static void Push(lua_State* luastate, const bool); + static void Push(lua_State* luastate, const float); + static void Push(lua_State* luastate, const double); + static void Push(lua_State* luastate, const std::string&); + static void Push(lua_State* luastate, const char*); + static void Push(lua_State* luastate, Object const* obj); + static void Push(lua_State* luastate, WorldObject const* obj); + static void Push(lua_State* luastate, Unit const* unit); + static void Push(lua_State* luastate, Pet const* pet); + static void Push(lua_State* luastate, TempSummon const* summon); + static void Push(lua_State* luastate, ObjectGuid const guid); + static void Push(lua_State* luastate, GemPropertiesEntry const& gemProperties); + static void Push(lua_State* luastate, SpellEntry const& spell); + static void Push(lua_State* luastate, CreatureTemplate const* creatureTemplate); + template + static void Push(lua_State* luastate, T const* ptr) + { + ALETemplate::Push(luastate, ptr); + } + + static std::string FormatQuery(lua_State* L, const char* query); + + bool ExecuteCall(int params, int res); + + /* + * Returns `true` if ALE has instance data for `map`. + */ + bool HasInstanceData(Map const* map); + + /* + * Use the top element of the stack as the instance data table for `map`, + * then pops it off the stack. + */ + void CreateInstanceData(Map const* map); + + /* + * Retrieve the instance data for the `Map` scripted by `ai` and push it + * onto the stack. + * + * An `ALEInstanceAI` is needed because the instance data might + * not exist (i.e. ALE has been reloaded). + * + * In that case, the AI is "reloaded" (new instance data table is created + * and loaded with the last known save state, and `Load`/`Initialize` + * hooks are called). + */ + void PushInstanceData(lua_State* L, ALEInstanceAI* ai, bool incrementCounter = true); + + void RunScripts(); + bool ShouldReload() const { return reload; } + bool HasLuaState() const { return L != NULL; } + uint64 GetCallstackId() const { return callstackid; } + int Register(lua_State* L, uint8 reg, uint32 entry, ObjectGuid guid, uint32 instanceId, uint32 event_id, int functionRef, uint32 shots); + + // Checks + template static T CHECKVAL(lua_State* luastate, int narg); + template static T CHECKVAL(lua_State* luastate, int narg, T def) + { + return lua_isnoneornil(luastate, narg) ? def : CHECKVAL(luastate, narg); + } + template static T* CHECKOBJ(lua_State* luastate, int narg, bool error = true) + { + return ALETemplate::Check(luastate, narg, error); + } + static ALEObject* CHECKTYPE(lua_State* luastate, int narg, const char *tname, bool error = true); + + CreatureAI* GetAI(Creature* creature); + InstanceData* GetInstanceData(Map* map); + void FreeInstanceId(uint32 instanceId); + + /* Custom */ + void OnTimedEvent(int funcRef, uint32 delay, uint32 calls, WorldObject* obj); + bool OnCommand(ChatHandler& handler, const char* text); + void OnWorldUpdate(uint32 diff); + void OnLootItem(Player* pPlayer, Item* pItem, uint32 count, ObjectGuid guid); + void OnLootMoney(Player* pPlayer, uint32 amount); + void OnFirstLogin(Player* pPlayer); + void OnEquip(Player* pPlayer, Item* pItem, uint8 bag, uint8 slot); + void OnRepop(Player* pPlayer); + void OnResurrect(Player* pPlayer); + void OnQuestAbandon(Player* pPlayer, uint32 questId); + void OnLearnTalents(Player* pPlayer, uint32 talentId, uint32 talentRank, uint32 spellid); + InventoryResult OnCanUseItem(const Player* pPlayer, uint32 itemEntry); + void OnLuaStateClose(); + void OnLuaStateOpen(); + bool OnAddonMessage(Player* sender, uint32 type, std::string& msg, Player* receiver, Guild* guild, Group* group, Channel* channel); + void OnPetAddedToWorld(Player* player, Creature* pet); + void OnQuestRewardItem(Player* player, Item* item, uint32 count); + void OnCreateItem(Player* player, Item* item, uint32 count); + void OnStoreNewItem(Player* player, Item* item, uint32 count); + void OnPlayerCompleteQuest(Player* player, Quest const* quest); + + /* Item */ + void OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, Item* pTarget); + bool OnQuestAccept(Player* pPlayer, Item* pItem, Quest const* pQuest); + bool OnUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets); + bool OnItemUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets); + bool OnItemGossip(Player* pPlayer, Item* pItem, SpellCastTargets const& targets); + bool OnExpire(Player* pPlayer, ItemTemplate const* pProto); + bool OnRemove(Player* pPlayer, Item* item); + void HandleGossipSelectOption(Player* pPlayer, Item* item, uint32 sender, uint32 action, const std::string& code); + + /* Creature */ + void OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, Creature* pTarget); + bool OnGossipHello(Player* pPlayer, Creature* pCreature); + bool OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action); + bool OnGossipSelectCode(Player* pPlayer, Creature* pCreature, uint32 sender, uint32 action, const char* code); + bool OnQuestAccept(Player* pPlayer, Creature* pCreature, Quest const* pQuest); + bool OnQuestReward(Player* pPlayer, Creature* pCreature, Quest const* pQuest, uint32 opt); + void GetDialogStatus(const Player* pPlayer, const Creature* pCreature); + + bool OnSummoned(Creature* creature, Unit* summoner); + bool UpdateAI(Creature* me, const uint32 diff); + bool EnterCombat(Creature* me, Unit* target); + bool DamageTaken(Creature* me, Unit* attacker, uint32& damage); + bool JustDied(Creature* me, Unit* killer); + bool KilledUnit(Creature* me, Unit* victim); + bool JustSummoned(Creature* me, Creature* summon); + bool SummonedCreatureDespawn(Creature* me, Creature* summon); + bool MovementInform(Creature* me, uint32 type, uint32 id); + bool AttackStart(Creature* me, Unit* target); + bool EnterEvadeMode(Creature* me); + bool JustRespawned(Creature* me); + bool JustReachedHome(Creature* me); + bool ReceiveEmote(Creature* me, Player* player, uint32 emoteId); + bool CorpseRemoved(Creature* me, uint32& respawnDelay); + bool MoveInLineOfSight(Creature* me, Unit* who); + bool SpellHit(Creature* me, WorldObject* caster, SpellInfo const* spell); + bool SpellHitTarget(Creature* me, WorldObject* target, SpellInfo const* spell); + bool SummonedCreatureDies(Creature* me, Creature* summon, Unit* killer); + bool OwnerAttackedBy(Creature* me, Unit* attacker); + bool OwnerAttacked(Creature* me, Unit* target); + void On_Reset(Creature* me); + void OnCreatureAuraApply(Creature* me, Aura* aura); + void OnCreatureHeal(Creature* me, Unit* target, uint32& gain); + void OnCreatureDamage(Creature* me, Unit* target, uint32& gain); + void OnCreatureAuraRemove(Creature* me, Aura* aura, AuraRemoveMode mode); + void OnCreatureModifyPeriodicDamageAurasTick(Creature* me, Unit* target, uint32& damage, SpellInfo const* spellInfo); + void OnCreatureModifyMeleeDamage(Creature* me, Unit* target, uint32& damage); + void OnCreatureModifySpellDamageTaken(Creature* me, Unit* target, int32& damage, SpellInfo const* spellInfo); + void OnCreatureModifyHealReceived(Creature* me, Unit* target, uint32& heal, SpellInfo const* spellInfo); + uint32 OnCreatureDealDamage(Creature* me, Unit* pVictim, uint32 damage, DamageEffectType damagetype); + + /* GameObject */ + void OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, GameObject* pTarget); + bool OnGameObjectUse(Player* pPlayer, GameObject* pGameObject); + bool OnGossipHello(Player* pPlayer, GameObject* pGameObject); + bool OnGossipSelect(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action); + bool OnGossipSelectCode(Player* pPlayer, GameObject* pGameObject, uint32 sender, uint32 action, const char* code); + bool OnQuestAccept(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest); + bool OnQuestReward(Player* pPlayer, GameObject* pGameObject, Quest const* pQuest, uint32 opt); + void GetDialogStatus(const Player* pPlayer, const GameObject* pGameObject); + void OnDestroyed(GameObject* pGameObject, WorldObject* attacker); + void OnDamaged(GameObject* pGameObject, WorldObject* attacker); + void OnLootStateChanged(GameObject* pGameObject, uint32 state); + void OnGameObjectStateChanged(GameObject* pGameObject, uint32 state); + void UpdateAI(GameObject* pGameObject, uint32 diff); + void OnSpawn(GameObject* gameobject); + + /* Packet */ + bool OnPacketSend(WorldSession* session, const WorldPacket& packet); + void OnPacketSendAny(Player* player, const WorldPacket& packet, bool& result); + void OnPacketSendOne(Player* player, const WorldPacket& packet, bool& result); + bool OnPacketReceive(WorldSession* session, WorldPacket const& packet); + void OnPacketReceiveAny(Player* player, WorldPacket const& packet, bool& result); + void OnPacketReceiveOne(Player* player, WorldPacket const& packet, bool& result); + + /* Player */ + void OnPlayerEnterCombat(Player* pPlayer, Unit* pEnemy); + void OnPlayerLeaveCombat(Player* pPlayer); + void OnPVPKill(Player* pKiller, Player* pKilled); + void OnCreatureKill(Player* pKiller, Creature* pKilled); + void OnPlayerKilledByCreature(Creature* pKiller, Player* pKilled); + void OnLevelChanged(Player* pPlayer, uint8 oldLevel); + void OnFreeTalentPointsChanged(Player* pPlayer, uint32 newPoints); + void OnTalentsReset(Player* pPlayer, bool noCost); + void OnMoneyChanged(Player* pPlayer, int32& amount); + void OnGiveXP(Player* pPlayer, uint32& amount, Unit* pVictim, uint8 xpSource); + bool OnReputationChange(Player* pPlayer, uint32 factionID, int32& standing, bool incremental); + void OnDuelRequest(Player* pTarget, Player* pChallenger); + void OnDuelStart(Player* pStarter, Player* pChallenger); + void OnDuelEnd(Player* pWinner, Player* pLoser, DuelCompleteType type); + bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg); + bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Group* pGroup); + bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Guild* pGuild); + bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Channel* pChannel); + bool OnChat(Player* pPlayer, uint32 type, uint32 lang, std::string& msg, Player* pReceiver); + void OnEmote(Player* pPlayer, uint32 emote); + void OnTextEmote(Player* pPlayer, uint32 textEmote, uint32 emoteNum, ObjectGuid guid); + void OnPlayerSpellCast(Player* pPlayer, Spell* pSpell, bool skipCheck); + void OnLogin(Player* pPlayer); + void OnLogout(Player* pPlayer); + void OnCreate(Player* pPlayer); + void OnDelete(uint32 guid); + void OnSave(Player* pPlayer); + void OnBindToInstance(Player* pPlayer, Difficulty difficulty, uint32 mapid, bool permanent); + void OnUpdateArea(Player* pPlayer, uint32 oldArea, uint32 newArea); + void OnUpdateZone(Player* pPlayer, uint32 newZone, uint32 newArea); + void OnMapChanged(Player* pPlayer); + void HandleGossipSelectOption(Player* pPlayer, uint32 menuId, uint32 sender, uint32 action, const std::string& code); + void OnLearnSpell(Player* player, uint32 spellId); + void OnAchiComplete(Player* player, AchievementEntry const* achievement); + void OnFfaPvpStateUpdate(Player* player, bool hasFfaPvp); + bool OnCanInitTrade(Player* player, Player* target); + bool OnCanSendMail(Player* player, ObjectGuid receiverGuid, ObjectGuid mailbox, std::string& subject, std::string& body, uint32 money, uint32 cod, Item* item); + bool OnCanJoinLfg(Player* player, uint8 roles, lfg::LfgDungeonSet& dungeons, const std::string& comment); + bool OnCanGroupInvite(Player* player, std::string& memberName); + void OnGroupRollRewardItem(Player* player, Item* item, uint32 count, RollVote voteType, Roll* roll); + void OnBattlegroundDesertion(Player* player, const BattlegroundDesertionType type); + void OnCreatureKilledByPet(Player* player, Creature* killed); + bool OnPlayerCanUpdateSkill(Player* player, uint32 skill_id); + void OnPlayerBeforeUpdateSkill(Player* player, uint32 skill_id, uint32& value, uint32 max, uint32 step); + void OnPlayerUpdateSkill(Player* player, uint32 skill_id, uint32 value, uint32 max, uint32 step, uint32 new_value); + bool CanPlayerResurrect(Player* player); + void OnPlayerQuestAccept(Player* player, Quest const* quest); + void OnPlayerAuraApply(Player* player, Aura* aura); + void OnPlayerAuraRemove(Player* player, Aura* aura, AuraRemoveMode mode); + void OnPlayerHeal(Player* player, Unit* target, uint32& gain); + void OnPlayerDamage(Player* player, Unit* target, uint32& damage); + void OnPlayerModifyPeriodicDamageAurasTick(Player* player, Unit* target, uint32& damage, SpellInfo const* spellInfo); + void OnPlayerModifyMeleeDamage(Player* player, Unit* target, uint32& damage); + void OnPlayerModifySpellDamageTaken(Player* player, Unit* target, int32& damage, SpellInfo const* spellInfo); + void OnPlayerModifyHealReceived(Player* player, Unit* target, uint32& heal, SpellInfo const* spellInfo); + uint32 OnPlayerDealDamage(Player* player, Unit* pVictim, uint32 damage, DamageEffectType damagetype); + void OnPlayerReleasedGhost(Player* player); + + /* Vehicle */ + void OnInstall(Vehicle* vehicle); + void OnUninstall(Vehicle* vehicle); + void OnInstallAccessory(Vehicle* vehicle, Creature* accessory); + void OnAddPassenger(Vehicle* vehicle, Unit* passenger, int8 seatId); + void OnRemovePassenger(Vehicle* vehicle, Unit* passenger); + + /* AreaTrigger */ + bool OnAreaTrigger(Player* pPlayer, AreaTriggerEntry const* pTrigger); + + /* Weather */ + void OnChange(Weather* weather, uint32 zone, WeatherState state, float grade); + + /* Auction House */ + void OnAdd(AuctionHouseObject* ah, AuctionEntry* entry); + void OnRemove(AuctionHouseObject* ah, AuctionEntry* entry); + void OnSuccessful(AuctionHouseObject* ah, AuctionEntry* entry); + void OnExpire(AuctionHouseObject* ah, AuctionEntry* entry); + + /* Guild */ + void OnAddMember(Guild* guild, Player* player, uint32 plRank); + void OnRemoveMember(Guild* guild, Player* player, bool isDisbanding); + void OnMOTDChanged(Guild* guild, const std::string& newMotd); + void OnInfoChanged(Guild* guild, const std::string& newInfo); + void OnCreate(Guild* guild, Player* leader, const std::string& name); + void OnDisband(Guild* guild); + void OnMemberWitdrawMoney(Guild* guild, Player* player, uint32& amount, bool isRepair); + void OnMemberDepositMoney(Guild* guild, Player* player, uint32& amount); + void OnItemMove(Guild* guild, Player* player, Item* pItem, bool isSrcBank, uint8 srcContainer, uint8 srcSlotId, bool isDestBank, uint8 destContainer, uint8 destSlotId); + void OnEvent(Guild* guild, uint8 eventType, uint32 playerGuid1, uint32 playerGuid2, uint8 newRank); + void OnBankEvent(Guild* guild, uint8 eventType, uint8 tabId, uint32 playerGuid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId); + + /* Group */ + void OnAddMember(Group* group, ObjectGuid guid); + void OnInviteMember(Group* group, ObjectGuid guid); + void OnRemoveMember(Group* group, ObjectGuid guid, uint8 method); + void OnChangeLeader(Group* group, ObjectGuid newLeaderGuid, ObjectGuid oldLeaderGuid); + void OnDisband(Group* group); + void OnCreate(Group* group, ObjectGuid leaderGuid, GroupType groupType); + + /* Map */ + void OnCreate(Map* map); + void OnDestroy(Map* map); + void OnPlayerEnter(Map* map, Player* player); + void OnPlayerLeave(Map* map, Player* player); + void OnUpdate(Map* map, uint32 diff); + void OnAddToWorld(Creature* creature); + void OnRemoveFromWorld(Creature* creature); + void OnAddToWorld(GameObject* gameobject); + void OnRemoveFromWorld(GameObject* gameobject); + void OnRemove(Creature* creature); + void OnRemove(GameObject* gameobject); + + /* Instance */ + void OnInitialize(ALEInstanceAI* ai); + void OnLoad(ALEInstanceAI* ai); + void OnUpdateInstance(ALEInstanceAI* ai, uint32 diff); + void OnPlayerEnterInstance(ALEInstanceAI* ai, Player* player); + void OnCreatureCreate(ALEInstanceAI* ai, Creature* creature); + void OnGameObjectCreate(ALEInstanceAI* ai, GameObject* gameobject); + bool OnCheckEncounterInProgress(ALEInstanceAI* ai); + + /* World */ + void OnOpenStateChange(bool open); + void OnConfigLoad(bool reload, bool isBefore); + void OnShutdownInitiate(ShutdownExitCode code, ShutdownMask mask); + void OnShutdownCancel(); + void OnStartup(); + void OnShutdown(); + void OnGameEventStart(uint32 eventid); + void OnGameEventStop(uint32 eventid); + + /* Battle Ground */ + void OnBGStart(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId); + void OnBGEnd(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId, TeamId winner); + void OnBGCreate(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId); + void OnBGDestroy(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId); + + /* Ticket */ + void OnTicketCreate(GmTicket* ticket); + void OnTicketClose(GmTicket* ticket); + void OnTicketUpdateLastChange(GmTicket* ticket); + void OnTicketResolve(GmTicket* ticket); + + /* Spell */ + void OnSpellPrepare(Unit* caster, Spell* spell, SpellInfo const* spellInfo); + void OnSpellCast(Unit* caster, Spell* spell, SpellInfo const* spellInfo, bool skipCheck); + void OnSpellCastCancel(Unit* caster, Spell* spell, SpellInfo const* spellInfo, bool bySelf); + + /* AllCreature */ + void OnAllCreatureAddToWorld(Creature* creature); + void OnAllCreatureRemoveFromWorld(Creature* creature); + void OnAllCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature); + void OnAllCreatureBeforeSelectLevel(const CreatureTemplate* cinfo, Creature* creature, uint8& level); + void OnAllCreatureAuraApply(Creature* me, Aura* aura); + void OnAllCreatureHeal(Creature* me, Unit* target, uint32& gain); + void OnAllCreatureDamage(Creature* me, Unit* target, uint32& gain); + void OnAllCreatureAuraRemove(Creature* me, Aura* aura, AuraRemoveMode mode); + void OnAllCreatureModifyPeriodicDamageAurasTick(Creature* me, Unit* target, uint32& damage, SpellInfo const* spellInfo); + void OnAllCreatureModifyMeleeDamage(Creature* me, Unit* target, uint32& damage); + void OnAllCreatureModifySpellDamageTaken(Creature* me, Unit* target, int32& damage, SpellInfo const* spellInfo); + void OnAllCreatureModifyHealReceived(Creature* me, Unit* target, uint32& heal, SpellInfo const* spellInfo); + uint32 OnAllCreatureDealDamage(Creature* me, Unit* pVictim, uint32 damage, DamageEffectType damagetype); +}; +template<> Unit* ALE::CHECKOBJ(lua_State* L, int narg, bool error); +template<> Object* ALE::CHECKOBJ(lua_State* L, int narg, bool error); +template<> WorldObject* ALE::CHECKOBJ(lua_State* L, int narg, bool error); +template<> ALEObject* ALE::CHECKOBJ(lua_State* L, int narg, bool error); + +#define sALE ALE::GALE +#endif diff --git a/modules/mod-ale/src/LuaEngine/LuaFunctions.cpp b/modules/mod-ale/src/LuaEngine/LuaFunctions.cpp new file mode 100644 index 0000000..c0acc01 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/LuaFunctions.cpp @@ -0,0 +1,1997 @@ +/* +* Copyright (C) 2010 - 2025 Eluna Lua Engine +* This program is free software licensed under GPL version 3 +* Please see the included DOCS/LICENSE.md for more information +*/ + +extern "C" +{ +#include "lua.h" +}; + +// ALE +#include "LuaEngine.h" +#include "ALEEventMgr.h" +#include "ALEIncludes.h" +#include "ALETemplate.h" +#include "ALEUtility.h" + +// Method includes +#include "GlobalMethods.h" +#include "ObjectMethods.h" +#include "WorldObjectMethods.h" +#include "UnitMethods.h" +#include "PlayerMethods.h" +#include "CreatureMethods.h" +#include "GroupMethods.h" +#include "GuildMethods.h" +#include "GameObjectMethods.h" +#include "ALEQueryMethods.h" +#include "AuraMethods.h" +#include "ItemMethods.h" +#include "WorldPacketMethods.h" +#include "SpellMethods.h" +#include "QuestMethods.h" +#include "MapMethods.h" +#include "CorpseMethods.h" +#include "VehicleMethods.h" +#include "BattleGroundMethods.h" +#include "ChatHandlerMethods.h" +#include "AchievementMethods.h" +#include "ItemTemplateMethods.h" +#include "RollMethods.h" +#include "TicketMethods.h" +#include "SpellInfoMethods.h" +#include "PetMethods.h" +#include "LootMethods.h" +#include "TransportMethods.h" + +// DBCStores includes +#include "GemPropertiesEntryMethods.h" +#include "SpellEntryMethods.h" + +luaL_Reg GlobalMethods[] = +{ + // Hooks + { "RegisterPacketEvent", &LuaGlobalFunctions::RegisterPacketEvent }, + { "RegisterServerEvent", &LuaGlobalFunctions::RegisterServerEvent }, + { "RegisterPlayerEvent", &LuaGlobalFunctions::RegisterPlayerEvent }, + { "RegisterGuildEvent", &LuaGlobalFunctions::RegisterGuildEvent }, + { "RegisterGroupEvent", &LuaGlobalFunctions::RegisterGroupEvent }, + { "RegisterCreatureEvent", &LuaGlobalFunctions::RegisterCreatureEvent }, + { "RegisterUniqueCreatureEvent", &LuaGlobalFunctions::RegisterUniqueCreatureEvent }, + { "RegisterCreatureGossipEvent", &LuaGlobalFunctions::RegisterCreatureGossipEvent }, + { "RegisterGameObjectEvent", &LuaGlobalFunctions::RegisterGameObjectEvent }, + { "RegisterGameObjectGossipEvent", &LuaGlobalFunctions::RegisterGameObjectGossipEvent }, + { "RegisterItemEvent", &LuaGlobalFunctions::RegisterItemEvent }, + { "RegisterItemGossipEvent", &LuaGlobalFunctions::RegisterItemGossipEvent }, + { "RegisterPlayerGossipEvent", &LuaGlobalFunctions::RegisterPlayerGossipEvent }, + { "RegisterBGEvent", &LuaGlobalFunctions::RegisterBGEvent }, + { "RegisterMapEvent", &LuaGlobalFunctions::RegisterMapEvent }, + { "RegisterInstanceEvent", &LuaGlobalFunctions::RegisterInstanceEvent }, + { "RegisterTicketEvent", &LuaGlobalFunctions::RegisterTicketEvent }, + { "RegisterSpellEvent", &LuaGlobalFunctions::RegisterSpellEvent }, + { "RegisterAllCreatureEvent", &LuaGlobalFunctions::RegisterAllCreatureEvent }, + + + { "ClearBattleGroundEvents", &LuaGlobalFunctions::ClearBattleGroundEvents }, + { "ClearCreatureEvents", &LuaGlobalFunctions::ClearCreatureEvents }, + { "ClearUniqueCreatureEvents", &LuaGlobalFunctions::ClearUniqueCreatureEvents }, + { "ClearCreatureGossipEvents", &LuaGlobalFunctions::ClearCreatureGossipEvents }, + { "ClearGameObjectEvents", &LuaGlobalFunctions::ClearGameObjectEvents }, + { "ClearGameObjectGossipEvents", &LuaGlobalFunctions::ClearGameObjectGossipEvents }, + { "ClearGroupEvents", &LuaGlobalFunctions::ClearGroupEvents }, + { "ClearGuildEvents", &LuaGlobalFunctions::ClearGuildEvents }, + { "ClearItemEvents", &LuaGlobalFunctions::ClearItemEvents }, + { "ClearItemGossipEvents", &LuaGlobalFunctions::ClearItemGossipEvents }, + { "ClearPacketEvents", &LuaGlobalFunctions::ClearPacketEvents }, + { "ClearPlayerEvents", &LuaGlobalFunctions::ClearPlayerEvents }, + { "ClearPlayerGossipEvents", &LuaGlobalFunctions::ClearPlayerGossipEvents }, + { "ClearServerEvents", &LuaGlobalFunctions::ClearServerEvents }, + { "ClearMapEvents", &LuaGlobalFunctions::ClearMapEvents }, + { "ClearInstanceEvents", &LuaGlobalFunctions::ClearInstanceEvents }, + { "ClearTicketEvents", &LuaGlobalFunctions::ClearTicketEvents }, + { "ClearSpellEvents", &LuaGlobalFunctions::ClearSpellEvents }, + { "ClearAllCreatureEvents", &LuaGlobalFunctions::ClearAllCreatureEvents }, + + // Getters + { "GetLuaEngine", &LuaGlobalFunctions::GetLuaEngine }, + { "GetCoreName", &LuaGlobalFunctions::GetCoreName }, + { "GetConfigValue", &LuaGlobalFunctions::GetConfigValue }, + { "GetRealmID", &LuaGlobalFunctions::GetRealmID }, + { "GetCoreVersion", &LuaGlobalFunctions::GetCoreVersion }, + { "GetCoreExpansion", &LuaGlobalFunctions::GetCoreExpansion }, + { "GetStateMap", &LuaGlobalFunctions::GetStateMap }, + { "GetStateMapId", &LuaGlobalFunctions::GetStateMapId }, + { "GetStateInstanceId", &LuaGlobalFunctions::GetStateInstanceId }, + { "GetQuest", &LuaGlobalFunctions::GetQuest }, + { "GetPlayerByGUID", &LuaGlobalFunctions::GetPlayerByGUID }, + { "GetPlayerByName", &LuaGlobalFunctions::GetPlayerByName }, + { "GetGameTime", &LuaGlobalFunctions::GetGameTime }, + { "GetPlayersInWorld", &LuaGlobalFunctions::GetPlayersInWorld }, + { "GetGuildByName", &LuaGlobalFunctions::GetGuildByName }, + { "GetGuildByLeaderGUID", &LuaGlobalFunctions::GetGuildByLeaderGUID }, + { "GetPlayerCount", &LuaGlobalFunctions::GetPlayerCount }, + { "GetPlayerGUID", &LuaGlobalFunctions::GetPlayerGUID }, + { "GetItemGUID", &LuaGlobalFunctions::GetItemGUID }, + { "GetItemTemplate", &LuaGlobalFunctions::GetItemTemplate }, + { "GetObjectGUID", &LuaGlobalFunctions::GetObjectGUID }, + { "GetUnitGUID", &LuaGlobalFunctions::GetUnitGUID }, + { "GetGUIDLow", &LuaGlobalFunctions::GetGUIDLow }, + { "GetGUIDType", &LuaGlobalFunctions::GetGUIDType }, + { "GetGUIDEntry", &LuaGlobalFunctions::GetGUIDEntry }, + { "GetPackedGUIDSize", &LuaGlobalFunctions::GetPackedGUIDSize }, + { "GetAreaName", &LuaGlobalFunctions::GetAreaName }, + { "GetOwnerHalaa", &LuaGlobalFunctions::GetOwnerHalaa }, + { "bit_not", &LuaGlobalFunctions::bit_not }, + { "bit_xor", &LuaGlobalFunctions::bit_xor }, + { "bit_rshift", &LuaGlobalFunctions::bit_rshift }, + { "bit_lshift", &LuaGlobalFunctions::bit_lshift }, + { "bit_or", &LuaGlobalFunctions::bit_or }, + { "bit_and", &LuaGlobalFunctions::bit_and }, + { "GetItemLink", &LuaGlobalFunctions::GetItemLink }, + { "GetMapById", &LuaGlobalFunctions::GetMapById }, + { "GetCurrTime", &LuaGlobalFunctions::GetCurrTime }, + { "GetTimeDiff", &LuaGlobalFunctions::GetTimeDiff }, + { "PrintInfo", &LuaGlobalFunctions::PrintInfo }, + { "PrintError", &LuaGlobalFunctions::PrintError }, + { "PrintDebug", &LuaGlobalFunctions::PrintDebug }, + { "GetActiveGameEvents", &LuaGlobalFunctions::GetActiveGameEvents }, + { "GetGossipMenuOptionLocale", &LuaGlobalFunctions::GetGossipMenuOptionLocale }, + { "GetMapEntrance", &LuaGlobalFunctions::GetMapEntrance }, + { "GetSpellInfo", &LuaGlobalFunctions::GetSpellInfo }, + + // Boolean + { "IsCompatibilityMode", &LuaGlobalFunctions::IsCompatibilityMode }, + { "IsInventoryPos", &LuaGlobalFunctions::IsInventoryPos }, + { "IsEquipmentPos", &LuaGlobalFunctions::IsEquipmentPos }, + { "IsBankPos", &LuaGlobalFunctions::IsBankPos }, + { "IsBagPos", &LuaGlobalFunctions::IsBagPos }, + { "IsGameEventActive", &LuaGlobalFunctions::IsGameEventActive }, + + // Other + { "ReloadALE", &LuaGlobalFunctions::ReloadALE }, + { "RunCommand", &LuaGlobalFunctions::RunCommand }, + { "SendWorldMessage", &LuaGlobalFunctions::SendWorldMessage }, + { "WorldDBQuery", &LuaGlobalFunctions::WorldDBQuery }, + { "WorldDBQueryAsync", &LuaGlobalFunctions::WorldDBQueryAsync }, + { "WorldDBExecute", &LuaGlobalFunctions::WorldDBExecute }, + { "CharDBQuery", &LuaGlobalFunctions::CharDBQuery }, + { "CharDBQueryAsync", &LuaGlobalFunctions::CharDBQueryAsync }, + { "CharDBExecute", &LuaGlobalFunctions::CharDBExecute }, + { "AuthDBQuery", &LuaGlobalFunctions::AuthDBQuery }, + { "AuthDBQueryAsync", &LuaGlobalFunctions::AuthDBQueryAsync }, + { "AuthDBExecute", &LuaGlobalFunctions::AuthDBExecute }, + { "CreateLuaEvent", &LuaGlobalFunctions::CreateLuaEvent }, + { "RemoveEventById", &LuaGlobalFunctions::RemoveEventById }, + { "RemoveEvents", &LuaGlobalFunctions::RemoveEvents }, + { "PerformIngameSpawn", &LuaGlobalFunctions::PerformIngameSpawn }, + { "CreatePacket", &LuaGlobalFunctions::CreatePacket }, + { "AddVendorItem", &LuaGlobalFunctions::AddVendorItem }, + { "VendorRemoveItem", &LuaGlobalFunctions::VendorRemoveItem }, + { "VendorRemoveAllItems", &LuaGlobalFunctions::VendorRemoveAllItems }, + { "Kick", &LuaGlobalFunctions::Kick }, + { "Ban", &LuaGlobalFunctions::Ban }, + { "SaveAllPlayers", &LuaGlobalFunctions::SaveAllPlayers }, + { "SendMail", &LuaGlobalFunctions::SendMail }, + { "AddTaxiPath", &LuaGlobalFunctions::AddTaxiPath }, + { "CreateInt64", &LuaGlobalFunctions::CreateLongLong }, + { "CreateUint64", &LuaGlobalFunctions::CreateULongLong }, + { "StartGameEvent", &LuaGlobalFunctions::StartGameEvent }, + { "StopGameEvent", &LuaGlobalFunctions::StopGameEvent }, + { "HttpRequest", &LuaGlobalFunctions::HttpRequest }, + { "SetOwnerHalaa", &LuaGlobalFunctions::SetOwnerHalaa }, + { "LookupEntry", &LuaGlobalFunctions::LookupEntry }, + + { NULL, NULL } +}; + +ALERegister ObjectMethods[] = +{ + // Getters + { "GetEntry", &LuaObject::GetEntry }, + { "GetGUID", &LuaObject::GetGUID }, + { "GetGUIDLow", &LuaObject::GetGUIDLow }, + { "GetInt32Value", &LuaObject::GetInt32Value }, + { "GetUInt32Value", &LuaObject::GetUInt32Value }, + { "GetFloatValue", &LuaObject::GetFloatValue }, + { "GetByteValue", &LuaObject::GetByteValue }, + { "GetUInt16Value", &LuaObject::GetUInt16Value }, + { "GetUInt64Value", &LuaObject::GetUInt64Value }, + { "GetScale", &LuaObject::GetScale }, + { "GetTypeId", &LuaObject::GetTypeId }, + + // Setters + { "SetInt32Value", &LuaObject::SetInt32Value }, + { "SetUInt32Value", &LuaObject::SetUInt32Value }, + { "UpdateUInt32Value", &LuaObject::UpdateUInt32Value }, + { "SetFloatValue", &LuaObject::SetFloatValue }, + { "SetByteValue", &LuaObject::SetByteValue }, + { "SetUInt16Value", &LuaObject::SetUInt16Value }, + { "SetInt16Value", &LuaObject::SetInt16Value }, + { "SetUInt64Value", &LuaObject::SetUInt64Value }, + { "SetScale", &LuaObject::SetScale }, + { "SetFlag", &LuaObject::SetFlag }, + + // Boolean + { "IsInWorld", &LuaObject::IsInWorld }, + { "IsPlayer", &LuaObject::IsPlayer }, + { "HasFlag", &LuaObject::HasFlag }, + + // Other + { "ToGameObject", &LuaObject::ToGameObject }, + { "ToUnit", &LuaObject::ToUnit }, + { "ToCreature", &LuaObject::ToCreature }, + { "ToPlayer", &LuaObject::ToPlayer }, + { "ToCorpse", &LuaObject::ToCorpse }, + { "RemoveFlag", &LuaObject::RemoveFlag }, + + { NULL, NULL } +}; + +ALERegister WorldObjectMethods[] = +{ + // Getters + { "GetName", &LuaWorldObject::GetName }, + { "GetMap", &LuaWorldObject::GetMap }, + { "GetPhaseMask", &LuaWorldObject::GetPhaseMask }, + { "SetPhaseMask", &LuaWorldObject::SetPhaseMask }, + { "GetInstanceId", &LuaWorldObject::GetInstanceId }, + { "GetAreaId", &LuaWorldObject::GetAreaId }, + { "GetZoneId", &LuaWorldObject::GetZoneId }, + { "GetMapId", &LuaWorldObject::GetMapId }, + { "GetX", &LuaWorldObject::GetX }, + { "GetY", &LuaWorldObject::GetY }, + { "GetZ", &LuaWorldObject::GetZ }, + { "GetO", &LuaWorldObject::GetO }, + { "GetLocation", &LuaWorldObject::GetLocation }, + { "GetPlayersInRange", &LuaWorldObject::GetPlayersInRange }, + { "GetCreaturesInRange", &LuaWorldObject::GetCreaturesInRange }, + { "GetGameObjectsInRange", &LuaWorldObject::GetGameObjectsInRange }, + { "GetNearestPlayer", &LuaWorldObject::GetNearestPlayer }, + { "GetNearestGameObject", &LuaWorldObject::GetNearestGameObject }, + { "GetNearestCreature", &LuaWorldObject::GetNearestCreature }, + { "GetNearObject", &LuaWorldObject::GetNearObject }, + { "GetNearObjects", &LuaWorldObject::GetNearObjects }, + { "GetDistance", &LuaWorldObject::GetDistance }, + { "GetExactDistance", &LuaWorldObject::GetExactDistance }, + { "GetDistance2d", &LuaWorldObject::GetDistance2d }, + { "GetExactDistance2d", &LuaWorldObject::GetExactDistance2d }, + { "GetRelativePoint", &LuaWorldObject::GetRelativePoint }, + { "GetAngle", &LuaWorldObject::GetAngle }, + { "GetTransport", &LuaWorldObject::GetTransport }, + + // Boolean + { "IsWithinLoS", &LuaWorldObject::IsWithinLoS }, + { "IsInMap", &LuaWorldObject::IsInMap }, + { "IsWithinDist3d", &LuaWorldObject::IsWithinDist3d }, + { "IsWithinDist2d", &LuaWorldObject::IsWithinDist2d }, + { "IsWithinDist", &LuaWorldObject::IsWithinDist }, + { "IsWithinDistInMap", &LuaWorldObject::IsWithinDistInMap }, + { "IsInRange", &LuaWorldObject::IsInRange }, + { "IsInRange2d", &LuaWorldObject::IsInRange2d }, + { "IsInRange3d", &LuaWorldObject::IsInRange3d }, + { "IsInFront", &LuaWorldObject::IsInFront }, + { "IsInBack", &LuaWorldObject::IsInBack }, + + // Other + { "SummonGameObject", &LuaWorldObject::SummonGameObject }, + { "SpawnCreature", &LuaWorldObject::SpawnCreature }, + { "SendPacket", &LuaWorldObject::SendPacket }, + { "RegisterEvent", &LuaWorldObject::RegisterEvent }, + { "RemoveEventById", &LuaWorldObject::RemoveEventById }, + { "RemoveEvents", &LuaWorldObject::RemoveEvents }, + { "PlayMusic", &LuaWorldObject::PlayMusic }, + { "PlayDirectSound", &LuaWorldObject::PlayDirectSound }, + { "PlayDistanceSound", &LuaWorldObject::PlayDistanceSound }, + + { NULL, NULL } +}; + +ALERegister UnitMethods[] = +{ + // Getters + { "GetLevel", &LuaUnit::GetLevel }, + { "GetHealth", &LuaUnit::GetHealth }, + { "GetDisplayId", &LuaUnit::GetDisplayId }, + { "GetNativeDisplayId", &LuaUnit::GetNativeDisplayId }, + { "GetPower", &LuaUnit::GetPower }, + { "GetMaxPower", &LuaUnit::GetMaxPower }, + { "GetPowerType", &LuaUnit::GetPowerType }, + { "GetMaxHealth", &LuaUnit::GetMaxHealth }, + { "GetHealthPct", &LuaUnit::GetHealthPct }, + { "GetPowerPct", &LuaUnit::GetPowerPct }, + { "GetGender", &LuaUnit::GetGender }, + { "GetRace", &LuaUnit::GetRace }, + { "GetClass", &LuaUnit::GetClass }, + { "GetRaceMask", &LuaUnit::GetRaceMask }, + { "GetClassMask", &LuaUnit::GetClassMask }, + { "GetRaceAsString", &LuaUnit::GetRaceAsString }, + { "GetClassAsString", &LuaUnit::GetClassAsString }, + { "GetAura", &LuaUnit::GetAura }, + { "GetFaction", &LuaUnit::GetFaction }, + { "GetCurrentSpell", &LuaUnit::GetCurrentSpell }, + { "GetCreatureType", &LuaUnit::GetCreatureType }, + { "GetMountId", &LuaUnit::GetMountId }, + { "GetOwner", &LuaUnit::GetOwner }, + { "GetFriendlyUnitsInRange", &LuaUnit::GetFriendlyUnitsInRange }, + { "GetUnfriendlyUnitsInRange", &LuaUnit::GetUnfriendlyUnitsInRange }, + { "GetOwnerGUID", &LuaUnit::GetOwnerGUID }, + { "GetCreatorGUID", &LuaUnit::GetCreatorGUID }, + { "GetMinionGUID", &LuaUnit::GetPetGUID }, + { "GetCharmerGUID", &LuaUnit::GetCharmerGUID }, + { "GetCharmGUID", &LuaUnit::GetCharmGUID }, + { "GetPetGUID", &LuaUnit::GetPetGUID }, + { "GetCritterGUID", &LuaUnit::GetCritterGUID }, + { "GetControllerGUID", &LuaUnit::GetControllerGUID }, + { "GetControllerGUIDS", &LuaUnit::GetControllerGUIDS }, + { "GetStandState", &LuaUnit::GetStandState }, + { "GetVictim", &LuaUnit::GetVictim }, + { "GetSpeed", &LuaUnit::GetSpeed }, + { "GetSpeedRate", &LuaUnit::GetSpeedRate }, + { "GetStat", &LuaUnit::GetStat }, + { "GetBaseSpellPower", &LuaUnit::GetBaseSpellPower }, + { "GetVehicleKit", &LuaUnit::GetVehicleKit }, + // {"GetVehicle", &LuaUnit::GetVehicle}, // :GetVehicle() - UNDOCUMENTED - Gets the Vehicle kit of the vehicle the unit is on + { "GetMovementType", &LuaUnit::GetMovementType }, + { "GetAttackers", &LuaUnit::GetAttackers }, + { "GetThreat", &LuaUnit::GetThreat }, + + // Setters + { "SetFaction", &LuaUnit::SetFaction }, + { "SetLevel", &LuaUnit::SetLevel }, + { "SetHealth", &LuaUnit::SetHealth }, + { "SetMaxHealth", &LuaUnit::SetMaxHealth }, + { "SetPower", &LuaUnit::SetPower }, + { "SetMaxPower", &LuaUnit::SetMaxPower }, + { "SetPowerType", &LuaUnit::SetPowerType }, + { "SetDisplayId", &LuaUnit::SetDisplayId }, + { "SetNativeDisplayId", &LuaUnit::SetNativeDisplayId }, + { "SetFacing", &LuaUnit::SetFacing }, + { "SetFacingToObject", &LuaUnit::SetFacingToObject }, + { "SetSpeed", &LuaUnit::SetSpeed }, + { "SetSpeedRate", &LuaUnit::SetSpeedRate }, + // {"SetStunned", &LuaUnit::SetStunned}, // :SetStunned([enable]) - UNDOCUMENTED - Stuns or removes stun + {"SetRooted", &LuaUnit::SetRooted}, + {"SetConfused", &LuaUnit::SetConfused}, + {"SetFeared", &LuaUnit::SetFeared}, + { "SetPvP", &LuaUnit::SetPvP }, + { "SetFFA", &LuaUnit::SetFFA }, + { "SetSanctuary", &LuaUnit::SetSanctuary }, + // {"SetCanFly", &LuaUnit::SetCanFly}, // :SetCanFly(apply) - UNDOCUMENTED + // {"SetVisible", &LuaUnit::SetVisible}, // :SetVisible(x) - UNDOCUMENTED + { "SetOwnerGUID", &LuaUnit::SetOwnerGUID }, + { "SetName", &LuaUnit::SetName }, + { "SetSheath", &LuaUnit::SetSheath }, + { "SetCreatorGUID", &LuaUnit::SetCreatorGUID }, + { "SetMinionGUID", &LuaUnit::SetPetGUID }, + { "SetPetGUID", &LuaUnit::SetPetGUID }, + { "SetCritterGUID", &LuaUnit::SetCritterGUID }, + { "SetWaterWalk", &LuaUnit::SetWaterWalk }, + { "SetStandState", &LuaUnit::SetStandState }, + { "SetInCombatWith", &LuaUnit::SetInCombatWith }, + { "ModifyPower", &LuaUnit::ModifyPower }, + { "SetImmuneTo", &LuaUnit::SetImmuneTo }, + + // Boolean + { "IsAlive", &LuaUnit::IsAlive }, + { "IsDead", &LuaUnit::IsDead }, + { "IsDying", &LuaUnit::IsDying }, + { "IsPvPFlagged", &LuaUnit::IsPvPFlagged }, + { "IsInCombat", &LuaUnit::IsInCombat }, + { "IsBanker", &LuaUnit::IsBanker }, + { "IsBattleMaster", &LuaUnit::IsBattleMaster }, + { "IsCharmed", &LuaUnit::IsCharmed }, + { "IsArmorer", &LuaUnit::IsArmorer }, + { "IsAttackingPlayer", &LuaUnit::IsAttackingPlayer }, + { "IsInWater", &LuaUnit::IsInWater }, + { "IsUnderWater", &LuaUnit::IsUnderWater }, + { "IsAuctioneer", &LuaUnit::IsAuctioneer }, + { "IsGuildMaster", &LuaUnit::IsGuildMaster }, + { "IsInnkeeper", &LuaUnit::IsInnkeeper }, + { "IsTrainer", &LuaUnit::IsTrainer }, + { "IsGossip", &LuaUnit::IsGossip }, + { "IsTaxi", &LuaUnit::IsTaxi }, + { "IsSpiritHealer", &LuaUnit::IsSpiritHealer }, + { "IsSpiritGuide", &LuaUnit::IsSpiritGuide }, + { "IsTabardDesigner", &LuaUnit::IsTabardDesigner }, + { "IsServiceProvider", &LuaUnit::IsServiceProvider }, + { "IsSpiritService", &LuaUnit::IsSpiritService }, + { "HealthBelowPct", &LuaUnit::HealthBelowPct }, + { "HealthAbovePct", &LuaUnit::HealthAbovePct }, + { "IsMounted", &LuaUnit::IsMounted }, + { "AttackStop", &LuaUnit::AttackStop }, + { "Attack", &LuaUnit::Attack }, + // {"IsVisible", &LuaUnit::IsVisible}, // :IsVisible() - UNDOCUMENTED + // {"IsMoving", &LuaUnit::IsMoving}, // :IsMoving() - UNDOCUMENTED + // {"IsFlying", &LuaUnit::IsFlying}, // :IsFlying() - UNDOCUMENTED + { "IsStopped", &LuaUnit::IsStopped }, + { "HasUnitState", &LuaUnit::HasUnitState }, + { "IsQuestGiver", &LuaUnit::IsQuestGiver }, + { "IsInAccessiblePlaceFor", &LuaUnit::IsInAccessiblePlaceFor }, + { "IsVendor", &LuaUnit::IsVendor }, + { "IsRooted", &LuaUnit::IsRooted }, + { "IsFullHealth", &LuaUnit::IsFullHealth }, + { "HasAura", &LuaUnit::HasAura }, + { "IsCasting", &LuaUnit::IsCasting }, + { "IsStandState", &LuaUnit::IsStandState }, + { "IsOnVehicle", &LuaUnit::IsOnVehicle }, + + // Other + { "HandleStatFlatModifier", &LuaUnit::HandleStatFlatModifier }, + { "AddAura", &LuaUnit::AddAura }, + { "RemoveAura", &LuaUnit::RemoveAura }, + { "RemoveAllAuras", &LuaUnit::RemoveAllAuras }, + { "RemoveArenaAuras", &LuaUnit::RemoveArenaAuras }, + { "ClearInCombat", &LuaUnit::ClearInCombat }, + { "DeMorph", &LuaUnit::DeMorph }, + { "SendUnitWhisper", &LuaUnit::SendUnitWhisper }, + { "SendUnitEmote", &LuaUnit::SendUnitEmote }, + { "SendUnitSay", &LuaUnit::SendUnitSay }, + { "SendUnitYell", &LuaUnit::SendUnitYell }, + { "CastSpell", &LuaUnit::CastSpell }, + { "CastCustomSpell", &LuaUnit::CastCustomSpell }, + { "CastSpellAoF", &LuaUnit::CastSpellAoF }, + { "Kill", &LuaUnit::Kill }, + { "StopSpellCast", &LuaUnit::StopSpellCast }, + { "InterruptSpell", &LuaUnit::InterruptSpell }, + { "SendChatMessageToPlayer", &LuaUnit::SendChatMessageToPlayer }, + { "PerformEmote", &LuaUnit::PerformEmote }, + { "EmoteState", &LuaUnit::EmoteState }, + { "CountPctFromCurHealth", &LuaUnit::CountPctFromCurHealth }, + { "CountPctFromMaxHealth", &LuaUnit::CountPctFromMaxHealth }, + { "Dismount", &LuaUnit::Dismount }, + { "Mount", &LuaUnit::Mount }, + // {"RestoreDisplayId", &LuaUnit::RestoreDisplayId}, // :RestoreDisplayId() - UNDOCUMENTED + // {"RestoreFaction", &LuaUnit::RestoreFaction}, // :RestoreFaction() - UNDOCUMENTED + // {"RemoveBindSightAuras", &LuaUnit::RemoveBindSightAuras}, // :RemoveBindSightAuras() - UNDOCUMENTED + // {"RemoveCharmAuras", &LuaUnit::RemoveCharmAuras}, // :RemoveCharmAuras() - UNDOCUMENTED + { "ClearThreatList", &LuaUnit::ClearThreatList }, + { "GetThreatList", &LuaUnit::GetThreatList }, + { "ClearUnitState", &LuaUnit::ClearUnitState }, + { "AddUnitState", &LuaUnit::AddUnitState }, + // {"DisableMelee", &LuaUnit::DisableMelee}, // :DisableMelee([disable]) - UNDOCUMENTED - if true, enables + // {"SummonGuardian", &LuaUnit::SummonGuardian}, // :SummonGuardian(entry, x, y, z, o[, duration]) - UNDOCUMENTED - summons a guardian to location. Scales with summoner, is friendly to him and guards him. + { "NearTeleport", &LuaUnit::NearTeleport }, + { "MoveIdle", &LuaUnit::MoveIdle }, + { "MoveRandom", &LuaUnit::MoveRandom }, + { "MoveHome", &LuaUnit::MoveHome }, + { "MoveFollow", &LuaUnit::MoveFollow }, + { "MoveChase", &LuaUnit::MoveChase }, + { "MoveConfused", &LuaUnit::MoveConfused }, + { "MoveFleeing", &LuaUnit::MoveFleeing }, + { "MoveTo", &LuaUnit::MoveTo }, + { "MoveJump", &LuaUnit::MoveJump }, + { "MoveStop", &LuaUnit::MoveStop }, + { "MoveExpire", &LuaUnit::MoveExpire }, + { "MoveClear", &LuaUnit::MoveClear }, + { "DealDamage", &LuaUnit::DealDamage }, + { "DealHeal", &LuaUnit::DealHeal }, + { "AddThreat", &LuaUnit::AddThreat }, + { "ModifyThreatPct", &LuaUnit::ModifyThreatPct }, + { "ClearThreat", &LuaUnit::ClearThreat }, + { "ResetAllThreat", &LuaUnit::ResetAllThreat }, + + { NULL, NULL } +}; + +ALERegister PlayerMethods[] = +{ + // Getters + { "GetInventoryFreeSlots", &LuaPlayer::GetInventoryFreeSlots }, + { "GetBankFreeSlots", &LuaPlayer::GetBankFreeSlots }, + { "GetSelection", &LuaPlayer::GetSelection }, + { "GetGMRank", &LuaPlayer::GetGMRank }, + { "GetGuildId", &LuaPlayer::GetGuildId }, + { "GetCoinage", &LuaPlayer::GetCoinage }, + { "GetTeam", &LuaPlayer::GetTeam }, + { "GetItemCount", &LuaPlayer::GetItemCount }, + { "GetGroup", &LuaPlayer::GetGroup }, + { "GetGuild", &LuaPlayer::GetGuild }, + { "GetAccountId", &LuaPlayer::GetAccountId }, + { "GetAccountName", &LuaPlayer::GetAccountName }, + { "GetCompletedQuestsCount", &LuaPlayer::GetCompletedQuestsCount }, + { "GetArenaPoints", &LuaPlayer::GetArenaPoints }, + { "GetHonorPoints", &LuaPlayer::GetHonorPoints }, + { "GetTodayHonorPoints", &LuaPlayer::GetTodayHonorPoints }, + { "GetYesterdayHonorPoints", &LuaPlayer::GetYesterdayHonorPoints }, + { "GetLifetimeKills", &LuaPlayer::GetLifetimeKills }, + { "GetTodayKills", &LuaPlayer::GetTodayKills }, + { "GetYesterdayKills", &LuaPlayer::GetYesterdayKills }, + { "GetPlayerIP", &LuaPlayer::GetPlayerIP }, + { "GetLevelPlayedTime", &LuaPlayer::GetLevelPlayedTime }, + { "GetTotalPlayedTime", &LuaPlayer::GetTotalPlayedTime }, + { "GetItemByPos", &LuaPlayer::GetItemByPos }, + { "GetItemByEntry", &LuaPlayer::GetItemByEntry }, + { "GetItemByGUID", &LuaPlayer::GetItemByGUID }, + { "GetMailCount", &LuaPlayer::GetMailCount }, + { "GetMailItem", &LuaPlayer::GetMailItem }, + { "GetReputation", &LuaPlayer::GetReputation }, + { "GetEquippedItemBySlot", &LuaPlayer::GetEquippedItemBySlot }, + { "GetQuestLevel", &LuaPlayer::GetQuestLevel }, + { "GetChatTag", &LuaPlayer::GetChatTag }, + { "GetRestBonus", &LuaPlayer::GetRestBonus }, + { "GetPhaseMaskForSpawn", &LuaPlayer::GetPhaseMaskForSpawn }, + { "GetAchievementPoints", &LuaPlayer::GetAchievementPoints }, + { "GetCompletedAchievementsCount", &LuaPlayer::GetCompletedAchievementsCount }, + { "GetReqKillOrCastCurrentCount", &LuaPlayer::GetReqKillOrCastCurrentCount }, + { "GetQuestStatus", &LuaPlayer::GetQuestStatus }, + { "GetInGameTime", &LuaPlayer::GetInGameTime }, + { "GetComboPoints", &LuaPlayer::GetComboPoints }, + { "GetComboTarget", &LuaPlayer::GetComboTarget }, + { "GetGuildName", &LuaPlayer::GetGuildName }, + { "GetFreeTalentPoints", &LuaPlayer::GetFreeTalentPoints }, + { "GetActiveSpec", &LuaPlayer::GetActiveSpec }, + { "GetSpecsCount", &LuaPlayer::GetSpecsCount }, + { "GetSpellCooldownDelay", &LuaPlayer::GetSpellCooldownDelay }, + { "GetGuildRank", &LuaPlayer::GetGuildRank }, + { "GetDifficulty", &LuaPlayer::GetDifficulty }, + { "GetHealthBonusFromStamina", &LuaPlayer::GetHealthBonusFromStamina }, + { "GetManaBonusFromIntellect", &LuaPlayer::GetManaBonusFromIntellect }, + { "GetMaxSkillValue", &LuaPlayer::GetMaxSkillValue }, + { "GetPureMaxSkillValue", &LuaPlayer::GetPureMaxSkillValue }, + { "GetSkillValue", &LuaPlayer::GetSkillValue }, + { "GetBaseSkillValue", &LuaPlayer::GetBaseSkillValue }, + { "GetPureSkillValue", &LuaPlayer::GetPureSkillValue }, + { "GetSkillPermBonusValue", &LuaPlayer::GetSkillPermBonusValue }, + { "GetSkillTempBonusValue", &LuaPlayer::GetSkillTempBonusValue }, + { "GetReputationRank", &LuaPlayer::GetReputationRank }, + { "GetDrunkValue", &LuaPlayer::GetDrunkValue }, + { "GetBattlegroundId", &LuaPlayer::GetBattlegroundId }, + { "GetBattlegroundTypeId", &LuaPlayer::GetBattlegroundTypeId }, + { "GetXP", &LuaPlayer::GetXP }, + { "GetXPRestBonus", &LuaPlayer::GetXPRestBonus }, + { "GetGroupInvite", &LuaPlayer::GetGroupInvite }, + { "GetSubGroup", &LuaPlayer::GetSubGroup }, + { "GetNextRandomRaidMember", &LuaPlayer::GetNextRandomRaidMember }, + { "GetOriginalGroup", &LuaPlayer::GetOriginalGroup }, + { "GetOriginalSubGroup", &LuaPlayer::GetOriginalSubGroup }, + { "GetChampioningFaction", &LuaPlayer::GetChampioningFaction }, + { "GetLatency", &LuaPlayer::GetLatency }, + // {"GetRecruiterId", &LuaPlayer::GetRecruiterId}, // :GetRecruiterId() - UNDOCUMENTED - Returns player's recruiter's ID + { "GetDbLocaleIndex", &LuaPlayer::GetDbLocaleIndex }, + { "GetDbcLocale", &LuaPlayer::GetDbcLocale }, + { "GetCorpse", &LuaPlayer::GetCorpse }, + { "GetGossipTextId", &LuaPlayer::GetGossipTextId }, + { "GetQuestRewardStatus", &LuaPlayer::GetQuestRewardStatus }, + { "GetShieldBlockValue", &LuaPlayer::GetShieldBlockValue }, + { "GetPlayerSettingValue", &LuaPlayer::GetPlayerSettingValue }, + { "GetTrader", &LuaPlayer::GetTrader }, + { "GetBonusTalentCount", &LuaPlayer::GetBonusTalentCount }, + { "GetKnownTaxiNodes", &LuaPlayer::GetKnownTaxiNodes }, + { "GetPet", &LuaPlayer::GetPet }, + { "GetTemporaryUnsummonedPetNumber", &LuaPlayer::GetTemporaryUnsummonedPetNumber }, + { "GetLastPetNumber", &LuaPlayer::GetLastPetNumber }, + { "GetLastPetSpell", &LuaPlayer::GetLastPetSpell }, + { "GetQuestSlotQuestId", &LuaPlayer::GetQuestSlotQuestId }, + { "GetTalentTreePoints", &LuaPlayer::GetTalentTreePoints }, + { "GetMostPointsTalentTree", &LuaPlayer::GetMostPointsTalentTree }, + + // Setters + { "SetTemporaryUnsummonedPetNumber", &LuaPlayer::SetTemporaryUnsummonedPetNumber }, + { "SetLastPetNumber", &LuaPlayer::SetLastPetNumber }, + { "SetLastPetSpell", &LuaPlayer::SetLastPetSpell }, + { "SetShowDKPet", &LuaPlayer::SetShowDKPet }, + { "AdvanceSkillsToMax", &LuaPlayer::AdvanceSkillsToMax }, + { "AdvanceSkill", &LuaPlayer::AdvanceSkill }, + { "AdvanceAllSkills", &LuaPlayer::AdvanceAllSkills }, + { "AddLifetimeKills", &LuaPlayer::AddLifetimeKills }, + { "SetCoinage", &LuaPlayer::SetCoinage }, + { "SetKnownTitle", &LuaPlayer::SetKnownTitle }, + { "UnsetKnownTitle", &LuaPlayer::UnsetKnownTitle }, + { "SetBindPoint", &LuaPlayer::SetBindPoint }, + { "SetArenaPoints", &LuaPlayer::SetArenaPoints }, + { "SetHonorPoints", &LuaPlayer::SetHonorPoints }, + { "SetSpellPower", &LuaPlayer::SetSpellPower }, + { "SetLifetimeKills", &LuaPlayer::SetLifetimeKills }, + { "SetGameMaster", &LuaPlayer::SetGameMaster }, + { "SetGMChat", &LuaPlayer::SetGMChat }, + { "SetKnownTaxiNodes", &LuaPlayer::SetKnownTaxiNodes }, + { "SetTaxiCheat", &LuaPlayer::SetTaxiCheat }, + { "SetGMVisible", &LuaPlayer::SetGMVisible }, + { "SetPvPDeath", &LuaPlayer::SetPvPDeath }, + { "SetAcceptWhispers", &LuaPlayer::SetAcceptWhispers }, + { "SetRestBonus", &LuaPlayer::SetRestBonus }, + { "SetQuestStatus", &LuaPlayer::SetQuestStatus }, + { "SetReputation", &LuaPlayer::SetReputation }, + { "SetFreeTalentPoints", &LuaPlayer::SetFreeTalentPoints }, + { "SetGuildRank", &LuaPlayer::SetGuildRank }, + // {"SetMovement", &LuaPlayer::SetMovement}, // :SetMovement(type) - UNDOCUMENTED - Sets player's movement type + { "SetSkill", &LuaPlayer::SetSkill }, + { "SetFactionForRace", &LuaPlayer::SetFactionForRace }, + { "SetDrunkValue", &LuaPlayer::SetDrunkValue }, + { "SetAtLoginFlag", &LuaPlayer::SetAtLoginFlag }, + { "SetPlayerLock", &LuaPlayer::SetPlayerLock }, + { "SetGender", &LuaPlayer::SetGender }, + { "SetSheath", &LuaPlayer::SetSheath }, + { "SetBonusTalentCount", &LuaPlayer::SetBonusTalentCount }, + { "AddBonusTalent", &LuaPlayer::AddBonusTalent }, + { "RemoveBonusTalent", &LuaPlayer::RemoveBonusTalent }, + { "GetHomebind", &LuaPlayer::GetHomebind }, + { "GetSpells", &LuaPlayer::GetSpells }, + { "GetAverageItemLevel", &LuaPlayer::GetAverageItemLevel }, + { "GetBarberShopCost", &LuaPlayer::GetBarberShopCost }, + { "GetSightRange", &LuaPlayer::GetSightRange }, + { "GetWeaponProficiency", &LuaPlayer::GetWeaponProficiency }, + { "GetArmorProficiency", &LuaPlayer::GetArmorProficiency }, + { "GetAmmoDPS", &LuaPlayer::GetAmmoDPS }, + { "GetShield", &LuaPlayer::GetShield }, + { "GetRunesState", &LuaPlayer::GetRunesState }, + { "GetViewpoint", &LuaPlayer::GetViewpoint }, + { "GetDodgeFromAgility", &LuaPlayer::GetDodgeFromAgility }, + { "GetMeleeCritFromAgility", &LuaPlayer::GetMeleeCritFromAgility }, + { "GetSpellCritFromIntellect", &LuaPlayer::GetSpellCritFromIntellect }, + { "GetInventoryItem", &LuaPlayer::GetInventoryItem }, + { "GetBankItem", &LuaPlayer::GetBankItem }, + { "GetCreationTime", &LuaPlayer::GetCreationTime }, + { "SetCanFly", &LuaPlayer::SetCanFly }, + + // Boolean + { "HasTankSpec", &LuaPlayer::HasTankSpec }, + { "HasMeleeSpec", &LuaPlayer::HasMeleeSpec }, + { "HasCasterSpec", &LuaPlayer::HasCasterSpec }, + { "HasHealSpec", &LuaPlayer::HasHealSpec }, + { "IsInGroup", &LuaPlayer::IsInGroup }, + { "IsInGuild", &LuaPlayer::IsInGuild }, + { "IsGM", &LuaPlayer::IsGM }, + { "IsImmuneToDamage", &LuaPlayer::IsImmuneToDamage }, + { "IsAlliance", &LuaPlayer::IsAlliance }, + { "IsHorde", &LuaPlayer::IsHorde }, + { "HasTitle", &LuaPlayer::HasTitle }, + { "HasItem", &LuaPlayer::HasItem }, + { "Teleport", &LuaPlayer::Teleport }, + { "AddItem", &LuaPlayer::AddItem }, + { "IsInArenaTeam", &LuaPlayer::IsInArenaTeam }, + { "CanRewardQuest", &LuaPlayer::CanRewardQuest }, + { "CanCompleteRepeatableQuest", &LuaPlayer::CanCompleteRepeatableQuest }, + { "CanCompleteQuest", &LuaPlayer::CanCompleteQuest }, + { "CanEquipItem", &LuaPlayer::CanEquipItem }, + { "IsFalling", &LuaPlayer::IsFalling }, + { "ToggleAFK", &LuaPlayer::ToggleAFK }, + { "ToggleDND", &LuaPlayer::ToggleDND }, + { "IsAFK", &LuaPlayer::IsAFK }, + { "IsDND", &LuaPlayer::IsDND }, + { "IsAcceptingWhispers", &LuaPlayer::IsAcceptingWhispers }, + { "IsGMChat", &LuaPlayer::IsGMChat }, + { "IsTaxiCheater", &LuaPlayer::IsTaxiCheater }, + { "IsGMVisible", &LuaPlayer::IsGMVisible }, + { "HasQuest", &LuaPlayer::HasQuest }, + { "InBattlegroundQueue", &LuaPlayer::InBattlegroundQueue }, + // {"IsImmuneToEnvironmentalDamage", &LuaPlayer::IsImmuneToEnvironmentalDamage}, // :IsImmuneToEnvironmentalDamage() - UNDOCUMENTED - Returns true if the player is immune to environmental damage + { "CanSpeak", &LuaPlayer::CanSpeak }, + { "HasAtLoginFlag", &LuaPlayer::HasAtLoginFlag }, + // {"InRandomLfgDungeon", &LuaPlayer::InRandomLfgDungeon}, // :InRandomLfgDungeon() - UNDOCUMENTED - Returns true if the player is in a random LFG dungeon + // {"HasPendingBind", &LuaPlayer::HasPendingBind}, // :HasPendingBind() - UNDOCUMENTED - Returns true if the player has a pending instance bind + { "HasAchieved", &LuaPlayer::HasAchieved }, + { "GetAchievementCriteriaProgress", &LuaPlayer::GetAchievementCriteriaProgress }, + { "SetAchievement", &LuaPlayer::SetAchievement }, + { "CanUninviteFromGroup", &LuaPlayer::CanUninviteFromGroup }, + { "IsRested", &LuaPlayer::IsRested }, + // {"CanFlyInZone", &LuaPlayer::CanFlyInZone}, // :CanFlyInZone(mapid, zone) - UNDOCUMENTED - Returns true if the player can fly in the area + // {"IsNeverVisible", &LuaPlayer::IsNeverVisible}, // :IsNeverVisible() - UNDOCUMENTED - Returns true if the player is never visible + { "IsVisibleForPlayer", &LuaPlayer::IsVisibleForPlayer }, + // {"IsUsingLfg", &LuaPlayer::IsUsingLfg}, // :IsUsingLfg() - UNDOCUMENTED - Returns true if the player is using LFG + { "HasQuestForItem", &LuaPlayer::HasQuestForItem }, + { "HasQuestForGO", &LuaPlayer::HasQuestForGO }, + { "CanShareQuest", &LuaPlayer::CanShareQuest }, + // {"HasReceivedQuestReward", &LuaPlayer::HasReceivedQuestReward}, // :HasReceivedQuestReward(entry) - UNDOCUMENTED - Returns true if the player has recieved the quest's reward + { "HasTalent", &LuaPlayer::HasTalent }, + { "IsInSameGroupWith", &LuaPlayer::IsInSameGroupWith }, + { "IsInSameRaidWith", &LuaPlayer::IsInSameRaidWith }, + { "IsGroupVisibleFor", &LuaPlayer::IsGroupVisibleFor }, + { "HasSkill", &LuaPlayer::HasSkill }, + { "IsHonorOrXPTarget", &LuaPlayer::IsHonorOrXPTarget }, + { "CanParry", &LuaPlayer::CanParry }, + { "CanBlock", &LuaPlayer::CanBlock }, + { "CanTitanGrip", &LuaPlayer::CanTitanGrip }, + { "InBattleground", &LuaPlayer::InBattleground }, + { "InArena", &LuaPlayer::InArena }, + // {"IsOutdoorPvPActive", &LuaPlayer::IsOutdoorPvPActive}, // :IsOutdoorPvPActive() - UNDOCUMENTED - Returns true if the player is outdoor pvp active + // {"IsARecruiter", &LuaPlayer::IsARecruiter}, // :IsARecruiter() - UNDOCUMENTED - Returns true if the player is a recruiter + { "CanUseItem", &LuaPlayer::CanUseItem }, + { "HasSpell", &LuaPlayer::HasSpell }, + { "HasSpellCooldown", &LuaPlayer::HasSpellCooldown }, + { "IsInWater", &LuaPlayer::IsInWater }, + { "CanFly", &LuaPlayer::CanFly }, + { "IsMoving", &LuaPlayer::IsMoving }, + { "IsFlying", &LuaPlayer::IsFlying }, + { "CanPetResurrect", &LuaPlayer::CanPetResurrect }, + { "IsExistPet", &LuaPlayer::IsExistPet }, + { "CanTameExoticPets", &LuaPlayer::CanTameExoticPets }, + { "IsPetNeedBeTemporaryUnsummoned", &LuaPlayer::IsPetNeedBeTemporaryUnsummoned }, + { "CanResummonPet", &LuaPlayer::CanResummonPet }, + { "CanSeeDKPet", &LuaPlayer::CanSeeDKPet }, + { "IsMaxLevel", &LuaPlayer::IsMaxLevel }, + { "IsDailyQuestDone", &LuaPlayer::IsDailyQuestDone }, + { "IsPvP", &LuaPlayer::IsPvP }, + { "IsFFAPvP", &LuaPlayer::IsFFAPvP }, + { "IsUsingLfg", &LuaPlayer::IsUsingLfg }, + { "InRandomLfgDungeon", &LuaPlayer::InRandomLfgDungeon }, + { "CanInteractWithQuestGiver", &LuaPlayer::CanInteractWithQuestGiver }, + { "CanSeeStartQuest", &LuaPlayer::CanSeeStartQuest }, + { "CanTakeQuest", &LuaPlayer::CanTakeQuest }, + { "CanAddQuest", &LuaPlayer::CanAddQuest }, + { "CalculateReputationGain", &LuaPlayer::CalculateReputationGain }, + { "HasTitleByIndex", &LuaPlayer::HasTitleByIndex }, + { "IsAtGroupRewardDistance", &LuaPlayer::IsAtGroupRewardDistance }, + { "IsAtLootRewardDistance", &LuaPlayer::IsAtLootRewardDistance }, + { "CanTeleport", &LuaPlayer::CanTeleport }, + { "IsSpectator", &LuaPlayer::IsSpectator }, + { "HasKnownTaxiNode", &LuaPlayer::HasKnownTaxiNode }, + { "IsBot", &LuaPlayer::IsBot }, + // { "HasSpellMod", &LuaPlayer::HasSpellMod }, + + // Gossip + { "GossipMenuAddItem", &LuaPlayer::GossipMenuAddItem }, + { "GossipSendMenu", &LuaPlayer::GossipSendMenu }, + { "GossipComplete", &LuaPlayer::GossipComplete }, + { "GossipClearMenu", &LuaPlayer::GossipClearMenu }, + + // Other + { "SendBroadcastMessage", &LuaPlayer::SendBroadcastMessage }, + { "SendAreaTriggerMessage", &LuaPlayer::SendAreaTriggerMessage }, + { "SendNotification", &LuaPlayer::SendNotification }, + { "SendPacket", &LuaPlayer::SendPacket }, + { "SendAddonMessage", &LuaPlayer::SendAddonMessage }, + { "ModifyMoney", &LuaPlayer::ModifyMoney }, + { "LearnSpell", &LuaPlayer::LearnSpell }, + { "LearnTalent", &LuaPlayer::LearnTalent }, + + { "RunCommand", &LuaPlayer::RunCommand }, + { "SetGlyph", &LuaPlayer::SetGlyph }, + { "GetGlyph", &LuaPlayer::GetGlyph }, + { "RemoveArenaSpellCooldowns", &LuaPlayer::RemoveArenaSpellCooldowns }, + { "RemoveItem", &LuaPlayer::RemoveItem }, + { "RemoveLifetimeKills", &LuaPlayer::RemoveLifetimeKills }, + { "ResurrectPlayer", &LuaPlayer::ResurrectPlayer }, + { "EquipItem", &LuaPlayer::EquipItem }, + { "ResetSpellCooldown", &LuaPlayer::ResetSpellCooldown }, + { "ResetTypeCooldowns", &LuaPlayer::ResetTypeCooldowns }, + { "ResetAllCooldowns", &LuaPlayer::ResetAllCooldowns }, + { "GiveXP", &LuaPlayer::GiveXP }, // :GiveXP(xp[, victim, pureXP, triggerHook]) - UNDOCUMENTED - Gives XP to the player. If pure is false, bonuses are count in. If triggerHook is false, GiveXp hook is not triggered. + // {"RemovePet", &LuaPlayer::RemovePet}, // :RemovePet([mode, returnreagent]) - UNDOCUMENTED - Removes the player's pet. Mode determines if the pet is saved and how + // {"SummonPet", &LuaPlayer::SummonPet}, // :SummonPet(entry, x, y, z, o, petType, despwtime) - Summons a pet for the player + { "Say", &LuaPlayer::Say }, + { "Yell", &LuaPlayer::Yell }, + { "TextEmote", &LuaPlayer::TextEmote }, + { "Whisper", &LuaPlayer::Whisper }, + { "CompleteQuest", &LuaPlayer::CompleteQuest }, + { "IncompleteQuest", &LuaPlayer::IncompleteQuest }, + { "FailQuest", &LuaPlayer::FailQuest }, + { "AddQuest", &LuaPlayer::AddQuest }, + { "RemoveQuest", &LuaPlayer::RemoveQuest }, + // {"RemoveActiveQuest", &LuaPlayer::RemoveActiveQuest}, // :RemoveActiveQuest(entry) - UNDOCUMENTED - Removes an active quest + // {"RemoveRewardedQuest", &LuaPlayer::RemoveRewardedQuest}, // :RemoveRewardedQuest(entry) - UNDOCUMENTED - Removes a rewarded quest + { "AreaExploredOrEventHappens", &LuaPlayer::AreaExploredOrEventHappens }, + { "GroupEventHappens", &LuaPlayer::GroupEventHappens }, + { "KilledMonsterCredit", &LuaPlayer::KilledMonsterCredit }, + // {"KilledPlayerCredit", &LuaPlayer::KilledPlayerCredit}, // :KilledPlayerCredit() - UNDOCUMENTED - Satisfies a player kill for the player + // {"KillGOCredit", &LuaPlayer::KillGOCredit}, // :KillGOCredit(GOEntry[, GUID]) - UNDOCUMENTED - Credits the player for destroying a GO, guid is optional + { "TalkedToCreature", &LuaPlayer::TalkedToCreature }, + { "ResetPetTalents", &LuaPlayer::ResetPetTalents }, + { "AddComboPoints", &LuaPlayer::AddComboPoints }, + // {"GainSpellComboPoints", &LuaPlayer::GainSpellComboPoints}, // :GainSpellComboPoints(amount) - UNDOCUMENTED - Player gains spell combo points + { "ClearComboPoints", &LuaPlayer::ClearComboPoints }, + { "RemoveSpell", &LuaPlayer::RemoveSpell }, + { "ResetTalents", &LuaPlayer::ResetTalents }, + { "ResetTalentsCost", &LuaPlayer::ResetTalentsCost }, + // {"AddTalent", &LuaPlayer::AddTalent}, // :AddTalent(spellid, spec, learning) - UNDOCUMENTED - Adds a talent spell for the player to given spec + { "RemoveFromGroup", &LuaPlayer::RemoveFromGroup }, + { "KillPlayer", &LuaPlayer::KillPlayer }, + { "DurabilityLossAll", &LuaPlayer::DurabilityLossAll }, + { "DurabilityLoss", &LuaPlayer::DurabilityLoss }, + { "DurabilityPointsLoss", &LuaPlayer::DurabilityPointsLoss }, + { "DurabilityPointsLossAll", &LuaPlayer::DurabilityPointsLossAll }, + { "DurabilityPointLossForEquipSlot", &LuaPlayer::DurabilityPointLossForEquipSlot }, + { "DurabilityRepairAll", &LuaPlayer::DurabilityRepairAll }, + { "DurabilityRepair", &LuaPlayer::DurabilityRepair }, + { "ModifyHonorPoints", &LuaPlayer::ModifyHonorPoints }, + { "ModifyArenaPoints", &LuaPlayer::ModifyArenaPoints }, + { "LeaveBattleground", &LuaPlayer::LeaveBattleground }, + // {"BindToInstance", &LuaPlayer::BindToInstance}, // :BindToInstance() - UNDOCUMENTED - Binds the player to the current instance + { "UnbindInstance", &LuaPlayer::UnbindInstance }, + { "UnbindAllInstances", &LuaPlayer::UnbindAllInstances }, + { "RemoveFromBattlegroundRaid", &LuaPlayer::RemoveFromBattlegroundRaid }, + { "ResetAchievements", &LuaPlayer::ResetAchievements }, + { "KickPlayer", &LuaPlayer::KickPlayer }, + { "LogoutPlayer", &LuaPlayer::LogoutPlayer }, + { "SendTrainerList", &LuaPlayer::SendTrainerList }, + { "SendListInventory", &LuaPlayer::SendListInventory }, + { "SendShowBank", &LuaPlayer::SendShowBank }, + { "SendTabardVendorActivate", &LuaPlayer::SendTabardVendorActivate }, + { "SendSpiritResurrect", &LuaPlayer::SendSpiritResurrect }, + { "SendTaxiMenu", &LuaPlayer::SendTaxiMenu }, + { "SendUpdateWorldState", &LuaPlayer::SendUpdateWorldState }, + { "RewardQuest", &LuaPlayer::RewardQuest }, + { "SendAuctionMenu", &LuaPlayer::SendAuctionMenu }, + { "SendShowMailBox", &LuaPlayer::SendShowMailBox }, + { "StartTaxi", &LuaPlayer::StartTaxi }, + { "GossipSendPOI", &LuaPlayer::GossipSendPOI }, + { "GossipAddQuests", &LuaPlayer::GossipAddQuests }, + { "SendQuestTemplate", &LuaPlayer::SendQuestTemplate }, + { "SpawnBones", &LuaPlayer::SpawnBones }, + { "RemovedInsignia", &LuaPlayer::RemovedInsignia }, + { "SendGuildInvite", &LuaPlayer::SendGuildInvite }, + { "Mute", &LuaPlayer::Mute }, + { "SummonPlayer", &LuaPlayer::SummonPlayer }, + { "SaveToDB", &LuaPlayer::SaveToDB }, + { "GroupInvite", &LuaPlayer::GroupInvite }, + { "GroupCreate", &LuaPlayer::GroupCreate }, + { "SendCinematicStart", &LuaPlayer::SendCinematicStart }, + { "SendMovieStart", &LuaPlayer::SendMovieStart }, + { "UpdatePlayerSetting", &LuaPlayer::UpdatePlayerSetting }, + { "TeleportTo", &LuaPlayer::TeleportTo }, + { "SummonPet", &LuaPlayer::SummonPet }, + { "CreatePet", &LuaPlayer::CreatePet }, + { "UnsummonPetTemporarily", &LuaPlayer::UnsummonPetTemporarily }, + { "RemovePet", &LuaPlayer::RemovePet }, + { "ResetPetTalents", &LuaPlayer::ResetPetTalents }, + { "LearnPetTalent", &LuaPlayer::LearnPetTalent }, + { "ResummonPetTemporaryUnSummonedIfAny", &LuaPlayer::ResummonPetTemporaryUnSummonedIfAny }, + { "SetPlayerFlag", &LuaPlayer::SetPlayerFlag }, + { "RemovePlayerFlag", &LuaPlayer::RemovePlayerFlag }, + { "DoRandomRoll", &LuaPlayer::DoRandomRoll }, + { "EnvironmentalDamage", &LuaPlayer::EnvironmentalDamage }, + { "InitTaxiNodesForLevel", &LuaPlayer::InitTaxiNodesForLevel }, + { "AbandonQuest", &LuaPlayer::AbandonQuest }, + { "AddWeaponProficiency", &LuaPlayer::AddWeaponProficiency }, + { "AddArmorProficiency", &LuaPlayer::AddArmorProficiency }, + { "SetAmmo", &LuaPlayer::SetAmmo }, + { "RemoveAmmo", &LuaPlayer::RemoveAmmo }, + { "SetCanTeleport", &LuaPlayer::SetCanTeleport }, + { "SetIsSpectator", &LuaPlayer::SetIsSpectator }, + { "SetViewpoint", &LuaPlayer::SetViewpoint }, + { "ToggleInstantFlight", &LuaPlayer::ToggleInstantFlight }, + { "SetCreationTime", &LuaPlayer::SetCreationTime }, + { "ApplyRatingMod", &LuaPlayer::ApplyRatingMod }, + + { NULL, NULL } +}; + +ALERegister CreatureMethods[] = +{ + // Getters + { "GetAITarget", &LuaCreature::GetAITarget }, + { "GetAITargets", &LuaCreature::GetAITargets }, + { "GetAITargetsCount", &LuaCreature::GetAITargetsCount }, + { "GetHomePosition", &LuaCreature::GetHomePosition }, + { "GetCorpseDelay", &LuaCreature::GetCorpseDelay }, + { "GetCreatureSpellCooldownDelay", &LuaCreature::GetCreatureSpellCooldownDelay }, + { "GetScriptId", &LuaCreature::GetScriptId }, + { "GetAIName", &LuaCreature::GetAIName }, + { "GetScriptName", &LuaCreature::GetScriptName }, + { "GetAggroRange", &LuaCreature::GetAggroRange }, + { "GetDefaultMovementType", &LuaCreature::GetDefaultMovementType }, + { "GetRespawnDelay", &LuaCreature::GetRespawnDelay }, + { "GetWanderRadius", &LuaCreature::GetWanderRadius }, + { "GetCurrentWaypointId", &LuaCreature::GetCurrentWaypointId }, + { "GetSpawnId", &LuaCreature::GetSpawnId }, + { "GetWaypointPath", &LuaCreature::GetWaypointPath }, + { "GetLootMode", &LuaCreature::GetLootMode }, + { "GetLootRecipient", &LuaCreature::GetLootRecipient }, + { "GetLootRecipientGroup", &LuaCreature::GetLootRecipientGroup }, + { "GetNPCFlags", &LuaCreature::GetNPCFlags }, + { "GetUnitFlags", &LuaCreature::GetUnitFlags }, + { "GetUnitFlagsTwo", &LuaCreature::GetUnitFlagsTwo }, + { "GetExtraFlags", &LuaCreature::GetExtraFlags }, + { "GetRank", &LuaCreature::GetRank }, + { "GetShieldBlockValue", &LuaCreature::GetShieldBlockValue }, + { "GetDBTableGUIDLow", &LuaCreature::GetDBTableGUIDLow }, + { "GetCreatureFamily", &LuaCreature::GetCreatureFamily }, + { "GetReactState", &LuaCreature::GetReactState }, + { "GetLoot", &LuaCreature::GetLoot }, + + // Setters + { "SetRegeneratingHealth", &LuaCreature::SetRegeneratingHealth }, + { "SetHover", &LuaCreature::SetHover }, + { "SetDisableGravity", &LuaCreature::SetDisableGravity }, + { "SetAggroEnabled", &LuaCreature::SetAggroEnabled }, + { "SetCorpseDelay", &LuaCreature::SetCorpseDelay }, + { "SetNoCallAssistance", &LuaCreature::SetNoCallAssistance }, + { "SetNoSearchAssistance", &LuaCreature::SetNoSearchAssistance }, + { "SetDefaultMovementType", &LuaCreature::SetDefaultMovementType }, + { "SetRespawnDelay", &LuaCreature::SetRespawnDelay }, + { "SetWanderRadius", &LuaCreature::SetWanderRadius }, + { "SetInCombatWithZone", &LuaCreature::SetInCombatWithZone }, + { "SetDisableReputationGain", &LuaCreature::SetDisableReputationGain }, + { "SetLootMode", &LuaCreature::SetLootMode }, + { "SetNPCFlags", &LuaCreature::SetNPCFlags }, + { "SetUnitFlags", &LuaCreature::SetUnitFlags }, + { "SetUnitFlagsTwo", &LuaCreature::SetUnitFlagsTwo }, + { "SetReactState", &LuaCreature::SetReactState }, + { "SetDeathState", &LuaCreature::SetDeathState }, + { "SetWalk", &LuaCreature::SetWalk }, + { "SetHomePosition", &LuaCreature::SetHomePosition }, + { "SetEquipmentSlots", &LuaCreature::SetEquipmentSlots }, + + // Boolean + { "IsRegeneratingHealth", &LuaCreature::IsRegeneratingHealth }, + { "IsDungeonBoss", &LuaCreature::IsDungeonBoss }, + { "IsWorldBoss", &LuaCreature::IsWorldBoss }, + { "IsRacialLeader", &LuaCreature::IsRacialLeader }, + { "IsCivilian", &LuaCreature::IsCivilian }, + { "IsTrigger", &LuaCreature::IsTrigger }, + { "IsGuard", &LuaCreature::IsGuard }, + { "IsElite", &LuaCreature::IsElite }, + { "IsInEvadeMode", &LuaCreature::IsInEvadeMode }, + { "HasCategoryCooldown", &LuaCreature::HasCategoryCooldown }, + { "CanWalk", &LuaCreature::CanWalk }, + { "CanSwim", &LuaCreature::CanSwim }, + { "CanAggro", &LuaCreature::CanAggro }, + { "CanStartAttack", &LuaCreature::CanStartAttack }, + { "HasSearchedAssistance", &LuaCreature::HasSearchedAssistance }, + { "IsTappedBy", &LuaCreature::IsTappedBy }, + { "HasLootRecipient", &LuaCreature::HasLootRecipient }, + { "CanAssistTo", &LuaCreature::CanAssistTo }, + { "IsTargetableForAttack", &LuaCreature::IsTargetableForAttack }, + { "CanCompleteQuest", &LuaCreature::CanCompleteQuest }, + { "IsReputationGainDisabled", &LuaCreature::IsReputationGainDisabled }, + { "IsDamageEnoughForLootingAndReward", &LuaCreature::IsDamageEnoughForLootingAndReward }, + { "HasLootMode", &LuaCreature::HasLootMode }, + { "HasSpell", &LuaCreature::HasSpell }, + { "HasQuest", &LuaCreature::HasQuest }, + { "HasSpellCooldown", &LuaCreature::HasSpellCooldown }, + { "CanFly", &LuaCreature::CanFly }, + + // Other + { "FleeToGetAssistance", &LuaCreature::FleeToGetAssistance }, + { "CallForHelp", &LuaCreature::CallForHelp }, + { "CallAssistance", &LuaCreature::CallAssistance }, + { "RemoveCorpse", &LuaCreature::RemoveCorpse }, + { "AllLootRemovedFromCorpse", &LuaCreature::AllLootRemovedFromCorpse }, + { "DespawnOrUnsummon", &LuaCreature::DespawnOrUnsummon }, + { "Respawn", &LuaCreature::Respawn }, + { "AttackStart", &LuaCreature::AttackStart }, + { "AddLootMode", &LuaCreature::AddLootMode }, + { "ResetLootMode", &LuaCreature::ResetLootMode }, + { "RemoveLootMode", &LuaCreature::RemoveLootMode }, + { "SaveToDB", &LuaCreature::SaveToDB }, + { "SelectVictim", &LuaCreature::SelectVictim }, + { "MoveWaypoint", &LuaCreature::MoveWaypoint }, + { "UpdateEntry", &LuaCreature::UpdateEntry }, + + { NULL, NULL } +}; + +ALERegister GameObjectMethods[] = +{ + // Getters + { "GetDisplayId", &LuaGameObject::GetDisplayId }, + { "GetGoState", &LuaGameObject::GetGoState }, + { "GetLootState", &LuaGameObject::GetLootState }, + { "GetLootRecipient", &LuaGameObject::GetLootRecipient }, + { "GetLootRecipientGroup", &LuaGameObject::GetLootRecipientGroup }, + { "GetSpawnId", &LuaGameObject::GetSpawnId }, + + // Setters + { "SetGoState", &LuaGameObject::SetGoState }, + { "SetLootState", &LuaGameObject::SetLootState }, + { "SetRespawnTime", &LuaGameObject::SetRespawnTime }, + { "SetRespawnDelay", &LuaGameObject::SetRespawnDelay }, + + // Boolean + { "IsTransport", &LuaGameObject::IsTransport }, + // {"IsDestructible", &LuaGameObject::IsDestructible}, // :IsDestructible() - UNDOCUMENTED + { "IsActive", &LuaGameObject::IsActive }, + { "HasQuest", &LuaGameObject::HasQuest }, + { "IsSpawned", &LuaGameObject::IsSpawned }, + + // Other + { "RemoveFromWorld", &LuaGameObject::RemoveFromWorld }, + { "UseDoorOrButton", &LuaGameObject::UseDoorOrButton }, + { "Despawn", &LuaGameObject::Despawn }, + { "Respawn", &LuaGameObject::Respawn }, + { "SaveToDB", &LuaGameObject::SaveToDB }, + { "AddLoot", &LuaGameObject::AddLoot }, + + { NULL, NULL } +}; + +ALERegister ItemMethods[] = +{ + // Getters + { "GetOwnerGUID", &LuaItem::GetOwnerGUID }, + { "GetOwner", &LuaItem::GetOwner }, + { "GetCount", &LuaItem::GetCount }, + { "GetMaxStackCount", &LuaItem::GetMaxStackCount }, + { "GetSlot", &LuaItem::GetSlot }, + { "GetBagSlot", &LuaItem::GetBagSlot }, + { "GetEnchantmentId", &LuaItem::GetEnchantmentId }, + { "GetSpellId", &LuaItem::GetSpellId }, + { "GetSpellTrigger", &LuaItem::GetSpellTrigger }, + { "GetItemLink", &LuaItem::GetItemLink }, + { "GetClass", &LuaItem::GetClass }, + { "GetSubClass", &LuaItem::GetSubClass }, + { "GetName", &LuaItem::GetName }, + { "GetDisplayId", &LuaItem::GetDisplayId }, + { "GetQuality", &LuaItem::GetQuality }, + { "GetBuyCount", &LuaItem::GetBuyCount }, + { "GetBuyPrice", &LuaItem::GetBuyPrice }, + { "GetSellPrice", &LuaItem::GetSellPrice }, + { "GetInventoryType", &LuaItem::GetInventoryType }, + { "GetAllowableClass", &LuaItem::GetAllowableClass }, + { "GetAllowableRace", &LuaItem::GetAllowableRace }, + { "GetItemLevel", &LuaItem::GetItemLevel }, + { "GetRequiredLevel", &LuaItem::GetRequiredLevel }, + { "GetStatsCount", &LuaItem::GetStatsCount }, + { "GetRandomProperty", &LuaItem::GetRandomProperty }, + { "GetRandomSuffix", &LuaItem::GetRandomSuffix }, + { "GetItemSet", &LuaItem::GetItemSet }, + { "GetBagSize", &LuaItem::GetBagSize }, + { "GetItemTemplate", &LuaItem::GetItemTemplate }, + + // Setters + { "SetOwner", &LuaItem::SetOwner }, + { "SetBinding", &LuaItem::SetBinding }, + { "SetCount", &LuaItem::SetCount }, + { "SetRandomProperty", &LuaItem::SetRandomProperty }, + { "SetRandomSuffix", &LuaItem::SetRandomSuffix }, + + // Boolean + { "IsSoulBound", &LuaItem::IsSoulBound }, + { "IsBoundAccountWide", &LuaItem::IsBoundAccountWide }, + { "IsBoundByEnchant", &LuaItem::IsBoundByEnchant }, + { "IsNotBoundToPlayer", &LuaItem::IsNotBoundToPlayer }, + { "IsLocked", &LuaItem::IsLocked }, + { "IsBag", &LuaItem::IsBag }, + { "IsCurrencyToken", &LuaItem::IsCurrencyToken }, + { "IsNotEmptyBag", &LuaItem::IsNotEmptyBag }, + { "IsBroken", &LuaItem::IsBroken }, + { "CanBeTraded", &LuaItem::CanBeTraded }, + { "IsInTrade", &LuaItem::IsInTrade }, + { "IsInBag", &LuaItem::IsInBag }, + { "IsEquipped", &LuaItem::IsEquipped }, + { "HasQuest", &LuaItem::HasQuest }, + { "IsPotion", &LuaItem::IsPotion }, + { "IsWeaponVellum", &LuaItem::IsWeaponVellum }, + { "IsArmorVellum", &LuaItem::IsArmorVellum }, + { "IsConjuredConsumable", &LuaItem::IsConjuredConsumable }, + //{"IsRefundExpired", &LuaItem::IsRefundExpired}, // :IsRefundExpired() - UNDOCUMENTED - Returns true if the item's refund time has expired + { "SetEnchantment", &LuaItem::SetEnchantment }, + { "ClearEnchantment", &LuaItem::ClearEnchantment }, + + // Other + { "SaveToDB", &LuaItem::SaveToDB }, + + { NULL, NULL } +}; + +ALERegister ItemTemplateMethods[] = +{ + { "GetItemId", &LuaItemTemplate::GetItemId }, + { "GetClass", &LuaItemTemplate::GetClass }, + { "GetSubClass", &LuaItemTemplate::GetSubClass }, + { "GetName", &LuaItemTemplate::GetName }, + { "GetDisplayId", &LuaItemTemplate::GetDisplayId }, + { "GetQuality", &LuaItemTemplate::GetQuality }, + { "GetFlags", &LuaItemTemplate::GetFlags }, + { "GetExtraFlags", &LuaItemTemplate::GetExtraFlags }, + { "GetBuyCount", &LuaItemTemplate::GetBuyCount }, + { "GetBuyPrice", &LuaItemTemplate::GetBuyPrice }, + { "GetSellPrice", &LuaItemTemplate::GetSellPrice }, + { "GetInventoryType", &LuaItemTemplate::GetInventoryType }, + { "GetAllowableClass", &LuaItemTemplate::GetAllowableClass }, + { "GetAllowableRace", &LuaItemTemplate::GetAllowableRace }, + { "GetItemLevel", &LuaItemTemplate::GetItemLevel }, + { "GetRequiredLevel", &LuaItemTemplate::GetRequiredLevel }, + { "GetIcon", &LuaItemTemplate::GetIcon }, + { NULL, NULL } +}; + +ALERegister AuraMethods[] = +{ + // Getters + { "GetCaster", &LuaAura::GetCaster }, + { "GetCasterGUID", &LuaAura::GetCasterGUID }, + { "GetCasterLevel", &LuaAura::GetCasterLevel }, + { "GetDuration", &LuaAura::GetDuration }, + { "GetMaxDuration", &LuaAura::GetMaxDuration }, + { "GetAuraId", &LuaAura::GetAuraId }, + { "GetStackAmount", &LuaAura::GetStackAmount }, + { "GetOwner", &LuaAura::GetOwner }, + + // Setters + { "SetDuration", &LuaAura::SetDuration }, + { "SetMaxDuration", &LuaAura::SetMaxDuration }, + { "SetStackAmount", &LuaAura::SetStackAmount }, + + // Other + { "Remove", &LuaAura::Remove }, + + { NULL, NULL } +}; + +ALERegister SpellMethods[] = +{ + // Getters + { "GetCaster", &LuaSpell::GetCaster }, + { "GetCastTime", &LuaSpell::GetCastTime }, + { "GetEntry", &LuaSpell::GetEntry }, + { "GetDuration", &LuaSpell::GetDuration }, + { "GetPowerCost", &LuaSpell::GetPowerCost }, + { "GetReagentCost", &LuaSpell::GetReagentCost }, + { "GetTargetDest", &LuaSpell::GetTargetDest }, + { "GetTarget", &LuaSpell::GetTarget }, + + // Setters + { "SetAutoRepeat", &LuaSpell::SetAutoRepeat }, + + // Boolean + { "IsAutoRepeat", &LuaSpell::IsAutoRepeat }, + + // Other + { "Cancel", &LuaSpell::Cancel }, + { "Cast", &LuaSpell::Cast }, + { "Finish", &LuaSpell::Finish }, + + { NULL, NULL } +}; + +ALERegister QuestMethods[] = +{ + // Getters + { "GetId", &LuaQuest::GetId }, + { "GetLevel", &LuaQuest::GetLevel }, + // {"GetMaxLevel", &LuaQuest::GetMaxLevel}, // :GetMaxLevel() - UNDOCUMENTED - Returns the quest's max level + { "GetMinLevel", &LuaQuest::GetMinLevel }, + { "GetNextQuestId", &LuaQuest::GetNextQuestId }, + { "GetPrevQuestId", &LuaQuest::GetPrevQuestId }, + { "GetNextQuestInChain", &LuaQuest::GetNextQuestInChain }, + { "GetFlags", &LuaQuest::GetFlags }, + { "GetType", &LuaQuest::GetType }, + + // Boolean + { "HasFlag", &LuaQuest::HasFlag }, + { "IsDaily", &LuaQuest::IsDaily }, + { "IsRepeatable", &LuaQuest::IsRepeatable }, + + { NULL, NULL } +}; + +ALERegister GroupMethods[] = +{ + // Getters + { "GetMembers", &LuaGroup::GetMembers }, + { "GetLeaderGUID", &LuaGroup::GetLeaderGUID }, + { "GetGUID", &LuaGroup::GetGUID }, + { "GetMemberGroup", &LuaGroup::GetMemberGroup }, + { "GetMemberGUID", &LuaGroup::GetMemberGUID }, + { "GetMembersCount", &LuaGroup::GetMembersCount }, + { "GetGroupType", &LuaGroup::GetGroupType }, + + // Setters + { "SetLeader", &LuaGroup::SetLeader }, + { "SetMembersGroup", &LuaGroup::SetMembersGroup }, + { "SetTargetIcon", &LuaGroup::SetTargetIcon }, + { "SetMemberFlag", &LuaGroup::SetMemberFlag }, + + // Boolean + { "IsLeader", &LuaGroup::IsLeader }, + { "AddMember", &LuaGroup::AddMember }, + { "RemoveMember", &LuaGroup::RemoveMember }, + { "Disband", &LuaGroup::Disband }, + { "IsFull", &LuaGroup::IsFull }, + { "IsLFGGroup", &LuaGroup::IsLFGGroup }, + { "IsRaidGroup", &LuaGroup::IsRaidGroup }, + { "IsBGGroup", &LuaGroup::IsBGGroup }, + // {"IsBFGroup", &LuaGroup::IsBFGroup}, // :IsBFGroup() - UNDOCUMENTED - Returns true if the group is a battlefield group + { "IsMember", &LuaGroup::IsMember }, + { "IsAssistant", &LuaGroup::IsAssistant }, + { "SameSubGroup", &LuaGroup::SameSubGroup }, + { "HasFreeSlotSubGroup", &LuaGroup::HasFreeSlotSubGroup }, + + // Other + { "SendPacket", &LuaGroup::SendPacket }, + // {"ConvertToLFG", &LuaGroup::ConvertToLFG}, // :ConvertToLFG() - UNDOCUMENTED - Converts the group to an LFG group + { "ConvertToRaid", &LuaGroup::ConvertToRaid }, + + { NULL, NULL } +}; + +ALERegister GuildMethods[] = +{ + // Getters + { "GetMembers", &LuaGuild::GetMembers }, + { "GetLeader", &LuaGuild::GetLeader }, + { "GetLeaderGUID", &LuaGuild::GetLeaderGUID }, + { "GetId", &LuaGuild::GetId }, + { "GetName", &LuaGuild::GetName }, + { "GetMOTD", &LuaGuild::GetMOTD }, + { "GetInfo", &LuaGuild::GetInfo }, + { "GetMemberCount", &LuaGuild::GetMemberCount }, + { "GetCreatedDate", &LuaGuild::GetCreatedDate }, + { "GetTotalBankMoney", &LuaGuild::GetTotalBankMoney }, + + // Setters + { "SetBankTabText", &LuaGuild::SetBankTabText }, + { "SetMemberRank", &LuaGuild::SetMemberRank }, + { "SetLeader", &LuaGuild::SetLeader }, + { "SetName", &LuaGuild::SetName }, + + // Other + { "SendPacket", &LuaGuild::SendPacket }, + { "SendPacketToRanked", &LuaGuild::SendPacketToRanked }, + { "Disband", &LuaGuild::Disband }, + { "AddMember", &LuaGuild::AddMember }, + { "DeleteMember", &LuaGuild::DeleteMember }, + { "SendMessage", &LuaGuild::SendMessage }, + { "UpdateMemberData", &LuaGuild::UpdateMemberData }, + { "MassInviteToEvent", &LuaGuild::MassInviteToEvent }, + { "SwapItems", &LuaGuild::SwapItems }, + { "SwapItemsWithInventory", &LuaGuild::SwapItemsWithInventory }, + { "ResetTimes", &LuaGuild::ResetTimes }, + { "ModifyBankMoney", &LuaGuild::ModifyBankMoney }, + + { NULL, NULL } +}; + +ALERegister VehicleMethods[] = +{ + // Getters + { "GetOwner", &LuaVehicle::GetOwner }, + { "GetEntry", &LuaVehicle::GetEntry }, + { "GetPassenger", &LuaVehicle::GetPassenger }, + + // Boolean + { "IsOnBoard", &LuaVehicle::IsOnBoard }, + + // Other + { "AddPassenger", &LuaVehicle::AddPassenger }, + { "RemovePassenger", &LuaVehicle::RemovePassenger }, + + { NULL, NULL } +}; + +ALERegister QueryMethods[] = +{ + // Getters + { "GetColumnCount", &LuaQuery::GetColumnCount }, + { "GetRowCount", &LuaQuery::GetRowCount }, + { "GetRow", &LuaQuery::GetRow }, + { "GetBool", &LuaQuery::GetBool }, + { "GetUInt8", &LuaQuery::GetUInt8 }, + { "GetUInt16", &LuaQuery::GetUInt16 }, + { "GetUInt32", &LuaQuery::GetUInt32 }, + { "GetUInt64", &LuaQuery::GetUInt64 }, + { "GetInt8", &LuaQuery::GetInt8 }, + { "GetInt16", &LuaQuery::GetInt16 }, + { "GetInt32", &LuaQuery::GetInt32 }, + { "GetInt64", &LuaQuery::GetInt64 }, + { "GetFloat", &LuaQuery::GetFloat }, + { "GetDouble", &LuaQuery::GetDouble }, + { "GetString", &LuaQuery::GetString }, + + // Boolean + { "NextRow", &LuaQuery::NextRow }, + { "IsNull", &LuaQuery::IsNull }, + + { NULL, NULL } +}; + +ALERegister PacketMethods[] = +{ + // Getters + { "GetOpcode", &LuaPacket::GetOpcode }, + { "GetSize", &LuaPacket::GetSize }, + + // Setters + { "SetOpcode", &LuaPacket::SetOpcode }, + + // Readers + { "ReadByte", &LuaPacket::ReadByte }, + { "ReadUByte", &LuaPacket::ReadUByte }, + { "ReadShort", &LuaPacket::ReadShort }, + { "ReadUShort", &LuaPacket::ReadUShort }, + { "ReadLong", &LuaPacket::ReadLong }, + { "ReadULong", &LuaPacket::ReadULong }, + { "ReadGUID", &LuaPacket::ReadGUID }, + { "ReadPackedGUID", &LuaPacket::ReadPackedGUID }, + { "ReadString", &LuaPacket::ReadString }, + { "ReadFloat", &LuaPacket::ReadFloat }, + { "ReadDouble", &LuaPacket::ReadDouble }, + + // Writers + { "WriteByte", &LuaPacket::WriteByte }, + { "WriteUByte", &LuaPacket::WriteUByte }, + { "WriteShort", &LuaPacket::WriteShort }, + { "WriteUShort", &LuaPacket::WriteUShort }, + { "WriteLong", &LuaPacket::WriteLong }, + { "WriteULong", &LuaPacket::WriteULong }, + { "WriteGUID", &LuaPacket::WriteGUID }, + { "WritePackedGUID", &LuaPacket::WritePackedGUID }, + { "WriteString", &LuaPacket::WriteString }, + { "WriteFloat", &LuaPacket::WriteFloat }, + { "WriteDouble", &LuaPacket::WriteDouble }, + + { NULL, NULL } +}; + +ALERegister MapMethods[] = +{ + // Getters + { "GetName", &LuaMap::GetName }, + { "GetDifficulty", &LuaMap::GetDifficulty }, + { "GetInstanceId", &LuaMap::GetInstanceId }, + { "GetInstanceData", &LuaMap::GetInstanceData }, + { "GetPlayerCount", &LuaMap::GetPlayerCount }, + { "GetPlayers", &LuaMap::GetPlayers }, + { "GetMapId", &LuaMap::GetMapId }, + { "GetAreaId", &LuaMap::GetAreaId }, + { "GetHeight", &LuaMap::GetHeight }, + { "GetWorldObject", &LuaMap::GetWorldObject }, + { "GetCreatures", &LuaMap::GetCreatures }, + { "GetCreaturesByAreaId", &LuaMap::GetCreaturesByAreaId }, + { "GetTransports", &LuaMap::GetTransports }, + + + // Setters + { "SetWeather", &LuaMap::SetWeather }, + + // Boolean + { "IsArena", &LuaMap::IsArena }, + { "IsBattleground", &LuaMap::IsBattleground }, + { "IsDungeon", &LuaMap::IsDungeon }, + { "IsEmpty", &LuaMap::IsEmpty }, + { "IsHeroic", &LuaMap::IsHeroic }, + { "IsRaid", &LuaMap::IsRaid }, + + // Other + { "SaveInstanceData", &LuaMap::SaveInstanceData }, + + { NULL, NULL } +}; + +ALERegister CorpseMethods[] = +{ + // Getters + { "GetOwnerGUID", &LuaCorpse::GetOwnerGUID }, + { "GetGhostTime", &LuaCorpse::GetGhostTime }, + { "GetType", &LuaCorpse::GetType }, + + // Other + { "ResetGhostTime", &LuaCorpse::ResetGhostTime }, + { "SaveToDB", &LuaCorpse::SaveToDB }, + + { NULL, NULL } +}; + +ALERegister AuctionMethods[] = +{ + { NULL, NULL } +}; + +ALERegister BattleGroundMethods[] = +{ + // Getters + { "GetName", &LuaBattleGround::GetName }, + { "GetAlivePlayersCountByTeam", &LuaBattleGround::GetAlivePlayersCountByTeam }, + { "GetMap", &LuaBattleGround::GetMap }, + { "GetBonusHonorFromKillCount", &LuaBattleGround::GetBonusHonorFromKillCount }, + { "GetEndTime", &LuaBattleGround::GetEndTime }, + { "GetFreeSlotsForTeam", &LuaBattleGround::GetFreeSlotsForTeam }, + { "GetInstanceId", &LuaBattleGround::GetInstanceId }, + { "GetMapId", &LuaBattleGround::GetMapId }, + { "GetTypeId", &LuaBattleGround::GetTypeId }, + { "GetMaxLevel", &LuaBattleGround::GetMaxLevel }, + { "GetMinLevel", &LuaBattleGround::GetMinLevel }, + { "GetMaxPlayers", &LuaBattleGround::GetMaxPlayers }, + { "GetMinPlayers", &LuaBattleGround::GetMinPlayers }, + { "GetMaxPlayersPerTeam", &LuaBattleGround::GetMaxPlayersPerTeam }, + { "GetMinPlayersPerTeam", &LuaBattleGround::GetMinPlayersPerTeam }, + { "GetWinner", &LuaBattleGround::GetWinner }, + { "GetStatus", &LuaBattleGround::GetStatus }, + + { NULL, NULL } +}; + +ALERegister ChatHandlerMethods[] = +{ + { "SendSysMessage", &LuaChatHandler::SendSysMessage }, + { "IsConsole", &LuaChatHandler::IsConsole }, + { "GetPlayer", &LuaChatHandler::GetPlayer }, + { "SendGlobalSysMessage", &LuaChatHandler::SendGlobalSysMessage }, + { "SendGlobalGMSysMessage", &LuaChatHandler::SendGlobalGMSysMessage }, + { "HasLowerSecurity", &LuaChatHandler::HasLowerSecurity }, + { "HasLowerSecurityAccount", &LuaChatHandler::HasLowerSecurityAccount }, + { "GetSelectedPlayer", &LuaChatHandler::GetSelectedPlayer }, + { "GetSelectedCreature", &LuaChatHandler::GetSelectedCreature }, + { "GetSelectedUnit", &LuaChatHandler::GetSelectedUnit }, + { "GetSelectedObject", &LuaChatHandler::GetSelectedObject }, + { "GetSelectedPlayerOrSelf", &LuaChatHandler::GetSelectedPlayerOrSelf }, + { "IsAvailable", &LuaChatHandler::IsAvailable }, + { "HasSentErrorMessage", &LuaChatHandler::HasSentErrorMessage }, + + { NULL, NULL } +}; + +ALERegister AchievementMethods[] = +{ + { "GetId", &LuaAchievement::GetId }, + { "GetName", &LuaAchievement::GetName }, + + { NULL, NULL } +}; + +ALERegister RollMethods[] = +{ + { "GetItemGUID", &LuaRoll::GetItemGUID }, + { "GetItemId", &LuaRoll::GetItemId }, + { "GetItemRandomPropId", &LuaRoll::GetItemRandomPropId }, + { "GetItemRandomSuffix", &LuaRoll::GetItemRandomSuffix }, + { "GetItemCount", &LuaRoll::GetItemCount }, + { "GetPlayerVote", &LuaRoll::GetPlayerVote }, + { "GetPlayerVoteGUIDs", &LuaRoll::GetPlayerVoteGUIDs }, + { "GetTotalPlayersRolling", &LuaRoll::GetTotalPlayersRolling }, + { "GetTotalNeed", &LuaRoll::GetTotalNeed }, + { "GetTotalGreed", &LuaRoll::GetTotalGreed }, + { "GetTotalPass", &LuaRoll::GetTotalPass }, + { "GetItemSlot", &LuaRoll::GetItemSlot }, + { "GetRollVoteMask", &LuaRoll::GetRollVoteMask }, + + { NULL, NULL } +}; + +ALERegister TicketMethods[] = +{ + { "IsClosed", &LuaTicket::IsClosed }, + { "IsCompleted", &LuaTicket::IsCompleted }, + { "IsFromPlayer", &LuaTicket::IsFromPlayer }, + { "IsAssigned", &LuaTicket::IsAssigned }, + { "IsAssignedTo", &LuaTicket::IsAssignedTo }, + { "IsAssignedNotTo", &LuaTicket::IsAssignedNotTo }, + + { "GetId", &LuaTicket::GetId }, + { "GetPlayer", &LuaTicket::GetPlayer }, + { "GetPlayerName", &LuaTicket::GetPlayerName }, + { "GetMessage", &LuaTicket::GetMessage }, + { "GetAssignedPlayer", &LuaTicket::GetAssignedPlayer }, + { "GetAssignedToGUID", &LuaTicket::GetAssignedToGUID }, + { "GetLastModifiedTime", &LuaTicket::GetLastModifiedTime }, + { "GetResponse", &LuaTicket::GetResponse }, + { "GetChatLog", &LuaTicket::GetChatLog }, + + { "SetAssignedTo", &LuaTicket::SetAssignedTo }, + { "SetResolvedBy", &LuaTicket::SetResolvedBy }, + { "SetCompleted", &LuaTicket::SetCompleted }, + { "SetMessage", &LuaTicket::SetMessage }, + { "SetComment", &LuaTicket::SetComment }, + { "SetViewed", &LuaTicket::SetViewed }, + { "SetUnassigned", &LuaTicket::SetUnassigned }, + { "SetPosition", &LuaTicket::SetPosition }, + { "AppendResponse", &LuaTicket::AppendResponse }, + { "DeleteResponse", &LuaTicket::DeleteResponse }, + + { NULL, NULL } +}; + +ALERegister SpellInfoMethods[] = +{ + // Getters + { "GetAttributes", &LuaSpellInfo::GetAttributes }, + { "GetCategory", &LuaSpellInfo::GetCategory }, + { "GetName", &LuaSpellInfo::GetName }, + { "CheckShapeshift", &LuaSpellInfo::CheckShapeshift }, + { "CheckLocation", &LuaSpellInfo::CheckLocation }, + { "CheckTarget", &LuaSpellInfo::CheckTarget }, + { "CheckExplicitTarget", &LuaSpellInfo::CheckExplicitTarget }, + { "CheckTargetCreatureType", &LuaSpellInfo::CheckTargetCreatureType }, + { "CheckTargetCreatureType", &LuaSpellInfo::CheckTargetCreatureType }, + { "GetSchoolMask", &LuaSpellInfo::GetSchoolMask }, + { "GetAllEffectsMechanicMask", &LuaSpellInfo::GetAllEffectsMechanicMask }, + { "GetEffectMechanicMask", &LuaSpellInfo::GetEffectMechanicMask }, + { "GetSpellMechanicMaskByEffectMask", &LuaSpellInfo::GetSpellMechanicMaskByEffectMask }, + { "GetEffectMechanic", &LuaSpellInfo::GetEffectMechanic }, + { "GetDispelMask", &LuaSpellInfo::GetDispelMask }, + { "GetExplicitTargetMask", &LuaSpellInfo::GetExplicitTargetMask }, + { "GetAuraState", &LuaSpellInfo::GetAuraState }, + { "GetSpellSpecific", &LuaSpellInfo::GetSpellSpecific }, + { "GetEffectMiscValueA", &LuaSpellInfo::GetEffectMiscValueA }, + { "GetEffectMiscValueB", &LuaSpellInfo::GetEffectMiscValueB }, + + // Setters + + // Boolean + { "HasAreaAuraEffect", &LuaSpellInfo::HasAreaAuraEffect }, + { "HasAttribute", &LuaSpellInfo::HasAttribute }, + { "HasAura", &LuaSpellInfo::HasAura }, + { "HasEffect", &LuaSpellInfo::HasEffect }, + + { "IsAbilityLearnedWithProfession", &LuaSpellInfo::IsAbilityLearnedWithProfession }, + { "IsAbilityOfSkillType", &LuaSpellInfo::IsAbilityOfSkillType }, + { "IsAffectingArea", &LuaSpellInfo::IsAffectingArea }, + { "IsAllowingDeadTarget", &LuaSpellInfo::IsAllowingDeadTarget }, + { "IsAutocastable", &LuaSpellInfo::IsAutocastable }, + { "IsAutoRepeatRangedSpell", &LuaSpellInfo::IsAutoRepeatRangedSpell }, + { "IsBreakingStealth", &LuaSpellInfo::IsBreakingStealth }, + { "IsChanneled", &LuaSpellInfo::IsChanneled }, + { "IsCooldownStartedOnEvent", &LuaSpellInfo::IsCooldownStartedOnEvent }, + { "IsDeathPersistent", &LuaSpellInfo::IsDeathPersistent }, + { "IsExplicitDiscovery", &LuaSpellInfo::IsExplicitDiscovery }, + { "IsLootCrafting", &LuaSpellInfo::IsLootCrafting }, + { "IsMultiSlotAura", &LuaSpellInfo::IsMultiSlotAura }, + { "IsPassive", &LuaSpellInfo::IsPassive }, + { "IsPassiveStackableWithRanks", &LuaSpellInfo::IsPassiveStackableWithRanks }, + { "IsPositive", &LuaSpellInfo::IsPositive }, + { "IsPositiveEffect", &LuaSpellInfo::IsPositiveEffect }, + { "IsPrimaryProfession", &LuaSpellInfo::IsPrimaryProfession }, + { "IsPrimaryProfessionFirstRank", &LuaSpellInfo::IsPrimaryProfessionFirstRank }, + { "IsProfession", &LuaSpellInfo::IsProfession }, + { "IsProfessionOrRiding", &LuaSpellInfo::IsProfessionOrRiding }, + { "IsRangedWeaponSpell", &LuaSpellInfo::IsRangedWeaponSpell }, + { "IsRequiringDeadTarget", &LuaSpellInfo::IsRequiringDeadTarget }, + { "IsStackableWithRanks", &LuaSpellInfo::IsStackableWithRanks }, + { "IsTargetingArea", &LuaSpellInfo::IsTargetingArea }, + { "IsAffectedBySpellMods", &LuaSpellInfo::IsAffectedBySpellMods }, + /* { "IsAffectedBySpellMod", &LuaSpellInfo::IsAffectedBySpellMod }, */ + { "CanPierceImmuneAura", &LuaSpellInfo::CanPierceImmuneAura }, + { "CanDispelAura", &LuaSpellInfo::CanDispelAura }, + { "IsSingleTarget", &LuaSpellInfo::IsSingleTarget }, + { "IsAuraExclusiveBySpecificWith", &LuaSpellInfo::IsAuraExclusiveBySpecificWith }, + { "IsAuraExclusiveBySpecificPerCasterWith", &LuaSpellInfo::IsAuraExclusiveBySpecificPerCasterWith }, + { "CanBeUsedInCombat", &LuaSpellInfo::CanBeUsedInCombat }, + + { "NeedsComboPoints", &LuaSpellInfo::NeedsComboPoints }, + { "NeedsExplicitUnitTarget", &LuaSpellInfo::NeedsExplicitUnitTarget }, + { "NeedsToBeTriggeredByCaster", &LuaSpellInfo::NeedsToBeTriggeredByCaster }, + + { NULL, NULL } +}; + +ALERegister GemPropertiesEntryMethods[] = +{ + // Getters + { "GetId", &LuaGemPropertiesEntry::GetId }, + { "GetSpellItemEnchantement", &LuaGemPropertiesEntry::GetSpellItemEnchantement }, + + { NULL, NULL } +}; + +ALERegister SpellEntryMethods[] = +{ + // Getters + { "GetId", &LuaSpellEntry::GetId }, + { "GetCategory", &LuaSpellEntry::GetCategory }, + { "GetDispel", &LuaSpellEntry::GetDispel }, + { "GetMechanic", &LuaSpellEntry::GetMechanic }, + { "GetAttributes", &LuaSpellEntry::GetAttributes }, + { "GetAttributesEx", &LuaSpellEntry::GetAttributesEx }, + { "GetAttributesEx2", &LuaSpellEntry::GetAttributesEx2 }, + { "GetAttributesEx3", &LuaSpellEntry::GetAttributesEx3 }, + { "GetAttributesEx4", &LuaSpellEntry::GetAttributesEx4 }, + { "GetAttributesEx5", &LuaSpellEntry::GetAttributesEx5 }, + { "GetAttributesEx6", &LuaSpellEntry::GetAttributesEx6 }, + { "GetAttributesEx7", &LuaSpellEntry::GetAttributesEx7 }, + { "GetStances", &LuaSpellEntry::GetStances }, + { "GetStancesNot", &LuaSpellEntry::GetStancesNot }, + { "GetTargets", &LuaSpellEntry::GetTargets }, + { "GetTargetCreatureType", &LuaSpellEntry::GetTargetCreatureType }, + { "GetRequiresSpellFocus", &LuaSpellEntry::GetRequiresSpellFocus }, + { "GetFacingCasterFlags", &LuaSpellEntry::GetFacingCasterFlags }, + { "GetCasterAuraState", &LuaSpellEntry::GetCasterAuraState }, + { "GetTargetAuraState", &LuaSpellEntry::GetTargetAuraState }, + { "GetCasterAuraStateNot", &LuaSpellEntry::GetCasterAuraStateNot }, + { "GetTargetAuraStateNot", &LuaSpellEntry::GetTargetAuraStateNot }, + { "GetCasterAuraSpell", &LuaSpellEntry::GetCasterAuraSpell }, + { "GetTargetAuraSpell", &LuaSpellEntry::GetTargetAuraSpell }, + { "GetExcludeCasterAuraSpell", &LuaSpellEntry::GetExcludeCasterAuraSpell }, + { "GetExcludeTargetAuraSpell", &LuaSpellEntry::GetExcludeTargetAuraSpell }, + { "GetCastingTimeIndex", &LuaSpellEntry::GetCastingTimeIndex }, + { "GetRecoveryTime", &LuaSpellEntry::GetRecoveryTime }, + { "GetCategoryRecoveryTime", &LuaSpellEntry::GetCategoryRecoveryTime }, + { "GetInterruptFlags", &LuaSpellEntry::GetInterruptFlags }, + { "GetAuraInterruptFlags", &LuaSpellEntry::GetAuraInterruptFlags }, + { "GetChannelInterruptFlags", &LuaSpellEntry::GetChannelInterruptFlags }, + { "GetProcFlags", &LuaSpellEntry::GetProcFlags }, + { "GetProcChance", &LuaSpellEntry::GetProcChance }, + { "GetProcCharges", &LuaSpellEntry::GetProcCharges }, + { "GetMaxLevel", &LuaSpellEntry::GetMaxLevel }, + { "GetBaseLevel", &LuaSpellEntry::GetBaseLevel }, + { "GetSpellLevel", &LuaSpellEntry::GetSpellLevel }, + { "GetDurationIndex", &LuaSpellEntry::GetDurationIndex }, + { "GetPowerType", &LuaSpellEntry::GetPowerType }, + { "GetManaCost", &LuaSpellEntry::GetManaCost }, + { "GetManaCostPerlevel", &LuaSpellEntry::GetManaCostPerlevel }, + { "GetManaPerSecond", &LuaSpellEntry::GetManaPerSecond }, + { "GetManaPerSecondPerLevel", &LuaSpellEntry::GetManaPerSecondPerLevel }, + { "GetRangeIndex", &LuaSpellEntry::GetRangeIndex }, + { "GetSpeed", &LuaSpellEntry::GetSpeed }, + { "GetStackAmount", &LuaSpellEntry::GetStackAmount }, + { "GetTotem", &LuaSpellEntry::GetTotem }, + { "GetReagent", &LuaSpellEntry::GetReagent }, + { "GetReagentCount", &LuaSpellEntry::GetReagentCount }, + { "GetEquippedItemClass", &LuaSpellEntry::GetEquippedItemClass }, + { "GetEquippedItemSubClassMask", &LuaSpellEntry::GetEquippedItemSubClassMask }, + { "GetEquippedItemInventoryTypeMask", &LuaSpellEntry::GetEquippedItemInventoryTypeMask }, + { "GetEffect", &LuaSpellEntry::GetEffect }, + { "GetEffectDieSides", &LuaSpellEntry::GetEffectDieSides }, + { "GetEffectRealPointsPerLevel", &LuaSpellEntry::GetEffectRealPointsPerLevel }, + { "GetEffectBasePoints", &LuaSpellEntry::GetEffectBasePoints }, + { "GetEffectMechanic", &LuaSpellEntry::GetEffectMechanic }, + { "GetEffectImplicitTargetA", &LuaSpellEntry::GetEffectImplicitTargetA }, + { "GetEffectImplicitTargetB", &LuaSpellEntry::GetEffectImplicitTargetB }, + { "GetEffectRadiusIndex", &LuaSpellEntry::GetEffectRadiusIndex }, + { "GetEffectApplyAuraName", &LuaSpellEntry::GetEffectApplyAuraName }, + { "GetEffectAmplitude", &LuaSpellEntry::GetEffectAmplitude }, + { "GetEffectValueMultiplier", &LuaSpellEntry::GetEffectValueMultiplier }, + { "GetEffectChainTarget", &LuaSpellEntry::GetEffectChainTarget }, + { "GetEffectItemType", &LuaSpellEntry::GetEffectItemType }, + { "GetEffectMiscValue", &LuaSpellEntry::GetEffectMiscValue }, + { "GetEffectMiscValueB", &LuaSpellEntry::GetEffectMiscValueB }, + { "GetEffectTriggerSpell", &LuaSpellEntry::GetEffectTriggerSpell }, + { "GetEffectPointsPerComboPoint", &LuaSpellEntry::GetEffectPointsPerComboPoint }, + { "GetEffectSpellClassMask", &LuaSpellEntry::GetEffectSpellClassMask }, + { "GetSpellVisual", &LuaSpellEntry::GetSpellVisual }, + { "GetSpellIconID", &LuaSpellEntry::GetSpellIconID }, + { "GetActiveIconID", &LuaSpellEntry::GetActiveIconID }, + { "GetSpellPriority", &LuaSpellEntry::GetSpellPriority }, + { "GetSpellName", &LuaSpellEntry::GetSpellName }, + { "GetRank", &LuaSpellEntry::GetRank }, + { "GetManaCostPercentage", &LuaSpellEntry::GetManaCostPercentage }, + { "GetStartRecoveryCategory", &LuaSpellEntry::GetStartRecoveryCategory }, + { "GetStartRecoveryTime", &LuaSpellEntry::GetStartRecoveryTime }, + { "GetMaxTargetLevel", &LuaSpellEntry::GetMaxTargetLevel }, + { "GetSpellFamilyName", &LuaSpellEntry::GetSpellFamilyName }, + { "GetSpellFamilyFlags", &LuaSpellEntry::GetSpellFamilyFlags }, + { "GetMaxAffectedTargets", &LuaSpellEntry::GetMaxAffectedTargets }, + { "GetDmgClass", &LuaSpellEntry::GetDmgClass }, + { "GetPreventionType", &LuaSpellEntry::GetPreventionType }, + { "GetEffectDamageMultiplier", &LuaSpellEntry::GetEffectDamageMultiplier }, + { "GetTotemCategory", &LuaSpellEntry::GetTotemCategory }, + { "GetAreaGroupId", &LuaSpellEntry::GetAreaGroupId }, + { "GetSchoolMask", &LuaSpellEntry::GetSchoolMask }, + { "GetRuneCostID", &LuaSpellEntry::GetRuneCostID }, + { "GetEffectBonusMultiplier", &LuaSpellEntry::GetEffectBonusMultiplier }, + + // Setters + { "SetCategory", &LuaSpellEntry::SetCategory }, + { "SetDispel", &LuaSpellEntry::SetDispel }, + { "SetMechanic", &LuaSpellEntry::SetMechanic }, + { "SetAttributes", &LuaSpellEntry::SetAttributes }, + { "SetAttributesEx", &LuaSpellEntry::SetAttributesEx }, + { "SetAttributesEx2", &LuaSpellEntry::SetAttributesEx2 }, + { "SetAttributesEx3", &LuaSpellEntry::SetAttributesEx3 }, + { "SetAttributesEx4", &LuaSpellEntry::SetAttributesEx4 }, + { "SetAttributesEx5", &LuaSpellEntry::SetAttributesEx5 }, + { "SetAttributesEx6", &LuaSpellEntry::SetAttributesEx6 }, + { "SetAttributesEx7", &LuaSpellEntry::SetAttributesEx7 }, + { "SetStances", &LuaSpellEntry::SetStances }, + { "SetStancesNot", &LuaSpellEntry::SetStancesNot }, + { "SetTargets", &LuaSpellEntry::SetTargets }, + { "SetTargetCreatureType", &LuaSpellEntry::SetTargetCreatureType }, + { "SetRequiresSpellFocus", &LuaSpellEntry::SetRequiresSpellFocus }, + { "SetFacingCasterFlags", &LuaSpellEntry::SetFacingCasterFlags }, + { "SetCasterAuraState", &LuaSpellEntry::SetCasterAuraState }, + { "SetTargetAuraState", &LuaSpellEntry::SetTargetAuraState }, + { "SetCasterAuraStateNot", &LuaSpellEntry::SetCasterAuraStateNot }, + { "SetTargetAuraStateNot", &LuaSpellEntry::SetTargetAuraStateNot }, + { "SetCasterAuraSpell", &LuaSpellEntry::SetCasterAuraSpell }, + { "SetTargetAuraSpell", &LuaSpellEntry::SetTargetAuraSpell }, + { "SetExcludeCasterAuraSpell", &LuaSpellEntry::SetExcludeCasterAuraSpell }, + { "SetExcludeTargetAuraSpell", &LuaSpellEntry::SetExcludeTargetAuraSpell }, + { "SetRecoveryTime", &LuaSpellEntry::SetRecoveryTime }, + { "SetCategoryRecoveryTime", &LuaSpellEntry::SetCategoryRecoveryTime }, + { "SetInterruptFlags", &LuaSpellEntry::SetInterruptFlags }, + { "SetAuraInterruptFlags", &LuaSpellEntry::SetAuraInterruptFlags }, + { "SetChannelInterruptFlags", &LuaSpellEntry::SetChannelInterruptFlags }, + { "SetProcFlags", &LuaSpellEntry::SetProcFlags }, + { "SetProcChance", &LuaSpellEntry::SetProcChance }, + { "SetProcCharges", &LuaSpellEntry::SetProcCharges }, + { "SetMaxLevel", &LuaSpellEntry::SetMaxLevel }, + { "SetBaseLevel", &LuaSpellEntry::SetBaseLevel }, + { "SetSpellLevel", &LuaSpellEntry::SetSpellLevel }, + { "SetPowerType", &LuaSpellEntry::SetPowerType }, + { "SetManaCost", &LuaSpellEntry::SetManaCost }, + { "SetManaCostPerlevel", &LuaSpellEntry::SetManaCostPerlevel }, + { "SetManaPerSecond", &LuaSpellEntry::SetManaPerSecond }, + { "SetManaPerSecondPerLevel", &LuaSpellEntry::SetManaPerSecondPerLevel }, + { "SetSpeed", &LuaSpellEntry::SetSpeed }, + { "SetStackAmount", &LuaSpellEntry::SetStackAmount }, + { "SetEquippedItemClass", &LuaSpellEntry::SetEquippedItemClass }, + { "SetEquippedItemSubClassMask", &LuaSpellEntry::SetEquippedItemSubClassMask }, + { "SetEquippedItemInventoryTypeMask", &LuaSpellEntry::SetEquippedItemInventoryTypeMask }, + { "SetSpellIconID", &LuaSpellEntry::SetSpellIconID }, + { "SetActiveIconID", &LuaSpellEntry::SetActiveIconID }, + { "SetSpellPriority", &LuaSpellEntry::SetSpellPriority }, + { "SetManaCostPercentage", &LuaSpellEntry::SetManaCostPercentage }, + { "SetStartRecoveryCategory", &LuaSpellEntry::SetStartRecoveryCategory }, + { "SetStartRecoveryTime", &LuaSpellEntry::SetStartRecoveryTime }, + { "SetMaxTargetLevel", &LuaSpellEntry::SetMaxTargetLevel }, + { "SetSpellFamilyName", &LuaSpellEntry::SetSpellFamilyName }, + { "SetMaxAffectedTargets", &LuaSpellEntry::SetMaxAffectedTargets }, + { "SetDmgClass", &LuaSpellEntry::SetDmgClass }, + { "SetPreventionType", &LuaSpellEntry::SetPreventionType }, + { "SetSchoolMask", &LuaSpellEntry::SetSchoolMask }, + { "SetRuneCostID", &LuaSpellEntry::SetRuneCostID }, + + { NULL, NULL } +}; + +ALERegister PetMethods[] = +{ + // Getters + { "GetPetType", &LuaPet::GetPetType }, + { "GetDuration", &LuaPet::GetDuration }, + { "GetHappinessState", &LuaPet::GetHappinessState }, + { "GetCurrentFoodBenefitLevel", &LuaPet::GetCurrentFoodBenefitLevel }, + { "GetMaxTalentPointsForLevel", &LuaPet::GetMaxTalentPointsForLevel }, + { "GetFreeTalentPoints", &LuaPet::GetFreeTalentPoints }, + { "GetUsedTalentCount", &LuaPet::GetUsedTalentCount }, + { "GetAuraUpdateMaskForRaid", &LuaPet::GetAuraUpdateMaskForRaid }, + { "GetOwner", &LuaPet::GetOwner }, + { "GetPetAutoSpellSize", &LuaPet::GetPetAutoSpellSize }, + { "GetPetAutoSpellOnPos", &LuaPet::GetPetAutoSpellOnPos }, + + // Setters + { "SetPetType", &LuaPet::SetPetType }, + { "SetDuration", &LuaPet::SetDuration }, + { "SetFreeTalentPoints", &LuaPet::SetFreeTalentPoints }, + { "SetUsedTalentCount", &LuaPet::SetUsedTalentCount }, + { "SetAuraUpdateMaskForRaid", &LuaPet::SetAuraUpdateMaskForRaid }, + { "SetRemoved", &LuaPet::SetRemoved }, + + // Boolean + { "IsControlled", &LuaPet::IsControlled }, + { "IsTemporarySummoned", &LuaPet::IsTemporarySummoned }, + { "IsPermanentPetFor", &LuaPet::IsPermanentPetFor }, + { "HaveInDiet", &LuaPet::HaveInDiet }, + { "HasTempSpell", &LuaPet::HasTempSpell }, + { "IsRemoved", &LuaPet::IsRemoved }, + { "IsBeingLoaded", &LuaPet::IsBeingLoaded }, + + // Other + { "CreateBaseAtCreature", &LuaPet::CreateBaseAtCreature }, + { "GivePetXP", &LuaPet::GivePetXP }, + { "GivePetLevel", &LuaPet::GivePetLevel }, + { "SynchronizeLevelWithOwner", &LuaPet::SynchronizeLevelWithOwner }, + { "ToggleAutocast", &LuaPet::ToggleAutocast }, + { "LearnPetPassives", &LuaPet::LearnPetPassives }, + { "CastWhenWillAvailable", &LuaPet::CastWhenWillAvailable }, + { "ClearCastWhenWillAvailable", &LuaPet::ClearCastWhenWillAvailable }, + { "AddSpell", &LuaPet::AddSpell }, + { "LearnSpell", &LuaPet::LearnSpell }, + { "LearnSpellHighRank", &LuaPet::LearnSpellHighRank }, + { "InitLevelupSpellsForLevel", &LuaPet::InitLevelupSpellsForLevel }, + { "UnlearnSpell", &LuaPet::UnlearnSpell }, + { "RemoveSpell", &LuaPet::RemoveSpell }, + { "CleanupActionBar", &LuaPet::CleanupActionBar }, + { "GenerateActionBarData", &LuaPet::GenerateActionBarData }, + { "InitPetCreateSpells", &LuaPet::InitPetCreateSpells }, + { "ResetTalents", &LuaPet::ResetTalents }, + { "InitTalentForLevel", &LuaPet::InitTalentForLevel }, + { "ResetAuraUpdateMaskForRaid", &LuaPet::ResetAuraUpdateMaskForRaid }, + { "SavePetToDB", &LuaPet::SavePetToDB }, + { "Remove", &LuaPet::Remove }, + + { NULL, NULL } +}; + +ALERegister LootMethods[] = +{ + // Get + { "GetMoney", &LuaLoot::GetMoney }, + { "GetItems", &LuaLoot::GetItems }, + { "GetQuestItems", &LuaLoot::GetQuestItems }, + { "GetUnlootedCount", &LuaLoot::GetUnlootedCount }, + { "GetLootType", &LuaLoot::GetLootType }, + { "GetRoundRobinPlayer", &LuaLoot::GetRoundRobinPlayer }, + { "GetLootOwner", &LuaLoot::GetLootOwner }, + { "GetContainer", &LuaLoot::GetContainer }, + { "GetSourceWorldObject", &LuaLoot::GetSourceWorldObject }, + { "GetItemCount", &LuaLoot::GetItemCount }, + { "GetMaxSlotForPlayer", &LuaLoot::GetMaxSlotForPlayer }, + + // Set + { "AddItem", &LuaLoot::AddItem }, + { "RemoveItem", &LuaLoot::RemoveItem }, + { "SetMoney", &LuaLoot::SetMoney }, + { "SetUnlootedCount", &LuaLoot::SetUnlootedCount }, + { "UpdateItemIndex", &LuaLoot::UpdateItemIndex }, + { "SetItemLooted", &LuaLoot::SetItemLooted }, + { "SetLootType", &LuaLoot::SetLootType }, + { "SetRoundRobinPlayer", &LuaLoot::SetRoundRobinPlayer }, + { "SetLootOwner", &LuaLoot::SetLootOwner }, + { "SetContainer", &LuaLoot::SetContainer }, + { "SetSourceWorldObject", &LuaLoot::SetSourceWorldObject }, + { "Clear", &LuaLoot::Clear }, + { "AddLooter", &LuaLoot::AddLooter }, + { "RemoveLooter", &LuaLoot::RemoveLooter }, + + // Boolean + { "HasItem", &LuaLoot::HasItem }, + { "HasQuestItems", &LuaLoot::HasQuestItems }, + { "HasItemForAll", &LuaLoot::HasItemForAll }, + { "HasOverThresholdItem", &LuaLoot::HasOverThresholdItem }, + { "IsLooted", &LuaLoot::IsLooted }, + { "IsEmpty", &LuaLoot::IsEmpty }, + + { NULL, NULL } +}; + +ALERegister TransportMethods[] = +{ + // Getters + { "GetPassengers", &LuaTransport::GetPassengers }, + + // Boolean + { "IsMotionTransport", &LuaTransport::IsMotionTransport }, + + // Other + { "AddPassenger", &LuaTransport::AddPassenger }, + { "RemovePassenger", &LuaTransport::RemovePassenger }, + { "EnableMovement", &LuaTransport::EnableMovement }, + + { NULL, NULL } +}; + +// fix compile error about accessing vehicle destructor +template<> int ALETemplate::CollectGarbage(lua_State* L) +{ + ASSERT(!manageMemory); + + // Get object pointer (and check type, no error) + ALEObject* obj = ALE::CHECKOBJ(L, 1, false); + delete obj; + return 0; +} + +// Template by Mud from http://stackoverflow.com/questions/4484437/lua-integer-type/4485511#4485511 +template<> int ALETemplate::Add(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) + ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Substract(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) - ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Multiply(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) * ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Divide(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) / ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Mod(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) % ALE::CHECKVAL(L, 2)); return 1; } +// template<> int ALETemplate::UnaryMinus(lua_State* L) { ALE::Push(L, -ALE::CHECKVAL(L, 1)); return 1; } +template<> int ALETemplate::Equal(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) == ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Less(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) < ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::LessOrEqual(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) <= ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Pow(lua_State* L) +{ + ALE::Push(L, static_cast(powl(static_cast(ALE::CHECKVAL(L, 1)), static_cast(ALE::CHECKVAL(L, 2))))); + return 1; +} +template<> int ALETemplate::ToString(lua_State* L) +{ + unsigned long long l = ALE::CHECKVAL(L, 1); + std::ostringstream ss; + ss << l; + ALE::Push(L, ss.str()); + return 1; +} + +template<> int ALETemplate::Add(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) + ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Substract(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) - ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Multiply(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) * ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Divide(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) / ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Mod(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) % ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::UnaryMinus(lua_State* L) { ALE::Push(L, -ALE::CHECKVAL(L, 1)); return 1; } +template<> int ALETemplate::Equal(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) == ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Less(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) < ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::LessOrEqual(lua_State* L) { ALE::Push(L, ALE::CHECKVAL(L, 1) <= ALE::CHECKVAL(L, 2)); return 1; } +template<> int ALETemplate::Pow(lua_State* L) +{ + ALE::Push(L, static_cast(powl(static_cast(ALE::CHECKVAL(L, 1)), static_cast(ALE::CHECKVAL(L, 2))))); + return 1; +} +template<> int ALETemplate::ToString(lua_State* L) +{ + long long l = ALE::CHECKVAL(L, 1); + std::ostringstream ss; + ss << l; + ALE::Push(L, ss.str()); + return 1; +} + +void RegisterFunctions(ALE* E) +{ + ALEGlobal::SetMethods(E, GlobalMethods); + + ALETemplate::Register(E, "Object"); + ALETemplate::SetMethods(E, ObjectMethods); + + ALETemplate::Register(E, "WorldObject"); + ALETemplate::SetMethods(E, ObjectMethods); + ALETemplate::SetMethods(E, WorldObjectMethods); + + ALETemplate::Register(E, "Unit"); + ALETemplate::SetMethods(E, ObjectMethods); + ALETemplate::SetMethods(E, WorldObjectMethods); + ALETemplate::SetMethods(E, UnitMethods); + + ALETemplate::Register(E, "Player"); + ALETemplate::SetMethods(E, ObjectMethods); + ALETemplate::SetMethods(E, WorldObjectMethods); + ALETemplate::SetMethods(E, UnitMethods); + ALETemplate::SetMethods(E, PlayerMethods); + + ALETemplate::Register(E, "Creature"); + ALETemplate::SetMethods(E, ObjectMethods); + ALETemplate::SetMethods(E, WorldObjectMethods); + ALETemplate::SetMethods(E, UnitMethods); + ALETemplate::SetMethods(E, CreatureMethods); + + ALETemplate::Register(E, "GameObject"); + ALETemplate::SetMethods(E, ObjectMethods); + ALETemplate::SetMethods(E, WorldObjectMethods); + ALETemplate::SetMethods(E, GameObjectMethods); + + ALETemplate::Register(E, "Transport"); + ALETemplate::SetMethods(E, ObjectMethods); + ALETemplate::SetMethods(E, WorldObjectMethods); + ALETemplate::SetMethods(E, GameObjectMethods); + ALETemplate::SetMethods(E, TransportMethods); + + ALETemplate::Register(E, "Corpse"); + ALETemplate::SetMethods(E, ObjectMethods); + ALETemplate::SetMethods(E, WorldObjectMethods); + ALETemplate::SetMethods(E, CorpseMethods); + + ALETemplate::Register(E, "Item"); + ALETemplate::SetMethods(E, ObjectMethods); + ALETemplate::SetMethods(E, ItemMethods); + + ALETemplate::Register(E, "ItemTemplate"); + ALETemplate::SetMethods(E, ItemTemplateMethods); + + ALETemplate::Register(E, "Vehicle"); + ALETemplate::SetMethods(E, VehicleMethods); + + ALETemplate::Register(E, "Group"); + ALETemplate::SetMethods(E, GroupMethods); + + ALETemplate::Register(E, "Guild"); + ALETemplate::SetMethods(E, GuildMethods); + + ALETemplate::Register(E, "Aura"); + ALETemplate::SetMethods(E, AuraMethods); + + ALETemplate::Register(E, "Spell"); + ALETemplate::SetMethods(E, SpellMethods); + + ALETemplate::Register(E, "Quest"); + ALETemplate::SetMethods(E, QuestMethods); + + ALETemplate::Register(E, "Map"); + ALETemplate::SetMethods(E, MapMethods); + + ALETemplate::Register(E, "AuctionHouseEntry"); + ALETemplate::SetMethods(E, AuctionMethods); + + ALETemplate::Register(E, "BattleGround"); + ALETemplate::SetMethods(E, BattleGroundMethods); + + ALETemplate::Register(E, "ChatHandler"); + ALETemplate::SetMethods(E, ChatHandlerMethods); + + ALETemplate::Register(E, "WorldPacket", true); + ALETemplate::SetMethods(E, PacketMethods); + + ALETemplate::Register(E, "ALEQuery", true); + ALETemplate::SetMethods(E, QueryMethods); + + ALETemplate::Register(E, "AchievementEntry"); + ALETemplate::SetMethods(E, AchievementMethods); + + ALETemplate::Register(E, "Roll"); + ALETemplate::SetMethods(E, RollMethods); + + ALETemplate::Register(E, "Ticket"); + ALETemplate::SetMethods(E, TicketMethods); + + ALETemplate::Register(E, "SpellInfo"); + ALETemplate::SetMethods(E, SpellInfoMethods); + + ALETemplate::Register(E, "GemPropertiesEntry"); + ALETemplate::SetMethods(E, GemPropertiesEntryMethods); + + ALETemplate::Register(E, "SpellEntry"); + ALETemplate::SetMethods(E, SpellEntryMethods); + + ALETemplate::Register(E, "CreatureTemplate"); + + ALETemplate::Register(E, "Loot"); + ALETemplate::SetMethods(E, LootMethods); + + ALETemplate::Register(E, "long long", true); + + ALETemplate::Register(E, "unsigned long long", true); +} diff --git a/modules/mod-ale/src/LuaEngine/docs/.gitignore b/modules/mod-ale/src/LuaEngine/docs/.gitignore new file mode 100644 index 0000000..3fd1eba --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/docs/.gitignore @@ -0,0 +1,2 @@ +# Ignore the temporary "build" folder. +build \ No newline at end of file diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/.gitignore b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/.gitignore new file mode 100644 index 0000000..b32d8aa --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/.gitignore @@ -0,0 +1,2 @@ +*.pyc +.idea \ No newline at end of file diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/__init__.py b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/__main__.py b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/__main__.py new file mode 100644 index 0000000..2d54bd0 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/__main__.py @@ -0,0 +1,169 @@ +import os +import shutil +import typing +from jinja2 import Environment, FileSystemLoader +from typedecorator import params, returns +from ALEDoc.parser import ClassParser, MethodDoc +import glob +import time + + +@returns([(str, typing.IO)]) +@params(search_path=str) +def find_class_files(search_path): + """Find and open all files containing ALE class methods in `search_path`. + + :param search_path: the path to search for ALE methods in + :return: a list of all files containing ALE methods, and the name of their respective classes + """ + # Search for all files ending in "Methods.h". + method_file_names = glob.glob(os.path.join(search_path, '**', '*Methods.h')) + # Open each file. + method_files = [open(file_name, 'r') for file_name in method_file_names] + return method_files + + +def make_renderer(template_path, link_parser_factory): + """Return a function that can be used to render Jinja2 templates from the `template_path` directory.""" + + # Set up jinja2 environment to load templates from the templates folder. + env = Environment(loader=FileSystemLoader(template_path)) + + + def inner(template_name, output_path, level, **kwargs): + env.filters['parse_links'], env.filters['parse_data_type'] = link_parser_factory(level) + template = env.get_template(template_name) + static = make_static(level) + root = make_root(level) + + with open('build/' + output_path, 'w') as out: + out.write(template.render(level=level, static=static, root=root, **kwargs)) + + return inner + + +def make_static(level): + return lambda file_name: ('../' * level) + 'static/' + file_name + + +def make_root(level): + return lambda file_name: ('../' * level) + file_name + + +if __name__ == '__main__': + # Recreate the build folder and copy static files over. + if os.path.exists('build'): + shutil.rmtree('build') + os.mkdir('build') + shutil.copytree('ALEDoc/static', 'build/static') + + # Load up all files with methods we need to parse. + print('Finding ALE method files...') + class_files = find_class_files('../') + + # Parse all the method files. + classes = [] + for f in class_files: + print(f'Parsing file {f.name}...') + classes.append(ClassParser.parse_file(f)) + f.close() + + # Sort the classes so they are in the correct order in lists. + classes.sort(key=lambda c: c.name) + + def make_parsers(level): + """Returns a function that parses content for refs to other classes, methods, or enums, + and automatically inserts the correct link. + """ + # Make lists of all class names and method names. + class_names = [] + method_names = [] + + for class_ in classes: + class_names.append('[' + class_.name + ']') + + for method in class_.methods: + method_names.append('[' + class_.name + ':' + method.name + ']') + + def link_parser(content): + # Replace all occurrencies of &Class:Function and then &Class with a link to given func or class + + for name in method_names: + # Take the [] off the front of the method's name. + full_name = name[1:-1] + # Split "Class:Method" into "Class" and "Method". + class_name, method_name = full_name.split(':') + url = '{}{}/{}.html'.format(('../' * level), class_name, method_name) + # Replace occurrencies of &Class:Method with the url created + content = content.replace(name, '{}'.format(url, full_name)) + + for name in class_names: + # Take the [] off the front of the class's name. + class_name = name[1:-1] + url = '{}{}/index.html'.format(('../' * level), class_name) + # Replace occurrencies of &Class:Method with the url created + content = content.replace(name, '{}'.format(url, class_name)) + + return content + + # Links to the "Programming in Lua" documentation for each Lua type. + lua_type_documentation = { + 'nil': 'http://www.lua.org/pil/2.1.html', + 'boolean': 'http://www.lua.org/pil/2.2.html', + 'number': 'http://www.lua.org/pil/2.3.html', + 'string': 'http://www.lua.org/pil/2.4.html', + 'table': 'http://www.lua.org/pil/2.5.html', + 'function': 'http://www.lua.org/pil/2.6.html', + '...': 'http://www.lua.org/pil/5.2.html', + } + + def data_type_parser(content): + # If the type is a Lua type, return a link to Lua documentation. + if content in lua_type_documentation: + url = lua_type_documentation[content] + return '{}'.format(url, content) + + # Otherwise try to build a link to the proper page. + if content in class_names: + class_name = content[1:-1] + url = '{}{}/index.html'.format(('../' * level), class_name) + return '{}'.format(url, class_name) + + # Case for enums to direct to a search on github + enum_name = content[1:-1] + url = 'https://github.com/azerothcore/azerothcore-wotlk/search?l=cpp&q=%22enum+{}%22&type=Code&utf8=%E2%9C%93'.format(enum_name) + return '{}'.format(url, enum_name) + + # By default we just return the name without the [] around it + return content[1:-1] + + return link_parser, data_type_parser + + # Create the render function with the template path and parser maker. + render = make_renderer('ALEDoc/templates', make_parsers) + + # Render the index. + render('index.html', 'index.html', level=0, classes=classes) + # Render the search index. + render('search-index.js', 'search-index.js', level=0, classes=classes) + # Render the date. + render('date.js', 'date.js', level=0, currdate=time.strftime("%d/%m/%Y")) + + for class_ in classes: + print(f'Rendering pages for class {class_.name}...') + + # Make a folder for the class. + os.mkdir('build/' + class_.name) + index_path = '{}/index.html'.format(class_.name) + sidebar_path = '{}/sidebar.js'.format(class_.name) + + # Render the class's index page. + render('class.html', index_path, level=1, classes=classes, current_class=class_) + + # Render the class's sidebar script. + render('sidebar.js', sidebar_path, level=1, classes=classes, current_class=class_) + + # Render each method's page. + for method in class_.methods: + method_path = '{}/{}.html'.format(class_.name, method.name) + render('method.html', method_path, level=1, current_class=class_, current_method=method) diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/parser.py b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/parser.py new file mode 100644 index 0000000..b03c642 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/parser.py @@ -0,0 +1,334 @@ +import os +import re +import typing +import markdown +from typedecorator import params, returns, Nullable + + +class ParameterDoc(object): + """The documentation data of a parameter or return value for an ALE method.""" + + # The integer ranges that each C++ type is valid for. None means valid for all numbers. + valid_ranges = { + 'float': None, + 'double': None, + 'int': ('-2,147,483,647', '2,147,483,647'), # This should be -32767..32767, but it's pretty safe to assume 32-bit. + 'int8': ('-127', '127'), + 'uint8': ('0', '255'), + 'int16': ('-32,767', '32,767'), + 'uint16': ('0', '65,535'), + 'int32': ('-2,147,483,647', '2,147,483,647'), + 'uint32': ('0', '4,294,967,295'), + 'int64': ('-9,223,372,036,854,775,808', '9,223,372,036,854,775,807'), + 'uint64': ('0', '18,446,744,073,709,551,615'), + 'ObjectGuid': ('0', '18,446,744,073,709,551,615'), + } + + @params(self=object, name=Nullable(str), data_type=str, description=str, default_value=Nullable(str)) + def __init__(self, name, data_type, description, default_value=None): + """If `name` is not provided, the Parameter is a returned value instead of a parameter.""" + self.name = name + self.data_type = data_type + self.default_value = default_value + + if self.data_type == '...': + self.name = '...' + else: + assert(self.name is not None) + + if description: + # Capitalize the first letter, add a period, and parse as Markdown. + self.description = '{}{}. '.format(description[0].capitalize(), description[1:]) + self.description = markdown.markdown(self.description) + else: + self.description = '' + + # If the data type is a C++ number, convert to Lua number and add range info to description. + if self.data_type in self.valid_ranges.keys(): + range = ParameterDoc.valid_ranges[self.data_type] + if range: + self.description += '

Valid numbers: integers from {0} to {1}.

'.format(range[0], range[1]) + else: + self.description += '

Valid numbers: all decimal numbers.

' + + self.data_type = 'number' + + elif self.data_type == 'bool': + self.data_type = 'boolean' + + elif self.data_type == 'int64' or self.data_type == 'uint64': + self.data_type = '[' + self.data_type + ']' + + elif not self.data_type in ['nil', 'boolean', 'number', 'string', 'table', 'function', '...'] and self.data_type[:1] != '[': + print(f"Missing angle brackets [] around the data type name: `{self.data_type}`") + + +class MethodDoc(object): + """The documentation data of an ALE method.""" + @params(self=object, name=str, description=str, prototypes=[str], parameters=[ParameterDoc], returned=[ParameterDoc]) + def __init__(self, name, description, prototypes, parameters, returned): + self.name = name + self.prototypes = prototypes + self.parameters = parameters + self.returned = returned + + # Parse the description as Markdown. + self.description = markdown.markdown(description) + # Pull the first paragraph out of the description as the short description. + self.short_description = self.description.split('

')[0][3:] + # If it has a description, it is "documented". + self.documented = self.description != '' + + +class MangosClassDoc(object): + """The documentation of a MaNGOS class that has Lua methods.""" + @params(self=object, name=str, description=str, methods=[MethodDoc]) + def __init__(self, name, description, methods): + self.name = name + # Parse the description as Markdown. + self.description = markdown.markdown(description) + # Pull the first paragraph out of the description as the short description. + self.short_description = self.description.split('

')[0][3:] + # Sort the methods by their names. + self.methods = sorted(methods, key=lambda m: m.name) + + # If any of our methods are not documented, we aren't fully documented. + for method in methods: + if not method.documented: + self.fully_documented = False + break + else: + self.fully_documented = True + + # In the same vein, if any of our methods are documented, we aren't fully *un*documented. + for method in methods: + if method.documented: + self.fully_undocumented = False + break + else: + self.fully_undocumented = True + + +class ClassParser(object): + """Parses a file line-by-line and returns methods when enough information is received to build them.""" + + # Various regular expressions to parse different parts of the doc string. + # There are used to parse the class's description. + class_start_regex = re.compile(r"\s*/\*\*\*") # The start of class documentation, i.e. /*** + class_body_regex = re.compile(r"\s*\*\s*(.*)") # The "body", i.e. a * and optionally some descriptive text. + class_end_regex = re.compile(r"\s*\*/") # The end of the comment portion, i.e. */ + + # These are used to parse method documentation. + start_regex = re.compile(r"\s*/\*\*") # The start of documentation, i.e. /** + body_regex = re.compile(r"\s*\s?\*\s?(.*)") # The "body", i.e. a * and optionally some descriptive text. + # An extra optional space (\s?) was thrown in to make it different from `class_body_regex`. + + param_regex = re.compile(r"""\s*\*\s@param\s # The @param tag starts with opt. whitespace followed by "* @param ". + ([^\s]+)\s(\w+)? # The data type, a space, and the name of the param. + (?:\s=\s(\w+))? # The default value: a = surrounded by spaces, followed by text. + (?:\s:\s(.+))? # The description: a colon surrounded by spaces, followed by text. + """, re.X) + # This is the same as the @param tag, minus the default value part. + return_regex = re.compile(r"""\s*\*\s@return\s + ([\[\]\w]+)\s(\w+) + (?:\s:\s(.+))? + """, re.X) + proto_regex = re.compile(r"""\s*\*\s@proto\s + ([\w\s,]+)? # The list of arguments. + (?:=\s)? # An equals sign and a space separate the args and returns. + (?:\(([\w\s,]+)\))? # The list of return values, in parens. + """, re.X) + + comment_end_regex = re.compile(r"\s*\*/") # The end of the comment portion, i.e. */ + end_regex = re.compile(r"\s*int\s(\w+)\s*\(") # The end of the documentation, i.e. int MethodName( + + def __init__(self, class_name): + assert ClassParser.class_body_regex is not ClassParser.body_regex + # The methods that have been parsed. + self.methods = [] + # The name of the class being parsed. + self.class_name = class_name + # The description of the class being parsed. + self.class_description = '' + # Reset the parser's state machine. + self.reset() + + def reset(self): + # What the last handled regex was, to determine what the next should be. + self.last_regex = None + + # These are used to piece together the next `Method`. + self.description = '' + self.params = [] + self.returned = [] + self.method_name = None + self.prototypes = [] + + def handle_class_body(self, match): + text = match.group(1) + self.class_description += text + '\n' + + def handle_body(self, match): + text = match.group(1) + self.description += text + '\n' + + def handle_param(self, match): + data_type, name, default, description = match.group(1), match.group(2), match.group(3), match.group(4) + self.params.append(ParameterDoc(name, data_type, description, default)) + + def handle_return(self, match): + data_type, name, description = match.group(1), match.group(2), match.group(3) + self.returned.append(ParameterDoc(name, data_type, description)) + + def handle_proto(self, match): + return_values, parameters = match.group(1), match.group(2) + parameters = ' '+parameters+' ' if parameters else '' + return_values = return_values + '= ' if return_values else '' + + if self.class_name == 'Global': + prototype = '{0}{{0}}({1})'.format(return_values, parameters) + else: + prototype = '{0}{1}:{{0}}({2})'.format(return_values, self.class_name, parameters) + + self.prototypes.append(prototype) + + def handle_end(self, match): + self.method_name = match.group(1) + + def make_prototype(parameters): + if parameters != '': + parameters = ' ' + parameters + ' ' + + if self.class_name == 'Global': + if self.returned: + return_values = ', '.join([param.name for param in self.returned]) + prototype = '{0} = {1}({2})'.format(return_values, self.method_name, parameters) + else: + prototype = '{0}({1})'.format(self.method_name, parameters) + else: + if self.returned: + return_values = ', '.join([param.name for param in self.returned]) + prototype = '{0} = {1}:{2}({3})'.format(return_values, self.class_name, self.method_name, parameters) + else: + prototype = '{0}:{1}({2})'.format(self.class_name, self.method_name, parameters) + + return prototype + + # If there's no prototype, make one with all params and returns. + if not self.prototypes: + # A list of all parameters with default values. + params_with_default = [] + # The index of the last non-default parameter. + last_non_default_i = 0 + # If False, a parameter WITHOUT a default value follows one WITH a default value. + # In this case, don't bother generating prototypes. + simple_order = True + + for i, param in enumerate(self.params): + if param.default_value: + params_with_default.append(param) + else: + last_non_default_i = i + if params_with_default: + simple_order = False + + if not params_with_default or not simple_order: + # Just generate one prototype with all the parameters. + parameters = ', '.join([param.name for param in self.params]) + self.prototypes.append(make_prototype(parameters)) + else: + # Generate a prototype for all the non-default parameters, + # then one for each default parameter with all the previous parameters. + for i in range(last_non_default_i, len(self.params)): + parameters = ', '.join([param.name for param in self.params[:i+1]]) + self.prototypes.append(make_prototype(parameters)) + + else: + # Format the method name into each prototype. + self.prototypes = [proto.format(self.method_name) for proto in self.prototypes] + + self.methods.append(MethodDoc(self.method_name, self.description, self.prototypes, self.params, self.returned)) + + # Table of which handler is used to handle each regular expressions. + regex_handlers = { + class_start_regex: None, + class_body_regex: handle_class_body, + class_end_regex: None, + start_regex: None, + body_regex: handle_body, + param_regex: handle_param, + return_regex: handle_return, + proto_regex: handle_proto, + comment_end_regex: None, + end_regex: handle_end, + } + + # Table of which regular expressions can follow the last handled regex. + # `body_regex` must always come LAST when used, since it also matches param, return, and comment_end. + next_regexes = { + None: [class_start_regex, start_regex, end_regex], + class_start_regex: [class_end_regex, class_body_regex], + class_body_regex: [class_end_regex, class_body_regex], + class_end_regex: [], + start_regex: [param_regex, return_regex, proto_regex, comment_end_regex, body_regex], + body_regex: [param_regex, return_regex, proto_regex, comment_end_regex, body_regex], + proto_regex: [param_regex, return_regex, proto_regex, comment_end_regex, body_regex], + param_regex: [param_regex, return_regex, comment_end_regex, body_regex], + return_regex: [return_regex, comment_end_regex], + comment_end_regex: [end_regex], + end_regex: [], + } + + @returns(Nullable(MethodDoc)) + @params(self=object, line=str) + def next_line(self, line): + """Parse the next line of the file. + + This method returns a `Method` when enough data to form a `Method` has been parsed. + Otherwise, it returns None. + """ + # Get the list of expected regular expressions using the last one handled. + valid_regexes = self.next_regexes[self.last_regex] + + # Try to find a match. + for regex in valid_regexes: + match = regex.match(line) + + if match: + handler = self.regex_handlers[regex] + + if handler: + handler(self, match) + + # Not every regex has a handler, but keep track of where we are anyway. + self.last_regex = regex + # Break at the first match. + break + else: + # No valid regex was found, reset everything. + self.reset() + + @returns(MangosClassDoc) + def to_class_doc(self): + """Create an instance of `MangosClassDoc` from the parser's data. + + Is called by `parse_file` once parsing is finished. + """ + return MangosClassDoc(self.class_name, self.class_description, self.methods) + + @staticmethod + @returns(MangosClassDoc) + @params(file=typing.IO) + def parse_file(file): + """Parse the file `file` into a documented class.""" + # Get the class name from "ClassMethods.h" by stripping off "Methods.h". + class_name = os.path.basename(file.name)[:-len('Methods.h')] + parser = ClassParser(class_name) + + line = file.readline() + + while line: + parser.next_line(line) + line = file.readline() + + return parser.to_class_doc() diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/FiraSans-Medium.woff b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/FiraSans-Medium.woff new file mode 100644 index 0000000..5627227 Binary files /dev/null and b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/FiraSans-Medium.woff differ diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/FiraSans-Regular.woff b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/FiraSans-Regular.woff new file mode 100644 index 0000000..9ff4044 Binary files /dev/null and b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/FiraSans-Regular.woff differ diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/Heuristica-Italic.woff b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/Heuristica-Italic.woff new file mode 100644 index 0000000..b0cebf0 Binary files /dev/null and b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/Heuristica-Italic.woff differ diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceCodePro-Regular.woff b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceCodePro-Regular.woff new file mode 100644 index 0000000..5576670 Binary files /dev/null and b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceCodePro-Regular.woff differ diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceCodePro-Semibold.woff b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceCodePro-Semibold.woff new file mode 100644 index 0000000..ca972a1 Binary files /dev/null and b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceCodePro-Semibold.woff differ diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceSerifPro-Bold.woff b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceSerifPro-Bold.woff new file mode 100644 index 0000000..ac1b1b3 Binary files /dev/null and b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceSerifPro-Bold.woff differ diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceSerifPro-Regular.woff b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceSerifPro-Regular.woff new file mode 100644 index 0000000..e8c43b8 Binary files /dev/null and b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/SourceSerifPro-Regular.woff differ diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/dark.css b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/dark.css new file mode 100644 index 0000000..a284615 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/dark.css @@ -0,0 +1,361 @@ +/* + _______ + / \ + .==. .==. + (( ))==(( )) + / "==" "=="\ + /____|| || ||___\ + ________ ____ ________ ___ ___ + | ___ \ / \ | ___ \ | | / / + | | \ \ / /\ \ | | \ \| |_/ / + | | ) / /__\ \ | |__/ /| ___ \ + | |__/ / ______ \| ____ \| | \ \ +_______|_______/__/ ____ \__\__|___\__\__|___\__\____ +| ___ \ | ____/ / \ | ___ \ | ____| ___ \ +| | \ \| |___ / /\ \ | | \ \| |___| | \ \ +| |__/ /| ____/ /__\ \ | | ) | ____| |__/ / +| ____ \| |__/ ______ \| |__/ /| |___| ____ \ +|__| \__\____/__/ \__\_______/ |______|__| \__\ + https://darkreader.org +*/ + +/*! Dark reader generated CSS | Licensed under MIT https://github.com/darkreader/darkreader/blob/main/LICENSE */ + +/* User-Agent Style */ +html { + background-color: #181a1b !important; +} +html { + color-scheme: dark !important; +} +html, body, input, textarea, select, button, dialog { + background-color: #181a1b; +} +html, body, input, textarea, select, button { + border-color: #736b5e; + color: #e8e6e3; +} +a { + color: #3391ff; +} +table { + border-color: #545b5e; +} +::placeholder { + color: #b2aba1; +} +input:-webkit-autofill, +textarea:-webkit-autofill, +select:-webkit-autofill { + background-color: #404400 !important; + color: #e8e6e3 !important; +} +::-webkit-scrollbar { + background-color: #202324; + color: #aba499; +} +::-webkit-scrollbar-thumb { + background-color: #454a4d; +} +::-webkit-scrollbar-thumb:hover { + background-color: #575e62; +} +::-webkit-scrollbar-thumb:active { + background-color: #484e51; +} +::-webkit-scrollbar-corner { + background-color: #181a1b; +} +::selection { + background-color: #004daa !important; + color: #e8e6e3 !important; +} +::-moz-selection { + background-color: #004daa !important; + color: #e8e6e3 !important; +} + +/* Invert Style */ +.jfk-bubble.gtx-bubble, .captcheck_answer_label > input + img, span#closed_text > img[src^="https://www.gstatic.com/images/branding/googlelogo"], span[data-href^="https://www.hcaptcha.com/"] > #icon, #bit-notification-bar-iframe, ::-webkit-calendar-picker-indicator { + filter: invert(100%) hue-rotate(180deg) contrast(90%) !important; +} + +/* Variables Style */ +:root { + --darkreader-neutral-background: #131516; + --darkreader-neutral-text: #d8d4cf; + --darkreader-selection-background: #004daa; + --darkreader-selection-text: #e8e6e3; +} + +/* Modified CSS */ +body { + color: rgb(200, 195, 188); +} +h1, +h2, +h3:not(.impl):not(.method), +h4:not(.method) { + color: rgb(232, 230, 227); +} +h1.fqn { + border-bottom-color: rgb(60, 65, 67); +} +h2, +h3:not(.impl):not(.method), +h4:not(.method) { + border-bottom-color: rgb(58, 62, 65); +} +.docblock code { + background-color: rgb(30, 32, 33); +} +pre { + background-color: rgb(30, 32, 33); +} +.sidebar .location { + background-image: initial; + background-color: rgb(41, 44, 46); + color: rgb(200, 195, 188); +} +.block a:hover { + background-image: initial; + background-color: rgb(30, 32, 33); +} +.content pre.line-numbers { + border-color: initial; +} +.line-numbers span { + color: rgb(214, 149, 75); +} +.line-numbers .line-highlighted { + background-color: rgb(68, 73, 2); +} +.content .highlighted { + background-color: rgb(53, 57, 59); + color: rgb(232, 230, 227) !important; +} +.content .highlighted a { + color: rgb(232, 230, 227) !important; +} +.content .highlighted.trait { + background-color: rgb(128, 80, 1); +} +.content .highlighted.mod { + background-color: rgb(29, 54, 86); +} +.content .highlighted.enum { + background-color: rgb(47, 77, 58); +} +.content .highlighted.struct { + background-color: rgb(98, 42, 25); +} +.content .highlighted.fn { + background-color: rgb(78, 55, 59); +} +.docblock h1, +.docblock h2, +.docblock h3, +.docblock h4, +.docblock h5 { + border-bottom-color: rgb(58, 62, 65); +} +nav { + border-bottom-color: rgb(57, 61, 64); +} +nav.main .current { + border-top-color: rgb(140, 130, 115); + border-bottom-color: rgb(140, 130, 115); +} +nav.main .separator { + border-color: rgb(140, 130, 115); +} +a { + text-decoration-color: initial; + color: rgb(232, 230, 227); + background-image: initial; + background-color: transparent; +} +p a { + color: rgb(92, 155, 206); +} +p a:hover { + text-decoration-color: initial; +} +.content a.trait, +.block a.current.trait { + color: rgb(252, 173, 39); +} +.content a.mod, +.block a.current.mod { + color: rgb(115, 156, 193); +} +.content a.enum, +.block a.current.enum { + color: rgb(155, 146, 133); +} +.content a.struct, +.block a.current.struct { + color: rgb(255, 94, 44); +} +.content a.fn, +.block a.current.fn { + color: rgb(157, 149, 136); +} +.content .fnname { + color: rgb(157, 149, 136); +} +.search-input { + outline-color: initial; + border-color: initial; + color: rgb(178, 172, 162); + box-shadow: rgb(42, 45, 47) 0px 0px 0px 1px, + rgba(0, 0, 0, 0) 0px 0px 0px 2px; +} +.search-input:focus { + border-color: initial; + outline-color: initial; + box-shadow: rgb(6, 113, 173) 0px 0px 8px; +} +#help { + background-image: initial; + background-color: rgb(36, 39, 41); + box-shadow: rgba(0, 0, 0, 0.2) 0px 0px 6px; + border-color: rgb(66, 72, 74); +} +#help dt { + border-color: rgb(66, 72, 74); + background-image: initial; + background-color: rgb(24, 26, 27); +} +.stability { + border-left-color: initial; +} +.stability.Deprecated { + border-color: rgb(100, 65, 106); + color: rgb(179, 123, 188); +} +.stability.Experimental { + border-color: rgb(124, 38, 36); + color: rgb(202, 101, 98); +} +.stability.Unstable { + border-color: rgb(124, 95, 36); + color: rgb(202, 167, 98); +} +.stability.Stable { + border-color: rgb(60, 120, 64); + color: rgb(123, 211, 128); +} +.stability.Frozen { + border-color: rgb(0, 211, 70); + color: rgb(121, 255, 164); +} +.stability.Locked { + border-color: rgb(0, 145, 200); + color: rgb(106, 215, 255); +} +.stability.Unmarked { + border-color: rgb(67, 73, 76); +} +.summary.Deprecated { + background-color: rgb(110, 72, 117); +} +.summary.Experimental { + background-color: rgb(129, 40, 37); +} +.summary.Unstable { + background-color: rgb(97, 74, 28); +} +.summary.Stable { + background-color: rgb(67, 134, 71); +} +.summary.Unmarked { + background-color: rgb(62, 68, 70); +} +:target { + background-image: initial; + background-color: rgb(56, 58, 0); +} +pre.rust .kw { + color: rgb(149, 105, 177); +} +pre.rust .kw-2, +pre.rust .prelude-ty { + color: rgb(114, 160, 201); +} +pre.rust .number, +pre.rust .string { + color: rgb(226, 255, 106); +} +pre.rust .self, +pre.rust .boolval, +pre.rust .prelude-val, +pre.rust .attribute, +pre.rust .attribute .ident { + color: rgb(219, 73, 73); +} +pre.rust .comment { + color: rgb(161, 152, 140); +} +pre.rust .doccomment { + color: rgb(184, 178, 168); +} +pre.rust .macro, +pre.rust .macro-nonterminal { + color: rgb(107, 192, 197); +} +pre.rust .lifetime { + color: rgb(236, 158, 81); +} +.methods .section-header { + border-bottom-color: initial !important; +} +.collapse-toggle { + color: rgb(168, 160, 149); +} +.toggle-label { + color: rgb(168, 160, 149); +} + +/* Override Style */ +.vimvixen-hint { + background-color: #7b5300 !important; + border-color: #d8b013 !important; + color: #f3e8c8 !important; +} +::placeholder { + opacity: 0.5 !important; +} +#edge-translate-panel-body, +.MuiTypography-body1, +.nfe-quote-text { + color: var(--darkreader-neutral-text) !important; +} +gr-main-header { + background-color: #0f3a48 !important; +} +.tou-z65h9k, +.tou-mignzq, +.tou-1b6i2ox, +.tou-lnqlqk { + background-color: var(--darkreader-neutral-background) !important; +} +.tou-75mvi { + background-color: #032029 !important; +} +.tou-ta9e87, +.tou-1w3fhi0, +.tou-1b8t2us, +.tou-py7lfi, +.tou-1lpmd9d, +.tou-1frrtv8, +.tou-17ezmgn { + background-color: #0a0a0a !important; +} +.tou-uknfeu { + background-color: #231603 !important; +} +.tou-6i3zyv { + background-color: #19576c !important; +} +embed[type="application/pdf"][src="about:blank"] { filter: invert(100%) contrast(90%); } diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/eluna-logo.png b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/eluna-logo.png new file mode 100644 index 0000000..6694768 Binary files /dev/null and b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/eluna-logo.png differ diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/favicon.ico b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/favicon.ico new file mode 100644 index 0000000..9c83cad Binary files /dev/null and b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/favicon.ico differ diff --git a/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/jquery.js b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/jquery.js new file mode 100644 index 0000000..2adda35 --- /dev/null +++ b/modules/mod-ale/src/LuaEngine/docs/ALEDoc/static/jquery.js @@ -0,0 +1,4 @@ +/*! jQuery v2.1.0 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k="".trim,l={},m=a.document,n="2.1.0",o=function(a,b){return new o.fn.init(a,b)},p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};o.fn=o.prototype={jquery:n,constructor:o,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=o.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return o.each(this,a,b)},map:function(a){return this.pushStack(o.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},o.extend=o.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||o.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(o.isPlainObject(d)||(e=o.isArray(d)))?(e?(e=!1,f=c&&o.isArray(c)?c:[]):f=c&&o.isPlainObject(c)?c:{},g[b]=o.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},o.extend({expando:"jQuery"+(n+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===o.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return a-parseFloat(a)>=0},isPlainObject:function(a){if("object"!==o.type(a)||a.nodeType||o.isWindow(a))return!1;try{if(a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(b){return!1}return!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=o.trim(a),a&&(1===a.indexOf("use strict")?(b=m.createElement("script"),b.text=a,m.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":k.call(a)},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?o.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),o.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||o.guid++,f):void 0},now:Date.now,support:l}),o.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=o.type(a);return"function"===c||o.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s="sizzle"+-new Date,t=a.document,u=0,v=0,w=eb(),x=eb(),y=eb(),z=function(a,b){return a===b&&(j=!0),0},A="undefined",B=1<<31,C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=D.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",M=L.replace("w","w#"),N="\\["+K+"*("+L+")"+K+"*(?:([*^$|!~]?=)"+K+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+M+")|)|)"+K+"*\\]",O=":("+L+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+N.replace(3,8)+")*)|.*)\\)|)",P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(O),U=new RegExp("^"+M+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L.replace("w","w*")+")"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=/'|\\/g,ab=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),bb=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{G.apply(D=H.call(t.childNodes),t.childNodes),D[t.childNodes.length].nodeType}catch(cb){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function db(a,b,d,e){var f,g,h,i,j,m,p,q,u,v;if((b?b.ownerDocument||b:t)!==l&&k(b),b=b||l,d=d||[],!a||"string"!=typeof a)return d;if(1!==(i=b.nodeType)&&9!==i)return[];if(n&&!e){if(f=Z.exec(a))if(h=f[1]){if(9===i){if(g=b.getElementById(h),!g||!g.parentNode)return d;if(g.id===h)return d.push(g),d}else if(b.ownerDocument&&(g=b.ownerDocument.getElementById(h))&&r(b,g)&&g.id===h)return d.push(g),d}else{if(f[2])return G.apply(d,b.getElementsByTagName(a)),d;if((h=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(h)),d}if(c.qsa&&(!o||!o.test(a))){if(q=p=s,u=b,v=9===i&&a,1===i&&"object"!==b.nodeName.toLowerCase()){m=ob(a),(p=b.getAttribute("id"))?q=p.replace(_,"\\$&"):b.setAttribute("id",q),q="[id='"+q+"'] ",j=m.length;while(j--)m[j]=q+pb(m[j]);u=$.test(a)&&mb(b.parentNode)||b,v=m.join(",")}if(v)try{return G.apply(d,u.querySelectorAll(v)),d}catch(w){}finally{p||b.removeAttribute("id")}}}return xb(a.replace(P,"$1"),b,d,e)}function eb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function fb(a){return a[s]=!0,a}function gb(a){var b=l.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function hb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function ib(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||B)-(~a.sourceIndex||B);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function jb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function kb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function lb(a){return fb(function(b){return b=+b,fb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function mb(a){return a&&typeof a.getElementsByTagName!==A&&a}c=db.support={},f=db.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},k=db.setDocument=function(a){var b,e=a?a.ownerDocument||a:t,g=e.defaultView;return e!==l&&9===e.nodeType&&e.documentElement?(l=e,m=e.documentElement,n=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){k()},!1):g.attachEvent&&g.attachEvent("onunload",function(){k()})),c.attributes=gb(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=gb(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(e.getElementsByClassName)&&gb(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=gb(function(a){return m.appendChild(a).id=s,!e.getElementsByName||!e.getElementsByName(s).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==A&&n){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ab,bb);return function(a){var c=typeof a.getAttributeNode!==A&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==A?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==A&&n?b.getElementsByClassName(a):void 0},p=[],o=[],(c.qsa=Y.test(e.querySelectorAll))&&(gb(function(a){a.innerHTML="",a.querySelectorAll("[t^='']").length&&o.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||o.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll(":checked").length||o.push(":checked")}),gb(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&o.push("name"+K+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||o.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),o.push(",.*:")})),(c.matchesSelector=Y.test(q=m.webkitMatchesSelector||m.mozMatchesSelector||m.oMatchesSelector||m.msMatchesSelector))&&gb(function(a){c.disconnectedMatch=q.call(a,"div"),q.call(a,"[s!='']:x"),p.push("!=",O)}),o=o.length&&new RegExp(o.join("|")),p=p.length&&new RegExp(p.join("|")),b=Y.test(m.compareDocumentPosition),r=b||Y.test(m.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},z=b?function(a,b){if(a===b)return j=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===t&&r(t,a)?-1:b===e||b.ownerDocument===t&&r(t,b)?1:i?I.call(i,a)-I.call(i,b):0:4&d?-1:1)}:function(a,b){if(a===b)return j=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],k=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:i?I.call(i,a)-I.call(i,b):0;if(f===g)return ib(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)k.unshift(c);while(h[d]===k[d])d++;return d?ib(h[d],k[d]):h[d]===t?-1:k[d]===t?1:0},e):l},db.matches=function(a,b){return db(a,null,null,b)},db.matchesSelector=function(a,b){if((a.ownerDocument||a)!==l&&k(a),b=b.replace(S,"='$1']"),!(!c.matchesSelector||!n||p&&p.test(b)||o&&o.test(b)))try{var d=q.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return db(b,l,null,[a]).length>0},db.contains=function(a,b){return(a.ownerDocument||a)!==l&&k(a),r(a,b)},db.attr=function(a,b){(a.ownerDocument||a)!==l&&k(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!n):void 0;return void 0!==f?f:c.attributes||!n?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},db.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},db.uniqueSort=function(a){var b,d=[],e=0,f=0;if(j=!c.detectDuplicates,i=!c.sortStable&&a.slice(0),a.sort(z),j){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return i=null,a},e=db.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=db.selectors={cacheLength:50,createPseudo:fb,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ab,bb),a[3]=(a[4]||a[5]||"").replace(ab,bb),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||db.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&db.error(a[0]),a},PSEUDO:function(a){var b,c=!a[5]&&a[2];return V.CHILD.test(a[0])?null:(a[3]&&void 0!==a[4]?a[2]=a[4]:c&&T.test(c)&&(b=ob(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ab,bb).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=w[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&w(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==A&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=db.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),t=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&t){k=q[s]||(q[s]={}),j=k[a]||[],n=j[0]===u&&j[1],m=j[0]===u&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[u,n,m];break}}else if(t&&(j=(b[s]||(b[s]={}))[a])&&j[0]===u)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(t&&((l[s]||(l[s]={}))[a]=[u,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||db.error("unsupported pseudo: "+a);return e[s]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?fb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:fb(function(a){var b=[],c=[],d=g(a.replace(P,"$1"));return d[s]?fb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:fb(function(a){return function(b){return db(a,b).length>0}}),contains:fb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:fb(function(a){return U.test(a||"")||db.error("unsupported lang: "+a),a=a.replace(ab,bb).toLowerCase(),function(b){var c;do if(c=n?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===m},focus:function(a){return a===l.activeElement&&(!l.hasFocus||l.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:lb(function(){return[0]}),last:lb(function(a,b){return[b-1]}),eq:lb(function(a,b,c){return[0>c?c+b:c]}),even:lb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:lb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:lb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:lb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function qb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=v++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[u,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[s]||(b[s]={}),(h=i[d])&&h[0]===u&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function rb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function sb(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function tb(a,b,c,d,e,f){return d&&!d[s]&&(d=tb(d)),e&&!e[s]&&(e=tb(e,f)),fb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||wb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:sb(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=sb(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=sb(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ub(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],i=g||d.relative[" "],j=g?1:0,k=qb(function(a){return a===b},i,!0),l=qb(function(a){return I.call(b,a)>-1},i,!0),m=[function(a,c,d){return!g&&(d||c!==h)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>j;j++)if(c=d.relative[a[j].type])m=[qb(rb(m),c)];else{if(c=d.filter[a[j].type].apply(null,a[j].matches),c[s]){for(e=++j;f>e;e++)if(d.relative[a[e].type])break;return tb(j>1&&rb(m),j>1&&pb(a.slice(0,j-1).concat({value:" "===a[j-2].type?"*":""})).replace(P,"$1"),c,e>j&&ub(a.slice(j,e)),f>e&&ub(a=a.slice(e)),f>e&&pb(a))}m.push(c)}return rb(m)}function vb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,i,j,k){var m,n,o,p=0,q="0",r=f&&[],s=[],t=h,v=f||e&&d.find.TAG("*",k),w=u+=null==t?1:Math.random()||.1,x=v.length;for(k&&(h=g!==l&&g);q!==x&&null!=(m=v[q]);q++){if(e&&m){n=0;while(o=a[n++])if(o(m,g,i)){j.push(m);break}k&&(u=w)}c&&((m=!o&&m)&&p--,f&&r.push(m))}if(p+=q,c&&q!==p){n=0;while(o=b[n++])o(r,s,g,i);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=E.call(j));s=sb(s)}G.apply(j,s),k&&!f&&s.length>0&&p+b.length>1&&db.uniqueSort(j)}return k&&(u=w,h=t),r};return c?fb(f):f}g=db.compile=function(a,b){var c,d=[],e=[],f=y[a+" "];if(!f){b||(b=ob(a)),c=b.length;while(c--)f=ub(b[c]),f[s]?d.push(f):e.push(f);f=y(a,vb(e,d))}return f};function wb(a,b,c){for(var d=0,e=b.length;e>d;d++)db(a,b[d],c);return c}function xb(a,b,e,f){var h,i,j,k,l,m=ob(a);if(!f&&1===m.length){if(i=m[0]=m[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&c.getById&&9===b.nodeType&&n&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(ab,bb),b)||[])[0],!b)return e;a=a.slice(i.shift().value.length)}h=V.needsContext.test(a)?0:i.length;while(h--){if(j=i[h],d.relative[k=j.type])break;if((l=d.find[k])&&(f=l(j.matches[0].replace(ab,bb),$.test(i[0].type)&&mb(b.parentNode)||b))){if(i.splice(h,1),a=f.length&&pb(i),!a)return G.apply(e,f),e;break}}}return g(a,m)(f,b,!n,e,$.test(a)&&mb(b.parentNode)||b),e}return c.sortStable=s.split("").sort(z).join("")===s,c.detectDuplicates=!!j,k(),c.sortDetached=gb(function(a){return 1&a.compareDocumentPosition(l.createElement("div"))}),gb(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||hb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&gb(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||hb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),gb(function(a){return null==a.getAttribute("disabled")})||hb(J,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),db}(a);o.find=t,o.expr=t.selectors,o.expr[":"]=o.expr.pseudos,o.unique=t.uniqueSort,o.text=t.getText,o.isXMLDoc=t.isXML,o.contains=t.contains;var u=o.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(o.isFunction(b))return o.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return o.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return o.filter(b,a,c);b=o.filter(b,a)}return o.grep(a,function(a){return g.call(b,a)>=0!==c})}o.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?o.find.matchesSelector(d,a)?[d]:[]:o.find.matches(a,o.grep(b,function(a){return 1===a.nodeType}))},o.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(o(a).filter(function(){for(b=0;c>b;b++)if(o.contains(e[b],this))return!0}));for(b=0;c>b;b++)o.find(a,e[b],d);return d=this.pushStack(c>1?o.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?o(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=o.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof o?b[0]:b,o.merge(this,o.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:m,!0)),v.test(c[1])&&o.isPlainObject(b))for(c in b)o.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=m.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=m,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):o.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(o):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),o.makeArray(a,this))};A.prototype=o.fn,y=o(m);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};o.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&o(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),o.fn.extend({has:function(a){var b=o(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(o.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?o(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&o.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?o.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(o(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(o.unique(o.merge(this.get(),o(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}o.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return o.dir(a,"parentNode")},parentsUntil:function(a,b,c){return o.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return o.dir(a,"nextSibling")},prevAll:function(a){return o.dir(a,"previousSibling")},nextUntil:function(a,b,c){return o.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return o.dir(a,"previousSibling",c)},siblings:function(a){return o.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return o.sibling(a.firstChild)},contents:function(a){return a.contentDocument||o.merge([],a.childNodes)}},function(a,b){o.fn[a]=function(c,d){var e=o.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=o.filter(d,e)),this.length>1&&(C[a]||o.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return o.each(a.match(E)||[],function(a,c){b[c]=!0}),b}o.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):o.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){o.each(b,function(b,c){var d=o.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&o.each(arguments,function(a,b){var c;while((c=o.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?o.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},o.extend({Deferred:function(a){var b=[["resolve","done",o.Callbacks("once memory"),"resolved"],["reject","fail",o.Callbacks("once memory"),"rejected"],["notify","progress",o.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return o.Deferred(function(c){o.each(b,function(b,f){var g=o.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&o.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?o.extend(a,d):d}},e={};return d.pipe=d.then,o.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&o.isFunction(a.promise)?e:0,g=1===f?a:o.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&o.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;o.fn.ready=function(a){return o.ready.promise().done(a),this},o.extend({isReady:!1,readyWait:1,holdReady:function(a){a?o.readyWait++:o.ready(!0)},ready:function(a){(a===!0?--o.readyWait:o.isReady)||(o.isReady=!0,a!==!0&&--o.readyWait>0||(H.resolveWith(m,[o]),o.fn.trigger&&o(m).trigger("ready").off("ready")))}});function I(){m.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),o.ready()}o.ready.promise=function(b){return H||(H=o.Deferred(),"complete"===m.readyState?setTimeout(o.ready):(m.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},o.ready.promise();var J=o.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===o.type(c)){e=!0;for(h in c)o.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,o.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(o(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};o.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=o.expando+Math.random()}K.uid=1,K.accepts=o.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,o.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(o.isEmptyObject(f))o.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,o.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{o.isArray(b)?d=b.concat(b.map(o.camelCase)):(e=o.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!o.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?o.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}o.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),o.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length; +while(c--)d=g[c].name,0===d.indexOf("data-")&&(d=o.camelCase(d.slice(5)),P(f,d,e[d]));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=o.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),o.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||o.isArray(c)?d=L.access(a,b,o.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=o.queue(a,b),d=c.length,e=c.shift(),f=o._queueHooks(a,b),g=function(){o.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:o.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),o.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";l.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return m.activeElement}catch(a){}}o.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=o.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof o!==U&&o.event.triggered!==b.type?o.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],n=q=h[1],p=(h[2]||"").split(".").sort(),n&&(l=o.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=o.event.special[n]||{},k=o.extend({type:n,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&o.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(n,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),o.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],n=q=h[1],p=(h[2]||"").split(".").sort(),n){l=o.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||o.removeEvent(a,n,r.handle),delete i[n])}else for(n in i)o.event.remove(a,n+b[j],c,d,!0);o.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,p=[d||m],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||m,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+o.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[o.expando]?b:new o.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:o.makeArray(c,[b]),n=o.event.special[q]||{},e||!n.trigger||n.trigger.apply(d,c)!==!1)){if(!e&&!n.noBubble&&!o.isWindow(d)){for(i=n.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||m)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:n.bindType||q,l=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),l&&l.apply(g,c),l=k&&g[k],l&&l.apply&&o.acceptData(g)&&(b.result=l.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||n._default&&n._default.apply(p.pop(),c)!==!1||!o.acceptData(d)||k&&o.isFunction(d[q])&&!o.isWindow(d)&&(h=d[k],h&&(d[k]=null),o.event.triggered=q,d[q](),o.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=o.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=o.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=o.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((o.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?o(e,this).index(i)>=0:o.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*\s*$/g,ib={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return o.nodeName(a,"table")&&o.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)o.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=o.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&o.nodeName(a,b)?o.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}o.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=o.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||o.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,n=a.length;n>m;m++)if(e=a[m],e||0===e)if("object"===o.type(e))o.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;o.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===o.inArray(e,d))&&(i=o.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f,g,h=o.event.special,i=0;void 0!==(c=a[i]);i++){if(o.acceptData(c)&&(f=c[L.expando],f&&(b=L.cache[f]))){if(d=Object.keys(b.events||{}),d.length)for(g=0;void 0!==(e=d[g]);g++)h[e]?o.event.remove(c,e):o.removeEvent(c,e,b.handle);L.cache[f]&&delete L.cache[f]}delete M.cache[c[M.expando]]}}}),o.fn.extend({text:function(a){return J(this,function(a){return void 0===a?o.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?o.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||o.cleanData(ob(c)),c.parentNode&&(b&&o.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(o.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return o.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(o.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,o.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,k=this.length,m=this,n=k-1,p=a[0],q=o.isFunction(p);if(q||k>1&&"string"==typeof p&&!l.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(k&&(c=o.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=o.map(ob(c,"script"),kb),g=f.length;k>j;j++)h=c,j!==n&&(h=o.clone(h,!0,!0),g&&o.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,o.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&o.contains(i,h)&&(h.src?o._evalUrl&&o._evalUrl(h.src):o.globalEval(h.textContent.replace(hb,"")))}return this}}),o.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){o.fn[a]=function(a){for(var c,d=[],e=o(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),o(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d=o(c.createElement(b)).appendTo(c.body),e=a.getDefaultComputedStyle?a.getDefaultComputedStyle(d[0]).display:o.css(d[0],"display");return d.detach(),e}function tb(a){var b=m,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||o("