Fractured: Paragon core hooks, mod-paragon, mod-ale, Docker build cap
- Track mod-paragon and mod-ale (un-ignore modules in .gitignore). - Ship docker-compose.override.yml with CMAKE_EXTRA_OPTIONS for LuaJIT (mod-ale). - Dockerfile: CBUILD_PARALLEL default to limit OOM under Docker/WSL2. - Core: CLASS_PARAGON sticky combo points (DetachComboTarget), selection rebind, Spell::CheckPower rune path for multi-resource Paragon. - spell_dk_death_rune: IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY) for Blood of the North / Reaping / DRM on Paragon. - Remove temporary Paragon CheckPower logging. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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
|
||||
|
||||
+11
-1
@@ -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"
|
||||
|
||||
#############################
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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.
|
||||
## =======================================================
|
||||
@@ -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
|
||||
+4
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 64 KiB |
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -0,0 +1,93 @@
|
||||
<div align="center">
|
||||
|
||||
# [](https://github.com/ElunaLuaEngine/Eluna)
|
||||
|
||||
*Unleash the power of Lua scripting in your AzerothCore server*
|
||||
|
||||
[](https://discord.com/invite/ZKSVREE7)
|
||||
[](http://www.lua.org/manual/5.2/)
|
||||
[](http://www.azerothcore.org/)
|
||||
|
||||
---
|
||||
</div>
|
||||
|
||||
> [!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]
|
||||
```
|
||||
|
||||
<div align="center">
|
||||
|
||||
---
|
||||
<sub>Made with ❤️ by the Eluna Community</sub>
|
||||
|
||||
[⬆ Back to Top](#)
|
||||
</div>
|
||||
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
@@ -0,0 +1,154 @@
|
||||
<div align="center">
|
||||
|
||||
# ALE - AzerothCore Lua Engine
|
||||
|
||||
*Unleash the power of Lua scripting in your AzerothCore server*
|
||||
|
||||
[](https://discord.com/invite/sqkPb623)
|
||||
[](http://www.lua.org/manual/5.2/)
|
||||
[](http://www.azerothcore.org/)
|
||||
|
||||
---
|
||||
</div>
|
||||
|
||||
> [!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 <azerothcore-path>/modules
|
||||
|
||||
# Clone the mod-ale repository
|
||||
git clone https://github.com/azerothcore/mod-ale.git
|
||||
|
||||
# Configure build with your preferred Lua version
|
||||
cd <azerothcore-build-directory>
|
||||
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
|
||||

|
||||
|
||||
### 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.
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<sub>Developed with ❤️ by the AzerothCore and ALE community</sub>
|
||||
|
||||
[⬆ Back to Top](#-overview)
|
||||
</div>
|
||||
@@ -0,0 +1,92 @@
|
||||
### [](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服务器,在那里您将能够找到社区提供的资源,版本和支持:
|
||||
<a href="https://discord.gg/bjkCVWqqfX">
|
||||
<img src="https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat" />
|
||||
</a>
|
||||
|
||||
官方的Azerothcore Discord服务器也提供了一个专门用于lua开发的通道:
|
||||
<a href="https://discord.gg/gkt4y2x">
|
||||
<img src="https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat" />
|
||||
</a>
|
||||
|
||||
#  mod-eluna for AzerothCore
|
||||
- azerothcore 的最新构建状态:[](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
|
||||
@@ -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/)
|
||||
@@ -0,0 +1 @@
|
||||
theme: jekyll-theme-architect
|
||||
@@ -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
|
||||
@@ -0,0 +1,437 @@
|
||||
<div align="center">
|
||||
|
||||
# 🤝 Contributing to ALE
|
||||
|
||||
*Help improve the AzerothCore Lua Engine*
|
||||
|
||||
[](https://discord.com/invite/ZKSVREE7)
|
||||
[](https://github.com/azerothcore/mod-ale/issues)
|
||||
|
||||
---
|
||||
</div>
|
||||
|
||||
> [!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<uint32> GetPlayerQuests(Player* player)
|
||||
{
|
||||
std::vector<uint32> 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)**
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<sub>Developed with ❤️ by the AzerothCore and ALE community</sub>
|
||||
|
||||
Thank you for contributing! 🎉
|
||||
|
||||
[⬆ Back to Top](#-contributing-to-ale)
|
||||
</div>
|
||||
@@ -0,0 +1,430 @@
|
||||
<div align="center">
|
||||
|
||||
# ⚙️ ALE Implementation Details
|
||||
|
||||
*Advanced features and technical documentation for ALE*
|
||||
|
||||
[](https://discord.com/invite/ZKSVREE7)
|
||||
[](http://www.azerothcore.org/)
|
||||
|
||||
---
|
||||
</div>
|
||||
|
||||
> [!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)**
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<sub>Developed with ❤️ by the AzerothCore and ALE community</sub>
|
||||
|
||||
[⬆ Back to Top](#-ale-implementation-details)
|
||||
</div>
|
||||
@@ -0,0 +1,308 @@
|
||||
<div align="center">
|
||||
|
||||
# 🔧 ALE Installation Guide
|
||||
|
||||
*Step-by-step instructions for installing ALE on AzerothCore*
|
||||
|
||||
[](https://discord.com/invite/ZKSVREE7)
|
||||
[](http://www.azerothcore.org/)
|
||||
|
||||
---
|
||||
</div>
|
||||
|
||||
> [!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 <azerothcore-path>/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 <azerothcore-build-directory>
|
||||
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 <azerothcore-path>/modules/mod-ale
|
||||
```
|
||||
|
||||
2. **Pull the latest changes:**
|
||||
|
||||
```bash
|
||||
git pull
|
||||
```
|
||||
|
||||
3. **Navigate to your build directory:**
|
||||
|
||||
```bash
|
||||
cd <azerothcore-build-directory>
|
||||
```
|
||||
|
||||
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)**
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<sub>Developed with ❤️ by the AzerothCore and ALE community</sub>
|
||||
|
||||
[⬆ Back to Top](#-ale-installation-guide)
|
||||
</div>
|
||||
@@ -0,0 +1,407 @@
|
||||
<div align="center">
|
||||
|
||||
# 🔀 Advanced ALE Integration
|
||||
|
||||
*Customizing and extending ALE for your specific needs*
|
||||
|
||||
[](https://discord.com/invite/ZKSVREE7)
|
||||
[](http://www.azerothcore.org/)
|
||||
|
||||
---
|
||||
</div>
|
||||
|
||||
> [!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 <azerothcore-path>/modules
|
||||
|
||||
# Clone ALE
|
||||
git clone https://github.com/azerothcore/mod-ale.git
|
||||
|
||||
# Rebuild AzerothCore
|
||||
cd <azerothcore-build-directory>
|
||||
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 <your-azerothcore-fork>
|
||||
|
||||
# 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 <your-azerothcore-fork>/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 <resolved-file>
|
||||
|
||||
# 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)**
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<sub>Developed with ❤️ by the AzerothCore and ALE community</sub>
|
||||
|
||||
[⬆ Back to Top](#-advanced-ale-integration)
|
||||
</div>
|
||||
@@ -0,0 +1,279 @@
|
||||
<div align="center">
|
||||
|
||||
# 📖 ALE Usage Guide
|
||||
|
||||
*Learn how to create powerful Lua scripts for your AzerothCore server*
|
||||
|
||||
[](https://discord.com/invite/ZKSVREE7)
|
||||
[](http://www.lua.org/manual/5.2/)
|
||||
|
||||
---
|
||||
</div>
|
||||
|
||||
> [!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)**
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<sub>Developed with ❤️ by the AzerothCore and ALE community</sub>
|
||||
|
||||
[⬆ Back to Top](#-ale-usage-guide)
|
||||
</div>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// From SC
|
||||
void AddSC_ALE();
|
||||
|
||||
// Add all
|
||||
void Addmod_aleScripts()
|
||||
{
|
||||
AddSC_ALE();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@@ -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 <<CONFIG_SH
|
||||
MTHREADS=4
|
||||
CWARNINGS=ON
|
||||
CDEBUG=OFF
|
||||
CTYPE=Release
|
||||
CSCRIPTS=static
|
||||
CUNIT_TESTS=ON
|
||||
CSERVERS=ON
|
||||
CTOOLS=ON
|
||||
CSCRIPTPCH=ON
|
||||
CCOREPCH=ON
|
||||
CCUSTOMOPTIONS='-DCMAKE_C_FLAGS="-Werror" -DCMAKE_CXX_FLAGS="-Werror"'
|
||||
DB_CHARACTERS_CONF="MYSQL_USER='root'; MYSQL_PASS='root'; MYSQL_HOST='localhost';"
|
||||
DB_AUTH_CONF="MYSQL_USER='root'; MYSQL_PASS='root'; MYSQL_HOST='localhost';"
|
||||
DB_WORLD_CONF="MYSQL_USER='root'; MYSQL_PASS='root'; MYSQL_HOST='localhost';"
|
||||
CONFIG_SH
|
||||
|
||||
time sudo apt-get update -y
|
||||
# time sudo apt-get upgrade -y
|
||||
time sudo apt-get install -y git lsb-release sudo ccache
|
||||
time ./acore.sh install-deps
|
||||
|
||||
time sudo apt-get install -y clang-11
|
||||
echo "CCOMPILERC=\"clang-11\"" >> ./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
|
||||
@@ -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
|
||||
@@ -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'
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "ALEConfig.h"
|
||||
|
||||
ALEConfig& ALEConfig::GetInstance()
|
||||
{
|
||||
static ALEConfig instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
ALEConfig::ALEConfig() : ConfigValueCache<ALEConfigValues>(ALEConfigValues::CONFIG_VALUE_COUNT)
|
||||
{
|
||||
}
|
||||
|
||||
void ALEConfig::Initialize(bool reload)
|
||||
{
|
||||
ConfigValueCache<ALEConfigValues>::Initialize(reload);
|
||||
}
|
||||
|
||||
void ALEConfig::BuildConfigCache()
|
||||
{
|
||||
SetConfigValue<bool>(ALEConfigValues::ENABLED, "ALE.Enabled", "false");
|
||||
SetConfigValue<bool>(ALEConfigValues::TRACEBACK_ENABLED, "ALE.TraceBack", "false");
|
||||
SetConfigValue<bool>(ALEConfigValues::AUTORELOAD_ENABLED, "ALE.AutoReload", "false");
|
||||
SetConfigValue<bool>(ALEConfigValues::BYTECODE_CACHE_ENABLED, "ALE.BytecodeCache", "false");
|
||||
|
||||
SetConfigValue<std::string>(ALEConfigValues::SCRIPT_PATH, "ALE.ScriptPath", "lua_scripts");
|
||||
SetConfigValue<std::string>(ALEConfigValues::REQUIRE_PATH, "ALE.RequirePaths", "");
|
||||
SetConfigValue<std::string>(ALEConfigValues::REQUIRE_CPATH, "ALE.RequireCPaths", "");
|
||||
|
||||
SetConfigValue<uint32>(ALEConfigValues::AUTORELOAD_INTERVAL, "ALE.AutoReloadInterval", 1);
|
||||
}
|
||||
@@ -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<ALEConfigValues>
|
||||
{
|
||||
public:
|
||||
static ALEConfig& GetInstance();
|
||||
|
||||
void Initialize(bool reload = false);
|
||||
|
||||
bool IsALEEnabled() const { return GetConfigValue<bool>(ALEConfigValues::ENABLED); }
|
||||
bool IsTraceBackEnabled() const { return GetConfigValue<bool>(ALEConfigValues::TRACEBACK_ENABLED); }
|
||||
bool IsAutoReloadEnabled() const { return GetConfigValue<bool>(ALEConfigValues::AUTORELOAD_ENABLED); }
|
||||
bool IsByteCodeCacheEnabled() const { return GetConfigValue<bool>(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<uint32>(ALEConfigValues::AUTORELOAD_INTERVAL); }
|
||||
|
||||
protected:
|
||||
void BuildConfigCache() override;
|
||||
|
||||
private:
|
||||
ALEConfig();
|
||||
~ALEConfig() = default;
|
||||
ALEConfig(const ALEConfig&) = delete;
|
||||
ALEConfig& operator=(const ALEConfig&) = delete;
|
||||
};
|
||||
|
||||
#endif // ALE_CONFIG_H
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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<uint32, uint32> > 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
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "ALEDBCRegistry.h"
|
||||
|
||||
std::vector<DBCDefinition> dbcRegistry = {
|
||||
REGISTER_DBC(GemProperties, GemPropertiesEntry, sGemPropertiesStore),
|
||||
REGISTER_DBC(Spell, SpellEntry, sSpellStore),
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef ALEDBCREGISTRY_H
|
||||
#define ALEDBCREGISTRY_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <typeinfo>
|
||||
|
||||
#include "DBCStores.h"
|
||||
#include "LuaEngine.h"
|
||||
|
||||
struct DBCDefinition
|
||||
{
|
||||
std::string name;
|
||||
void* storage;
|
||||
const std::type_info& type;
|
||||
std::function<const void*(uint32)> lookupFunction;
|
||||
std::function<void(lua_State*, const void*)> pushFunction;
|
||||
};
|
||||
|
||||
extern std::vector<DBCDefinition> dbcRegistry;
|
||||
|
||||
#define REGISTER_DBC(dbcName, entryType, store) \
|
||||
{ \
|
||||
#dbcName, \
|
||||
reinterpret_cast<void*>(&store), \
|
||||
typeid(DBCStorage<entryType>), \
|
||||
[](uint32 id) -> const void* { \
|
||||
return store.LookupEntry(id); \
|
||||
}, \
|
||||
[](lua_State* L, const void* entry) { \
|
||||
auto cast_entry = static_cast<const entryType*>(entry); \
|
||||
ALE::Push(L, *cast_entry); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif // ALEDBCREGISTRY_H
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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<uint64, LuaEvent*>(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);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 <map>
|
||||
|
||||
#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<uint64, LuaEvent*> EventList;
|
||||
typedef std::unordered_map<int, LuaEvent*> 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<ALEEventProcessor*> 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
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 <boost/filesystem.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 <thread>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <boost/filesystem.hpp>
|
||||
#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<bool> running;
|
||||
std::string watchPath;
|
||||
uint32 checkInterval;
|
||||
|
||||
std::map<std::string, std::time_t> fileTimestamps;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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<ALEInstanceAI*>(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<ALEInstanceAI*>(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<uint32>(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<ALEInstanceAI*>(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<uint64>(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)
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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<ALEInstanceAI*>(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
|
||||
@@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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<luaL_Reg*>(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<typename T>
|
||||
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<typename T>
|
||||
struct ALERegister
|
||||
{
|
||||
const char* name;
|
||||
int(*mfunc)(lua_State*, T*);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
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/<core>
|
||||
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<typename C>
|
||||
static void SetMethods(ALE* E, ALERegister<C>* 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<ALEObject**>(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<T*>(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<T*>(ALEObj->GetObj());
|
||||
}
|
||||
|
||||
static int GetType(lua_State* L)
|
||||
{
|
||||
lua_pushstring(L, tname);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int SetInvalidation(lua_State* L)
|
||||
{
|
||||
ALEObject* ALEObj = ALE::CHECKOBJ<ALEObject>(L, 1);
|
||||
bool invalidate = ALE::CHECKVAL<bool>(L, 2);
|
||||
|
||||
ALEObj->SetValidation(invalidate);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int CallMethod(lua_State* L)
|
||||
{
|
||||
T* obj = ALE::CHECKOBJ<T>(L, 1); // get self
|
||||
if (!obj)
|
||||
return 0;
|
||||
ALERegister<T>* l = static_cast<ALERegister<T>*>(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<Vehicle>::CollectGarbage
|
||||
static int CollectGarbage(lua_State* L)
|
||||
{
|
||||
// Get object pointer (and check type, no error)
|
||||
ALEObject* obj = ALE::CHECKOBJ<ALEObject>(L, 1, false);
|
||||
if (obj && manageMemory)
|
||||
delete static_cast<T*>(obj->GetObj());
|
||||
delete obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ToString(lua_State* L)
|
||||
{
|
||||
T* obj = ALE::CHECKOBJ<T>(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<T>(L, 1) == ALE::CHECKOBJ<T>(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<typename T>
|
||||
ALEObject::ALEObject(T * obj, bool manageMemory) : callstackid(1), _invalidate(!manageMemory), object(obj), type_name(ALETemplate<T>::tname)
|
||||
{
|
||||
SetValid(true);
|
||||
}
|
||||
|
||||
template<typename T> const char* ALETemplate<T>::tname = NULL;
|
||||
template<typename T> bool ALETemplate<T>::manageMemory = false;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#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<LockType> 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
|
||||
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 <memory>
|
||||
#include "Common.h"
|
||||
#include "ALEUtility.h"
|
||||
#include <type_traits>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* A set of bindings from keys of type `K` to Lua references.
|
||||
*/
|
||||
template<typename K>
|
||||
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<Binding> > BindingList;
|
||||
|
||||
std::unordered_map<K, BindingList> 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<uint64, BindingList*> 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<Binding>(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>& 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>& 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>& 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 <typename T>
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
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 <typename T1, typename T2, typename... T>
|
||||
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 <typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
|
||||
static inline result_type hash(T const & t)
|
||||
{
|
||||
return std::hash<typename std::underlying_type<T>::type>()(t);
|
||||
}
|
||||
|
||||
template <typename T, typename std::enable_if<!std::is_enum<T>::value>::type* = nullptr>
|
||||
static inline result_type hash(T const & t)
|
||||
{
|
||||
return std::hash<T>()(t);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
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 <typename H, typename T1, typename... T>
|
||||
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<typename T>
|
||||
struct equal_to < EventKey<T> >
|
||||
{
|
||||
bool operator()(EventKey<T> const& lhs, EventKey<T> const& rhs) const
|
||||
{
|
||||
return lhs.event_id == rhs.event_id;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct equal_to < EntryKey<T> >
|
||||
{
|
||||
bool operator()(EntryKey<T> const& lhs, EntryKey<T> const& rhs) const
|
||||
{
|
||||
return lhs.event_id == rhs.event_id
|
||||
&& lhs.entry == rhs.entry;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct equal_to < UniqueObjectKey<T> >
|
||||
{
|
||||
bool operator()(UniqueObjectKey<T> const& lhs, UniqueObjectKey<T> const& rhs) const
|
||||
{
|
||||
return lhs.event_id == rhs.event_id
|
||||
&& lhs.guid == rhs.guid
|
||||
&& lhs.instance_id == rhs.instance_id;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct hash < EventKey<T> >
|
||||
{
|
||||
typedef EventKey<T> argument_type;
|
||||
|
||||
hash_helper::result_type operator()(argument_type const& k) const
|
||||
{
|
||||
return hash_helper::hash(k.event_id);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct hash < EntryKey<T> >
|
||||
{
|
||||
typedef EntryKey<T> argument_type;
|
||||
|
||||
hash_helper::result_type operator()(argument_type const& k) const
|
||||
{
|
||||
return hash_helper::hash(k.event_id, k.entry);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct hash < UniqueObjectKey<T> >
|
||||
{
|
||||
typedef UniqueObjectKey<T> 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
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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<typename K1, typename K2>
|
||||
int ALE::SetupStack(BindingMap<K1>* bindings1, BindingMap<K2>* 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<typename T>
|
||||
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<typename K1, typename K2>
|
||||
void ALE::CallAllFunctions(BindingMap<K1>* bindings1, BindingMap<K2>* 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<typename K1, typename K2>
|
||||
bool ALE::CallAllFunctionsBool(BindingMap<K1>* bindings1, BindingMap<K2>* 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
|
||||
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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<int>(L, r + 0);
|
||||
* int second = CHECKVAL<int>(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
|
||||
@@ -0,0 +1,275 @@
|
||||
#include <thread>
|
||||
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<std::mutex> 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<std::mutex> 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef ALE_HTTP_MANAGER_H
|
||||
#define ALE_HTTP_MANAGER_H
|
||||
|
||||
#include <regex>
|
||||
|
||||
#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<HttpWorkItem*> workQueue;
|
||||
rigtorp::SPSCQueue<HttpResponse*> 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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,627 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* 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 <mutex>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <lua.h>
|
||||
};
|
||||
|
||||
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<typename T> class ALETemplate;
|
||||
|
||||
template<typename K> class BindingMap;
|
||||
template<typename T> struct EventKey;
|
||||
template<typename T> struct EntryKey;
|
||||
template<typename T> struct UniqueObjectKey;
|
||||
|
||||
// Type definition for bytecode buffer
|
||||
typedef std::vector<uint8> 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<LuaScript> ScriptList;
|
||||
|
||||
typedef std::recursive_mutex LockType;
|
||||
typedef std::lock_guard<LockType> 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<ALEFileWatcher> 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<uint32, int> instanceDataRefs;
|
||||
// Map from map ID -> Lua table ref
|
||||
std::unordered_map<uint32, int> 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<typename K1, typename K2> int SetupStack(BindingMap<K1>* bindings1, BindingMap<K2>* 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<typename T> void ReplaceArgument(T value, uint8 index);
|
||||
template<typename K1, typename K2> void CallAllFunctions(BindingMap<K1>* bindings1, BindingMap<K2>* bindings2, const K1& key1, const K2& key2);
|
||||
template<typename K1, typename K2> bool CallAllFunctionsBool(BindingMap<K1>* bindings1, BindingMap<K2>* 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<typename K> int SetupStack(BindingMap<K>* bindings, const K& key, int number_of_arguments)
|
||||
{
|
||||
return SetupStack<K, K>(bindings, NULL, key, key, number_of_arguments);
|
||||
}
|
||||
template<typename K> void CallAllFunctions(BindingMap<K>* bindings, const K& key)
|
||||
{
|
||||
CallAllFunctions<K, K>(bindings, NULL, key, key);
|
||||
}
|
||||
template<typename K> bool CallAllFunctionsBool(BindingMap<K>* bindings, const K& key, bool default_value = false)
|
||||
{
|
||||
return CallAllFunctionsBool<K, K>(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<typename T>
|
||||
void Push(T const* ptr) { Push(L, ptr); ++push_counter; }
|
||||
|
||||
public:
|
||||
static ALE* GALE;
|
||||
|
||||
lua_State* L;
|
||||
EventMgr* eventMgr;
|
||||
HttpManager httpManager;
|
||||
QueryCallbackProcessor queryProcessor;
|
||||
EventEmitter<void(std::string)> OnError;
|
||||
|
||||
BindingMap< EventKey<Hooks::ServerEvents> >* ServerEventBindings;
|
||||
BindingMap< EventKey<Hooks::PlayerEvents> >* PlayerEventBindings;
|
||||
BindingMap< EventKey<Hooks::GuildEvents> >* GuildEventBindings;
|
||||
BindingMap< EventKey<Hooks::GroupEvents> >* GroupEventBindings;
|
||||
BindingMap< EventKey<Hooks::VehicleEvents> >* VehicleEventBindings;
|
||||
BindingMap< EventKey<Hooks::BGEvents> >* BGEventBindings;
|
||||
BindingMap< EventKey<Hooks::AllCreatureEvents> >* AllCreatureEventBindings;
|
||||
|
||||
BindingMap< EntryKey<Hooks::PacketEvents> >* PacketEventBindings;
|
||||
BindingMap< EntryKey<Hooks::CreatureEvents> >* CreatureEventBindings;
|
||||
BindingMap< EntryKey<Hooks::GossipEvents> >* CreatureGossipBindings;
|
||||
BindingMap< EntryKey<Hooks::GameObjectEvents> >* GameObjectEventBindings;
|
||||
BindingMap< EntryKey<Hooks::GossipEvents> >* GameObjectGossipBindings;
|
||||
BindingMap< EntryKey<Hooks::ItemEvents> >* ItemEventBindings;
|
||||
BindingMap< EntryKey<Hooks::GossipEvents> >* ItemGossipBindings;
|
||||
BindingMap< EntryKey<Hooks::GossipEvents> >* PlayerGossipBindings;
|
||||
BindingMap< EntryKey<Hooks::InstanceEvents> >* MapEventBindings;
|
||||
BindingMap< EntryKey<Hooks::InstanceEvents> >* InstanceEventBindings;
|
||||
BindingMap< EventKey<Hooks::TicketEvents> >* TicketEventBindings;
|
||||
BindingMap< EntryKey<Hooks::SpellEvents> >* SpellEventBindings;
|
||||
|
||||
BindingMap< UniqueObjectKey<Hooks::CreatureEvents> >* 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<ALE*>(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<typename T>
|
||||
static void Push(lua_State* luastate, T const* ptr)
|
||||
{
|
||||
ALETemplate<T>::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<typename T> static T CHECKVAL(lua_State* luastate, int narg);
|
||||
template<typename T> static T CHECKVAL(lua_State* luastate, int narg, T def)
|
||||
{
|
||||
return lua_isnoneornil(luastate, narg) ? def : CHECKVAL<T>(luastate, narg);
|
||||
}
|
||||
template<typename T> static T* CHECKOBJ(lua_State* luastate, int narg, bool error = true)
|
||||
{
|
||||
return ALETemplate<T>::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<Unit>(lua_State* L, int narg, bool error);
|
||||
template<> Object* ALE::CHECKOBJ<Object>(lua_State* L, int narg, bool error);
|
||||
template<> WorldObject* ALE::CHECKOBJ<WorldObject>(lua_State* L, int narg, bool error);
|
||||
template<> ALEObject* ALE::CHECKOBJ<ALEObject>(lua_State* L, int narg, bool error);
|
||||
|
||||
#define sALE ALE::GALE
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
# Ignore the temporary "build" folder.
|
||||
build
|
||||
@@ -0,0 +1,2 @@
|
||||
*.pyc
|
||||
.idea
|
||||
@@ -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, '<a class="fn" href="{}">{}</a>'.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, '<a class="mod" href="{}">{}</a>'.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 '<strong><a href="{}">{}</a></strong>'.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 '<strong><a class="mod" href="{}">{}</a></strong>'.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 '<strong><a href="{}">{}</a></strong>'.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)
|
||||
@@ -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 += '<p><em>Valid numbers</em>: integers from {0} to {1}.</p>'.format(range[0], range[1])
|
||||
else:
|
||||
self.description += '<p><em>Valid numbers</em>: all decimal numbers.</p>'
|
||||
|
||||
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('</p>')[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('</p>')[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()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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%); }
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
File diff suppressed because one or more lines are too long
@@ -0,0 +1,520 @@
|
||||
/**
|
||||
* Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
* file at the top-level directory of this distribution and at
|
||||
* http://rust-lang.org/COPYRIGHT.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
* option. This file may not be copied, modified, or distributed
|
||||
* except according to those terms.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: 'Fira Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Fira Sans'), url("FiraSans-Regular.woff") format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Fira Sans';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Fira Sans Medium'), url("FiraSans-Medium.woff") format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Serif Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Source Serif Pro'), url("SourceSerifPro-Regular.woff") format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Serif Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: url("Heuristica-Italic.woff") format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Serif Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Source Serif Pro Bold'), url("SourceSerifPro-Bold.woff") format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Code Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Source Code Pro'), url("SourceCodePro-Regular.woff") format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Code Pro';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Source Code Pro Semibold'), url("SourceCodePro-Semibold.woff") format('woff');
|
||||
}
|
||||
|
||||
@import "normalize.css";
|
||||
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* General structure and fonts */
|
||||
|
||||
body {
|
||||
color: #333;
|
||||
min-width: 500px;
|
||||
font: 16px/1.4 "Source Serif Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
padding: 10px 15px 20px 15px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.4em;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
h1, h2, h3:not(.impl):not(.method), h4:not(.method) {
|
||||
color: black;
|
||||
font-weight: 500;
|
||||
margin: 20px 0 15px 0;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
h1.fqn {
|
||||
border-bottom: 1px dashed #D5D5D5;
|
||||
margin-top: 0;
|
||||
}
|
||||
h2, h3:not(.impl):not(.method), h4:not(.method) {
|
||||
border-bottom: 1px solid #DDDDDD;
|
||||
}
|
||||
h3.impl, h3.method, h4.method {
|
||||
font-weight: 600;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
h3.impl, h3.method {
|
||||
margin-top: 15px;
|
||||
}
|
||||
h1, h2, h3, h4, section.sidebar, a.source, .search-input, .content table a, .collapse-toggle {
|
||||
font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
ol, ul {
|
||||
padding-left: 25px;
|
||||
}
|
||||
ul ul, ol ul, ul ol, ol ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 .6em 0;
|
||||
}
|
||||
|
||||
code, pre {
|
||||
font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", Inconsolata, monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.docblock code {
|
||||
background-color: #F5F5F5;
|
||||
border-radius: 3px;
|
||||
/*padding: 0 0.2em;*/
|
||||
}
|
||||
pre {
|
||||
background-color: #F5F5F5;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.source pre {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content.source {
|
||||
margin-top: 50px;
|
||||
max-width: none;
|
||||
overflow: visible;
|
||||
margin-left: 0px;
|
||||
min-width: 70em;
|
||||
}
|
||||
|
||||
nav.sub {
|
||||
font-size: 16px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 200px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.content, nav { max-width: 960px; }
|
||||
|
||||
/* Everything else */
|
||||
|
||||
.js-only, .hidden { display: none; }
|
||||
|
||||
.sidebar {
|
||||
padding: 10px;
|
||||
}
|
||||
.sidebar img {
|
||||
margin: 20px auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.sidebar .location {
|
||||
font-size: 17px;
|
||||
margin: 30px 0 20px 0;
|
||||
background: #e1e1e1;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.block {
|
||||
padding: 0 10px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.block h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.block a {
|
||||
display: block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
line-height: 15px;
|
||||
padding: 7px 5px;
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
transition: border 500ms ease-out;
|
||||
}
|
||||
|
||||
.block a:hover {
|
||||
background: #F5F5F5;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.content.source pre.rust {
|
||||
white-space: pre;
|
||||
overflow: auto;
|
||||
padding-left: 0;
|
||||
}
|
||||
.content pre.line-numbers { float: left; border: none; }
|
||||
.line-numbers span { color: #c67e2d; }
|
||||
.line-numbers .line-highlighted {
|
||||
background-color: #f6fdb0;
|
||||
}
|
||||
|
||||
.content .highlighted {
|
||||
cursor: pointer;
|
||||
color: #000 !important;
|
||||
background-color: #ccc;
|
||||
}
|
||||
.content .highlighted a { color: #000 !important; }
|
||||
.content .highlighted.trait { background-color: #fece7e; }
|
||||
.content .highlighted.mod { background-color: #afc6e4; }
|
||||
.content .highlighted.enum { background-color: #b4d1b9; }
|
||||
.content .highlighted.struct { background-color: #e7b1a0; }
|
||||
.content .highlighted.fn { background-color: #c6afb3; }
|
||||
|
||||
.docblock.short.nowrap {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.docblock.short p {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0;
|
||||
}
|
||||
.docblock.short code { white-space: nowrap; }
|
||||
|
||||
.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5 {
|
||||
border-bottom: 1px solid #DDD;
|
||||
}
|
||||
|
||||
.docblock h1 { font-size: 1.3em; }
|
||||
.docblock h2 { font-size: 1.15em; }
|
||||
.docblock h3, .docblock h4, .docblock h5 { font-size: 1em; }
|
||||
|
||||
.content .out-of-band {
|
||||
float: right;
|
||||
font-size: 23px;
|
||||
}
|
||||
|
||||
.content table {
|
||||
border-spacing: 0 5px;
|
||||
border-collapse: separate;
|
||||
}
|
||||
.content td { vertical-align: top; }
|
||||
.content td:first-child { padding-right: 20px; }
|
||||
.content td p:first-child { margin-top: 0; }
|
||||
.content td h1, .content td h2 { margin-left: 0; font-size: 1.1em; }
|
||||
|
||||
.content .item-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.content .item-list li { margin-bottom: 3px; }
|
||||
|
||||
.content .multi-column {
|
||||
-moz-column-count: 5;
|
||||
-moz-column-gap: 2.5em;
|
||||
-webkit-column-count: 5;
|
||||
-webkit-column-gap: 2.5em;
|
||||
column-count: 5;
|
||||
column-gap: 2.5em;
|
||||
}
|
||||
.content .multi-column li { width: 100%; display: inline-block; }
|
||||
|
||||
.content .method {
|
||||
font-size: 1em;
|
||||
position: relative;
|
||||
}
|
||||
.content .methods .docblock { margin-left: 40px; }
|
||||
|
||||
.content .impl-methods .docblock { margin-left: 40px; }
|
||||
|
||||
nav {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
nav.main {
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
nav.main .current {
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
nav.main .separator {
|
||||
border: 1px solid #000;
|
||||
display: inline-block;
|
||||
height: 23px;
|
||||
margin: 0 20px;
|
||||
}
|
||||
nav.sum { text-align: right; }
|
||||
nav.sub form { display: inline; }
|
||||
|
||||
nav, .content {
|
||||
margin-left: 230px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
background: transparent;
|
||||
}
|
||||
p a { color: #4e8bca; }
|
||||
p a:hover { text-decoration: underline; }
|
||||
|
||||
.content a.trait, .block a.current.trait { color: #ed9603; }
|
||||
.content a.mod, .block a.current.mod { color: #4d76ae; }
|
||||
.content a.enum, .block a.current.enum { color: #5e9766; }
|
||||
.content a.struct, .block a.current.struct { color: #e53700; }
|
||||
.content a.fn, .block a.current.fn { color: #8c6067; }
|
||||
.content .fnname { color: #8c6067; }
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
/* Override Normalize.css: we have margins and do
|
||||
not want to overflow - the `moz` attribute is necessary
|
||||
until Firefox 29, too early to drop at this point */
|
||||
-moz-box-sizing: border-box !important;
|
||||
box-sizing: border-box !important;
|
||||
outline: none;
|
||||
border: none;
|
||||
border-radius: 1px;
|
||||
color: #555;
|
||||
margin-top: 5px;
|
||||
padding: 10px 16px;
|
||||
font-size: 17px;
|
||||
box-shadow: 0 0 0 1px #e0e0e0, 0 0 0 2px transparent;
|
||||
transition: border-color 300ms ease;
|
||||
transition: border-radius 300ms ease-in-out;
|
||||
transition: box-shadow 300ms ease-in-out;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
border-color: #66afe9;
|
||||
border-radius: 2px;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
box-shadow: 0 0 8px #078dd8;
|
||||
}
|
||||
|
||||
.search-results .desc {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#help {
|
||||
background: #e9e9e9;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 6px rgba(0,0,0,.2);
|
||||
position: absolute;
|
||||
top: 300px;
|
||||
left: 50%;
|
||||
margin-top: -125px;
|
||||
margin-left: -275px;
|
||||
width: 550px;
|
||||
height: 300px;
|
||||
border: 1px solid #bfbfbf;
|
||||
}
|
||||
|
||||
#help dt {
|
||||
float: left;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #bfbfbf;
|
||||
background: #fff;
|
||||
width: 23px;
|
||||
text-align: center;
|
||||
clear: left;
|
||||
display: block;
|
||||
margin-top: -1px;
|
||||
}
|
||||
#help dd { margin: 5px 33px; }
|
||||
#help .infos { padding-left: 0; }
|
||||
#help h1 { margin-top: 0; }
|
||||
#help div {
|
||||
width: 50%;
|
||||
float: left;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.stability {
|
||||
border-left: 6px solid;
|
||||
padding: 3px 6px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
h1 .stability {
|
||||
text-transform: lowercase;
|
||||
font-weight: 400;
|
||||
margin-left: 14px;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
.impl-methods .stability, .methods .stability {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.stability.Deprecated { border-color: #A071A8; color: #82478C; }
|
||||
.stability.Experimental { border-color: #D46D6A; color: #AA3C39; }
|
||||
.stability.Unstable { border-color: #D4B16A; color: #AA8439; }
|
||||
.stability.Stable { border-color: #54A759; color: #2D8632; }
|
||||
.stability.Frozen { border-color: #009431; color: #007726; }
|
||||
.stability.Locked { border-color: #0084B6; color: #00668c; }
|
||||
.stability.Unmarked { border-color: #BBBBBB; }
|
||||
|
||||
.summary {
|
||||
padding-right: 0px;
|
||||
}
|
||||
.summary.Deprecated { background-color: #A071A8; }
|
||||
.summary.Experimental { background-color: #D46D6A; }
|
||||
.summary.Unstable { background-color: #D4B16A; }
|
||||
.summary.Stable { background-color: #54A759; }
|
||||
.summary.Unmarked { background-color: #BBBBBB; }
|
||||
|
||||
:target { background: #FDFFD3; }
|
||||
|
||||
/* Code highlighting */
|
||||
pre.rust .kw { color: #8959A8; }
|
||||
pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; }
|
||||
pre.rust .number, pre.rust .string { color: #718C00; }
|
||||
pre.rust .self, pre.rust .boolval, pre.rust .prelude-val,
|
||||
pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; }
|
||||
pre.rust .comment { color: #8E908C; }
|
||||
pre.rust .doccomment { color: #4D4D4C; }
|
||||
pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; }
|
||||
pre.rust .lifetime { color: #B76514; }
|
||||
|
||||
.rusttest { display: none; }
|
||||
pre.rust { position: relative; }
|
||||
.test-arrow {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 10px;
|
||||
font-size: 150%;
|
||||
-webkit-transform: scaleX(-1);
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
.methods .section-header {
|
||||
/* Override parent class attributes. */
|
||||
border-bottom: none !important;
|
||||
font-size: 1.1em !important;
|
||||
margin: 0 0 -5px;
|
||||
padding: 0;
|
||||
}
|
||||
.section-header:hover a:after {
|
||||
content: '\2002\00a7\2002';
|
||||
}
|
||||
|
||||
/* Media Queries */
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
nav.sub {
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.collapse-toggle {
|
||||
font-weight: 100;
|
||||
position: absolute;
|
||||
left: 13px;
|
||||
color: #999;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.toggle-wrapper > .collapse-toggle {
|
||||
left: -24px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.toggle-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toggle-wrapper.collapsed {
|
||||
height: 1em;
|
||||
transition: height .2s;
|
||||
}
|
||||
|
||||
.collapse-toggle > .inner {
|
||||
display: inline-block;
|
||||
width: 1ch;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
@@ -0,0 +1,782 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*jslint browser: true, es5: true */
|
||||
/*globals $: true, rootPath: true */
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
var resizeTimeout, interval;
|
||||
|
||||
$('.js-only').removeClass('js-only');
|
||||
|
||||
function getQueryStringParams() {
|
||||
var params = {};
|
||||
window.location.search.substring(1).split("&").
|
||||
map(function(s) {
|
||||
var pair = s.split("=");
|
||||
params[decodeURIComponent(pair[0])] =
|
||||
typeof pair[1] === "undefined" ?
|
||||
null : decodeURIComponent(pair[1]);
|
||||
});
|
||||
return params;
|
||||
}
|
||||
|
||||
function browserSupportsHistoryApi() {
|
||||
return window.history && typeof window.history.pushState === "function";
|
||||
}
|
||||
|
||||
function resizeShortBlocks() {
|
||||
if (resizeTimeout) {
|
||||
clearTimeout(resizeTimeout);
|
||||
}
|
||||
resizeTimeout = setTimeout(function() {
|
||||
var contentWidth = $('.content').width();
|
||||
$('.docblock.short').width(function() {
|
||||
return contentWidth - 40 - $(this).prev().width();
|
||||
}).addClass('nowrap');
|
||||
$('.summary-column').width(function() {
|
||||
return contentWidth - 40 - $(this).prev().width();
|
||||
})
|
||||
}, 150);
|
||||
}
|
||||
resizeShortBlocks();
|
||||
$(window).on('resize', resizeShortBlocks);
|
||||
|
||||
function highlightSourceLines() {
|
||||
var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
|
||||
if (match) {
|
||||
from = parseInt(match[1], 10);
|
||||
to = Math.min(50000, parseInt(match[2] || match[1], 10));
|
||||
from = Math.min(from, to);
|
||||
if ($('#' + from).length === 0) {
|
||||
return;
|
||||
}
|
||||
$('#' + from)[0].scrollIntoView();
|
||||
$('.line-numbers span').removeClass('line-highlighted');
|
||||
for (i = from; i <= to; ++i) {
|
||||
$('#' + i).addClass('line-highlighted');
|
||||
}
|
||||
}
|
||||
}
|
||||
highlightSourceLines();
|
||||
$(window).on('hashchange', highlightSourceLines);
|
||||
|
||||
$(document).on('keyup', function(e) {
|
||||
if (document.activeElement.tagName === 'INPUT') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.which === 191 && $('#help').hasClass('hidden')) { // question mark
|
||||
e.preventDefault();
|
||||
$('#help').removeClass('hidden');
|
||||
} else if (e.which === 27) { // esc
|
||||
if (!$('#help').hasClass('hidden')) {
|
||||
e.preventDefault();
|
||||
$('#help').addClass('hidden');
|
||||
} else if (!$('#search').hasClass('hidden')) {
|
||||
e.preventDefault();
|
||||
$('#search').addClass('hidden');
|
||||
$('#main').removeClass('hidden');
|
||||
}
|
||||
} else if (e.which === 83) { // S
|
||||
e.preventDefault();
|
||||
$('.search-input').focus();
|
||||
}
|
||||
}).on('click', function(e) {
|
||||
if (!$(e.target).closest('#help').length) {
|
||||
$('#help').addClass('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
$('.version-selector').on('change', function() {
|
||||
var i, match,
|
||||
url = document.location.href,
|
||||
stripped = '',
|
||||
len = rootPath.match(/\.\.\//g).length + 1;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
match = url.match(/\/[^\/]*$/);
|
||||
if (i < len - 1) {
|
||||
stripped = match[0] + stripped;
|
||||
}
|
||||
url = url.substring(0, url.length - match[0].length);
|
||||
}
|
||||
|
||||
url += '/' + $('.version-selector').val() + stripped;
|
||||
|
||||
document.location.href = url;
|
||||
});
|
||||
/**
|
||||
* A function to compute the Levenshtein distance between two strings
|
||||
* Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
|
||||
* Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
|
||||
* This code is an unmodified version of the code written by Marco de Wit
|
||||
* and was found at http://stackoverflow.com/a/18514751/745719
|
||||
*/
|
||||
var levenshtein = (function() {
|
||||
var row2 = [];
|
||||
return function(s1, s2) {
|
||||
if (s1 === s2) {
|
||||
return 0;
|
||||
} else {
|
||||
var s1_len = s1.length, s2_len = s2.length;
|
||||
if (s1_len && s2_len) {
|
||||
var i1 = 0, i2 = 0, a, b, c, c2, row = row2;
|
||||
while (i1 < s1_len)
|
||||
row[i1] = ++i1;
|
||||
while (i2 < s2_len) {
|
||||
c2 = s2.charCodeAt(i2);
|
||||
a = i2;
|
||||
++i2;
|
||||
b = i2;
|
||||
for (i1 = 0; i1 < s1_len; ++i1) {
|
||||
c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
|
||||
a = row[i1];
|
||||
b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
|
||||
row[i1] = b;
|
||||
}
|
||||
}
|
||||
return b;
|
||||
} else {
|
||||
return s1_len + s2_len;
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
function initSearch(rawSearchIndex) {
|
||||
var currentResults, index, searchIndex;
|
||||
var MAX_LEV_DISTANCE = 3;
|
||||
var params = getQueryStringParams();
|
||||
|
||||
// Populate search bar with query string search term when provided,
|
||||
// but only if the input bar is empty. This avoid the obnoxious issue
|
||||
// where you start trying to do a search, and the index loads, and
|
||||
// suddenly your search is gone!
|
||||
if ($(".search-input")[0].value === "") {
|
||||
$(".search-input")[0].value = params.search || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the query and builds an index of results
|
||||
* @param {[Object]} query [The user query]
|
||||
* @param {[type]} max [The maximum results returned]
|
||||
* @param {[type]} searchWords [The list of search words to query
|
||||
* against]
|
||||
* @return {[type]} [A search index of results]
|
||||
*/
|
||||
function execQuery(query, max, searchWords) {
|
||||
var valLower = query.query.toLowerCase(),
|
||||
val = valLower,
|
||||
typeFilter = itemTypeFromName(query.type),
|
||||
results = [],
|
||||
split = valLower.split("::");
|
||||
|
||||
//remove empty keywords
|
||||
for (var j = 0; j < split.length; ++j) {
|
||||
split[j].toLowerCase();
|
||||
if (split[j] === "") {
|
||||
split.splice(j, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// quoted values mean literal search
|
||||
var nSearchWords = searchWords.length;
|
||||
if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
|
||||
val.charAt(val.length - 1) === val.charAt(0))
|
||||
{
|
||||
val = val.substr(1, val.length - 2);
|
||||
for (var i = 0; i < nSearchWords; ++i) {
|
||||
if (searchWords[i] === val) {
|
||||
// filter type: ... queries
|
||||
if (typeFilter < 0 || typeFilter === searchIndex[i].ty) {
|
||||
results.push({id: i, index: -1});
|
||||
}
|
||||
}
|
||||
if (results.length === max) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// gather matching search results up to a certain maximum
|
||||
val = val.replace(/\_/g, "");
|
||||
for (var i = 0; i < split.length; ++i) {
|
||||
for (var j = 0; j < nSearchWords; ++j) {
|
||||
var lev_distance;
|
||||
if (searchWords[j].indexOf(split[i]) > -1 ||
|
||||
searchWords[j].indexOf(val) > -1 ||
|
||||
searchWords[j].replace(/_/g, "").indexOf(val) > -1)
|
||||
{
|
||||
// filter type: ... queries
|
||||
if (typeFilter < 0 || typeFilter === searchIndex[j].ty) {
|
||||
results.push({
|
||||
id: j,
|
||||
index: searchWords[j].replace(/_/g, "").indexOf(val),
|
||||
lev: 0,
|
||||
});
|
||||
}
|
||||
} else if (
|
||||
(lev_distance = levenshtein(searchWords[j], val)) <=
|
||||
MAX_LEV_DISTANCE) {
|
||||
if (typeFilter < 0 || typeFilter === searchIndex[j].ty) {
|
||||
results.push({
|
||||
id: j,
|
||||
index: 0,
|
||||
// we want lev results to go lower than others
|
||||
lev: lev_distance,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (results.length === max) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var nresults = results.length;
|
||||
for (var i = 0; i < nresults; ++i) {
|
||||
results[i].word = searchWords[results[i].id];
|
||||
results[i].item = searchIndex[results[i].id] || {};
|
||||
}
|
||||
// if there are no results then return to default and fail
|
||||
if (results.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
results.sort(function(aaa, bbb) {
|
||||
var a, b;
|
||||
|
||||
// Sort by non levenshtein results and then levenshtein results by the distance
|
||||
// (less changes required to match means higher rankings)
|
||||
a = (aaa.lev);
|
||||
b = (bbb.lev);
|
||||
if (a !== b) return a - b;
|
||||
|
||||
// sort by crate (non-current crate goes later)
|
||||
a = (aaa.item.crate !== window.currentCrate);
|
||||
b = (bbb.item.crate !== window.currentCrate);
|
||||
if (a !== b) return a - b;
|
||||
|
||||
// sort by exact match (mismatch goes later)
|
||||
a = (aaa.word !== valLower);
|
||||
b = (bbb.word !== valLower);
|
||||
if (a !== b) return a - b;
|
||||
|
||||
// sort by item name length (longer goes later)
|
||||
a = aaa.word.length;
|
||||
b = bbb.word.length;
|
||||
if (a !== b) return a - b;
|
||||
|
||||
// sort by item name (lexicographically larger goes later)
|
||||
a = aaa.word;
|
||||
b = bbb.word;
|
||||
if (a !== b) return (a > b ? +1 : -1);
|
||||
|
||||
// sort by index of keyword in item name (no literal occurrence goes later)
|
||||
a = (aaa.index < 0);
|
||||
b = (bbb.index < 0);
|
||||
if (a !== b) return a - b;
|
||||
// (later literal occurrence, if any, goes later)
|
||||
a = aaa.index;
|
||||
b = bbb.index;
|
||||
if (a !== b) return a - b;
|
||||
|
||||
// sort by description (no description goes later)
|
||||
a = (aaa.item.desc === '');
|
||||
b = (bbb.item.desc === '');
|
||||
if (a !== b) return a - b;
|
||||
|
||||
// sort by type (later occurrence in `itemTypes` goes later)
|
||||
a = aaa.item.ty;
|
||||
b = bbb.item.ty;
|
||||
if (a !== b) return a - b;
|
||||
|
||||
// sort by path (lexicographically larger goes later)
|
||||
a = aaa.item.path;
|
||||
b = bbb.item.path;
|
||||
if (a !== b) return (a > b ? +1 : -1);
|
||||
|
||||
// que sera, sera
|
||||
return 0;
|
||||
});
|
||||
|
||||
// remove duplicates, according to the data provided
|
||||
for (var i = results.length - 1; i > 0; i -= 1) {
|
||||
if (results[i].word === results[i - 1].word &&
|
||||
results[i].item.ty === results[i - 1].item.ty &&
|
||||
results[i].item.path === results[i - 1].item.path)
|
||||
{
|
||||
results[i].id = -1;
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < results.length; ++i) {
|
||||
var result = results[i],
|
||||
name = result.item.name.toLowerCase(),
|
||||
path = result.item.path.toLowerCase(),
|
||||
parent = result.item.parent;
|
||||
|
||||
var valid = validateResult(name, path, split, parent);
|
||||
if (!valid) {
|
||||
result.id = -1;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate performs the following boolean logic. For example:
|
||||
* "File::open" will give IF A PARENT EXISTS => ("file" && "open")
|
||||
* exists in (name || path || parent) OR => ("file" && "open") exists in
|
||||
* (name || path )
|
||||
*
|
||||
* This could be written functionally, but I wanted to minimise
|
||||
* functions on stack.
|
||||
*
|
||||
* @param {[string]} name [The name of the result]
|
||||
* @param {[string]} path [The path of the result]
|
||||
* @param {[string]} keys [The keys to be used (["file", "open"])]
|
||||
* @param {[object]} parent [The parent of the result]
|
||||
* @return {[boolean]} [Whether the result is valid or not]
|
||||
*/
|
||||
function validateResult(name, path, keys, parent) {
|
||||
for (var i=0; i < keys.length; ++i) {
|
||||
// each check is for validation so we negate the conditions and invalidate
|
||||
if (!(
|
||||
// check for an exact name match
|
||||
name.toLowerCase().indexOf(keys[i]) > -1 ||
|
||||
// then an exact path match
|
||||
path.toLowerCase().indexOf(keys[i]) > -1 ||
|
||||
// next if there is a parent, check for exact parent match
|
||||
(parent !== undefined &&
|
||||
parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
|
||||
// lastly check to see if the name was a levenshtein match
|
||||
levenshtein(name.toLowerCase(), keys[i]) <=
|
||||
MAX_LEV_DISTANCE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getQuery() {
|
||||
var matches, type, query = $('.search-input').val();
|
||||
|
||||
matches = query.match(/^(fn|mod|str(uct)?|enum|trait|t(ype)?d(ef)?)\s*:\s*/i);
|
||||
if (matches) {
|
||||
type = matches[1].replace(/^td$/, 'typedef')
|
||||
.replace(/^str$/, 'struct')
|
||||
.replace(/^tdef$/, 'typedef')
|
||||
.replace(/^typed$/, 'typedef');
|
||||
query = query.substring(matches[0].length);
|
||||
}
|
||||
|
||||
return {
|
||||
query: query,
|
||||
type: type,
|
||||
id: query + type,
|
||||
};
|
||||
}
|
||||
|
||||
function initSearchNav() {
|
||||
var hoverTimeout, $results = $('.search-results .result');
|
||||
|
||||
$results.on('click', function() {
|
||||
var dst = $(this).find('a')[0];
|
||||
if (window.location.pathname == dst.pathname) {
|
||||
$('#search').addClass('hidden');
|
||||
$('#main').removeClass('hidden');
|
||||
}
|
||||
document.location.href = dst.href;
|
||||
}).on('mouseover', function() {
|
||||
var $el = $(this);
|
||||
clearTimeout(hoverTimeout);
|
||||
hoverTimeout = setTimeout(function() {
|
||||
$results.removeClass('highlighted');
|
||||
$el.addClass('highlighted');
|
||||
}, 20);
|
||||
});
|
||||
|
||||
$(document).off('keydown.searchnav');
|
||||
$(document).on('keydown.searchnav', function(e) {
|
||||
var $active = $results.filter('.highlighted');
|
||||
|
||||
if (e.which === 38) { // up
|
||||
e.preventDefault();
|
||||
if (!$active.length || !$active.prev()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$active.prev().addClass('highlighted');
|
||||
$active.removeClass('highlighted');
|
||||
} else if (e.which === 40) { // down
|
||||
e.preventDefault();
|
||||
if (!$active.length) {
|
||||
$results.first().addClass('highlighted');
|
||||
} else if ($active.next().length) {
|
||||
$active.next().addClass('highlighted');
|
||||
$active.removeClass('highlighted');
|
||||
}
|
||||
} else if (e.which === 13) { // return
|
||||
e.preventDefault();
|
||||
if ($active.length) {
|
||||
document.location.href = $active.find('a').prop('href');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function escape(content) {
|
||||
return $('<h1/>').text(content).html();
|
||||
}
|
||||
|
||||
function showResults(results) {
|
||||
var output, shown, query = getQuery();
|
||||
|
||||
currentResults = query.id;
|
||||
output = '<h1>Results for ' + escape(query.query) +
|
||||
(query.type ? ' (type: ' + escape(query.type) + ')' : '') + '</h1>';
|
||||
output += '<table class="search-results">';
|
||||
|
||||
if (results.length > 0) {
|
||||
shown = [];
|
||||
|
||||
results.forEach(function(item) {
|
||||
var name, type;
|
||||
|
||||
if (shown.indexOf(item) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
shown.push(item);
|
||||
name = item.name;
|
||||
type = itemTypes[item.ty];
|
||||
|
||||
output += '<tr class="' + type + ' result"><td>';
|
||||
|
||||
if (type === 'mod') {
|
||||
output += item.path +
|
||||
'::<a href="' + rootPath +
|
||||
item.path.replace(/::/g, '/') + // '/' +
|
||||
name + '/index.html" class="' +
|
||||
type + '">' + name + '</a>';
|
||||
} else if (type === 'static' || type === 'reexport') {
|
||||
output += item.path +
|
||||
'::<a href="' + rootPath +
|
||||
item.path.replace(/::/g, '/') +
|
||||
'/index.html" class="' + type +
|
||||
'">' + name + '</a>';
|
||||
} else if (item.parent !== undefined) {
|
||||
var myparent = item.parent;
|
||||
var anchor = '#' + type + '.' + name;
|
||||
output += item.path + '::' + myparent.name +
|
||||
'::<a href="' + rootPath +
|
||||
item.path.replace(/::/g, '/') +
|
||||
'/' + myparent.name +
|
||||
'.html' + anchor +
|
||||
'" class="' + type +
|
||||
'">' + name + '</a>';
|
||||
} else {
|
||||
output += item.path +
|
||||
'::<a href="' + rootPath +
|
||||
item.path.replace(/::/g, '/') +
|
||||
'/' + name +
|
||||
'.html" class="' + type +
|
||||
'">' + name + '</a>';
|
||||
}
|
||||
|
||||
output += '</td><td><span class="desc">' + item.desc +
|
||||
'</span></td></tr>';
|
||||
});
|
||||
} else {
|
||||
output += 'No results - Request function at <a href="https://github.com/azerothcore/mod-ale/issues">ALE issue tracker</a>';
|
||||
}
|
||||
|
||||
output += "</p>";
|
||||
$('#main.content').addClass('hidden');
|
||||
$('#search.content').removeClass('hidden').html(output);
|
||||
$('#search .desc').width($('#search').width() - 40 -
|
||||
$('#search td:first-child').first().width());
|
||||
initSearchNav();
|
||||
}
|
||||
|
||||
function search(e) {
|
||||
var query,
|
||||
filterdata = [],
|
||||
obj, i, len,
|
||||
results = [],
|
||||
maxResults = 200,
|
||||
resultIndex;
|
||||
var params = getQueryStringParams();
|
||||
|
||||
query = getQuery();
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
if (!query.query || query.id === currentResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Because searching is incremental by character, only the most
|
||||
// recent search query is added to the browser history.
|
||||
// Do not do this on local due to chrome security errors.
|
||||
// http://stackoverflow.com/a/32454237/3586583
|
||||
if (browserSupportsHistoryApi() && window.location.protocol != "file:") {
|
||||
if (!history.state && !params.search) {
|
||||
history.pushState(query, "", "?search=" +
|
||||
encodeURIComponent(query.query));
|
||||
} else {
|
||||
history.replaceState(query, "", "?search=" +
|
||||
encodeURIComponent(query.query));
|
||||
}
|
||||
}
|
||||
|
||||
resultIndex = execQuery(query, 20000, index);
|
||||
len = resultIndex.length;
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (resultIndex[i].id > -1) {
|
||||
obj = searchIndex[resultIndex[i].id];
|
||||
filterdata.push([obj.name, obj.ty, obj.path, obj.desc]);
|
||||
results.push(obj);
|
||||
}
|
||||
if (results.length >= maxResults) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
showResults(results);
|
||||
}
|
||||
|
||||
// This mapping table should match the discriminants of
|
||||
// `rustdoc::html::item_type::ItemType` type in Rust.
|
||||
var itemTypes = ["mod",
|
||||
"struct",
|
||||
"type",
|
||||
"fn",
|
||||
"type",
|
||||
"static",
|
||||
"trait",
|
||||
"impl",
|
||||
"viewitem",
|
||||
"tymethod",
|
||||
"method",
|
||||
"structfield",
|
||||
"variant",
|
||||
"ffi",
|
||||
"ffs",
|
||||
"macro",
|
||||
"primitive"];
|
||||
|
||||
function itemTypeFromName(typename) {
|
||||
for (var i = 0; i < itemTypes.length; ++i) {
|
||||
if (itemTypes[i] === typename) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function buildIndex(rawSearchIndex) {
|
||||
searchIndex = [];
|
||||
var searchWords = [];
|
||||
for (var crate in rawSearchIndex) {
|
||||
if (!rawSearchIndex.hasOwnProperty(crate)) { continue }
|
||||
|
||||
// an array of [(Number) item type,
|
||||
// (String) name,
|
||||
// (String) full path or empty string for previous path,
|
||||
// (String) description,
|
||||
// (optional Number) the parent path index to `paths`]
|
||||
var items = rawSearchIndex[crate].items;
|
||||
// an array of [(Number) item type,
|
||||
// (String) name]
|
||||
var paths = rawSearchIndex[crate].paths;
|
||||
|
||||
// convert `paths` into an object form
|
||||
var len = paths.length;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
paths[i] = {ty: paths[i][0], name: paths[i][1]};
|
||||
}
|
||||
|
||||
// convert `items` into an object form, and construct word indices.
|
||||
//
|
||||
// before any analysis is performed lets gather the search terms to
|
||||
// search against apart from the rest of the data. This is a quick
|
||||
// operation that is cached for the life of the page state so that
|
||||
// all other search operations have access to this cached data for
|
||||
// faster analysis operations
|
||||
var len = items.length;
|
||||
var lastPath = "";
|
||||
for (var i = 0; i < len; ++i) {
|
||||
var rawRow = items[i];
|
||||
var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
|
||||
path: rawRow[2] || lastPath, desc: rawRow[3],
|
||||
parent: paths[rawRow[4]]};
|
||||
searchIndex.push(row);
|
||||
if (typeof row.name === "string") {
|
||||
var word = row.name.toLowerCase();
|
||||
searchWords.push(word);
|
||||
} else {
|
||||
searchWords.push("");
|
||||
}
|
||||
lastPath = row.path;
|
||||
}
|
||||
}
|
||||
return searchWords;
|
||||
}
|
||||
|
||||
function startSearch() {
|
||||
var keyUpTimeout;
|
||||
$('.do-search').on('click', search);
|
||||
$('.search-input').on('keyup', function() {
|
||||
clearTimeout(keyUpTimeout);
|
||||
keyUpTimeout = setTimeout(search, 100);
|
||||
});
|
||||
|
||||
// Push and pop states are used to add search results to the browser
|
||||
// history.
|
||||
if (browserSupportsHistoryApi()) {
|
||||
$(window).on('popstate', function(e) {
|
||||
var params = getQueryStringParams();
|
||||
// When browsing back from search results the main page
|
||||
// visibility must be reset.
|
||||
if (!params.search) {
|
||||
$('#main.content').removeClass('hidden');
|
||||
$('#search.content').addClass('hidden');
|
||||
}
|
||||
// When browsing forward to search results the previous
|
||||
// search will be repeated, so the currentResults are
|
||||
// cleared to ensure the search is successful.
|
||||
currentResults = null;
|
||||
// Synchronize search bar with query string state and
|
||||
// perform the search. This will empty the bar if there's
|
||||
// nothing there, which lets you really go back to a
|
||||
// previous state with nothing in the bar.
|
||||
$('.search-input').val(params.search);
|
||||
// Some browsers fire 'onpopstate' for every page load
|
||||
// (Chrome), while others fire the event only when actually
|
||||
// popping a state (Firefox), which is why search() is
|
||||
// called both here and at the end of the startSearch()
|
||||
// function.
|
||||
search();
|
||||
});
|
||||
}
|
||||
search();
|
||||
}
|
||||
|
||||
index = buildIndex(rawSearchIndex);
|
||||
startSearch();
|
||||
|
||||
// Draw a convenient sidebar of known crates if we have a listing
|
||||
if (rootPath == '../') {
|
||||
var sidebar = $('.sidebar');
|
||||
var div = $('<div>').attr('class', 'block crate');
|
||||
div.append($('<h2>').text('All Classes'));
|
||||
|
||||
var crates = [];
|
||||
for (var crate in rawSearchIndex) {
|
||||
if (!rawSearchIndex.hasOwnProperty(crate)) { continue }
|
||||
crates.push(crate);
|
||||
}
|
||||
crates.sort();
|
||||
for (var i = 0; i < crates.length; ++i) {
|
||||
var klass = 'crate';
|
||||
if (crates[i] == window.currentCrate) {
|
||||
klass += ' current';
|
||||
}
|
||||
div.append($('<a>', {'href': '../' + crates[i] + '/index.html',
|
||||
'class': klass}).text(crates[i]));
|
||||
}
|
||||
sidebar.append(div);
|
||||
}
|
||||
}
|
||||
|
||||
window.initSearch = initSearch;
|
||||
|
||||
window.register_implementors = function(imp) {
|
||||
var list = $('#implementors-list');
|
||||
var libs = Object.getOwnPropertyNames(imp);
|
||||
for (var i = 0; i < libs.length; ++i) {
|
||||
if (libs[i] == currentCrate) continue;
|
||||
var structs = imp[libs[i]];
|
||||
for (var j = 0; j < structs.length; ++j) {
|
||||
var code = $('<code>').append(structs[j]);
|
||||
$.each(code.find('a'), function(idx, a) {
|
||||
var href = $(a).attr('href');
|
||||
if (!href.startsWith('http')) {
|
||||
$(a).attr('href', rootPath + $(a).attr('href'));
|
||||
}
|
||||
});
|
||||
var li = $('<li>').append(code);
|
||||
list.append(li);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (window.pending_implementors) {
|
||||
window.register_implementors(window.pending_implementors);
|
||||
}
|
||||
|
||||
// See documentation in html/render.rs for what this is doing.
|
||||
var query = getQueryStringParams();
|
||||
if (query['gotosrc']) {
|
||||
window.location = $('#src-' + query['gotosrc']).attr('href');
|
||||
}
|
||||
|
||||
$("#expand-all").on("click", function() {
|
||||
$(".docblock").show();
|
||||
$(".toggle-label").hide();
|
||||
$(".toggle-wrapper").removeClass("collapsed");
|
||||
$(".collapse-toggle").children(".inner").html("-");
|
||||
});
|
||||
|
||||
$("#collapse-all").on("click", function() {
|
||||
$(".docblock").hide();
|
||||
$(".toggle-label").show();
|
||||
$(".toggle-wrapper").addClass("collapsed");
|
||||
$(".collapse-toggle").children(".inner").html("+");
|
||||
});
|
||||
|
||||
$(document).on("click", ".collapse-toggle", function() {
|
||||
var toggle = $(this);
|
||||
var relatedDoc = toggle.parent().next();
|
||||
if (relatedDoc.is(".docblock")) {
|
||||
if (relatedDoc.is(":visible")) {
|
||||
relatedDoc.slideUp({duration:'fast', easing:'linear'});
|
||||
toggle.parent(".toggle-wrapper").addClass("collapsed");
|
||||
toggle.children(".inner").html("+");
|
||||
toggle.children(".toggle-label").fadeIn();
|
||||
} else {
|
||||
relatedDoc.slideDown({duration:'fast', easing:'linear'});
|
||||
toggle.parent(".toggle-wrapper").removeClass("collapsed");
|
||||
toggle.children(".inner").html("-");
|
||||
toggle.children(".toggle-label").hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$(function() {
|
||||
var toggle = "<a href='javascript:void(0)'"
|
||||
+ "class='collapse-toggle'>[<span class='inner'>-</span>]</a>";
|
||||
|
||||
$(".method").each(function() {
|
||||
if ($(this).next().is(".docblock")) {
|
||||
$(this).children().first().after(toggle);
|
||||
}
|
||||
});
|
||||
|
||||
var mainToggle = $(toggle);
|
||||
mainToggle.append("<span class='toggle-label' style='display:none'>"
|
||||
+ " Expand description</span></a>")
|
||||
var wrapper = $("<div class='toggle-wrapper'>");
|
||||
wrapper.append(mainToggle);
|
||||
$("#main > .docblock").before(wrapper);
|
||||
});
|
||||
|
||||
}());
|
||||
@@ -0,0 +1 @@
|
||||
/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}
|
||||
@@ -0,0 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="{% block description %}API documentation for the ALE engine.{% endblock %}">
|
||||
<meta name="keywords" content="ALE, lua, lua engine, azerothcore, script, scripting, doc, docs, documentation">
|
||||
|
||||
<title>{% block title %}ALE API{% endblock %}</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{{ static('main.css') }}">
|
||||
<link rel="stylesheet" type="text/css" href="{{ static('dark.css') }}">
|
||||
|
||||
<link rel="shortcut icon" href="{{ static('favicon.ico') }}">
|
||||
|
||||
</head>
|
||||
<body class="rustdoc">
|
||||
<!--[if lte IE 8]>
|
||||
<div class="warning">
|
||||
This old browser is unsupported and will most likely display funky
|
||||
things.
|
||||
</div>
|
||||
<![endif]-->
|
||||
|
||||
<section class="sidebar">
|
||||
<a href='{{ root('index.html') }}'><img src='{{ static('eluna-logo.png') }}' alt='ALE Logo' width='100'></a>
|
||||
<div class="block">
|
||||
{% block sidebar %}
|
||||
<h2>All Classes</h2>
|
||||
{% for class in classes -%}
|
||||
<a class="mod {{ 'current' if class == current_class }}" href="{{ root(class.name + '/index.html') }}">{{ class.name }}</a>
|
||||
{%- endfor %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<nav class="sub">
|
||||
<form class="search-form js-only">
|
||||
<div class="search-container">
|
||||
<input class="search-input" name="search"
|
||||
autocomplete="off"
|
||||
placeholder="Click or press 'S' to search, '?' for more options..."
|
||||
type="search">
|
||||
</div>
|
||||
</form>
|
||||
</nav>
|
||||
|
||||
<section id='main' class="content mod">
|
||||
<h1 class='fqn'>
|
||||
{% block document_title %}Title Missing{% endblock %}
|
||||
<span class='out-of-band'>
|
||||
<span id='render-detail'>
|
||||
<a id="collapse-all" href="#">[-]</a>
|
||||
<a id="expand-all" href="#">[+]</a>
|
||||
</span>
|
||||
</span>
|
||||
</h1>
|
||||
|
||||
{% block content %}<h2>Content missing.</h2>{% endblock %}
|
||||
</section>
|
||||
|
||||
<section id='search' class="content hidden"></section>
|
||||
|
||||
<section class="footer"></section>
|
||||
|
||||
<div id="help" class="hidden">
|
||||
<div class="shortcuts">
|
||||
<h1>Keyboard shortcuts</h1>
|
||||
<dl>
|
||||
<dt>?</dt>
|
||||
<dd>Show this help dialog</dd>
|
||||
<dt>S</dt>
|
||||
<dd>Focus the search field</dd>
|
||||
<dt>⇤</dt>
|
||||
<dd>Move up in search results</dd>
|
||||
<dt>⇥</dt>
|
||||
<dd>Move down in search results</dd>
|
||||
<dt>⏎</dt>
|
||||
<dd>Go to active search result</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="infos">
|
||||
<h1>Search tricks</h1>
|
||||
<p>
|
||||
Prefix searches with a type followed by a colon (e.g.
|
||||
<code>fn:</code>) to restrict the search to a given type.
|
||||
</p>
|
||||
<p>
|
||||
Accepted types are: <code>fn</code>, <code>mod</code>,
|
||||
<code>struct</code> (or <code>str</code>), <code>enum</code>,
|
||||
<code>trait</code>, <code>typedef</code> (or
|
||||
<code>tdef</code>).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.rootPath = "{{ ('../' * level) if level }}";
|
||||
</script>
|
||||
<script src="{{ static('jquery.js') }}"></script>
|
||||
<script src="{{ static('main.js') }}"></script>
|
||||
<script async src="{{ root('search-index.js') }}"></script>
|
||||
<center>Generated on <script src="{{ root('date.js') }}"></script></center>
|
||||
<center>©2026 - ALE Lua Engine</center>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
{% extends '_base.html' %}
|
||||
|
||||
|
||||
{% block title -%}
|
||||
{{ current_class.name }} - {{ super() }}
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block description -%}
|
||||
API documentation for the {{ current_class.name }} class in the ALE engine.
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block document_title -%}
|
||||
Class <a class="mod" href="">{{ current_class.name }}</a>
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block sidebar %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{{ current_class.description|parse_links }}
|
||||
|
||||
<h2 id='methods' class='section-header'><a href="#methods">Methods</a></h2>
|
||||
<table>
|
||||
{%- for method in current_class.methods %}
|
||||
<tr>
|
||||
<td>
|
||||
<a class='stability {{ 'Stable' if method.documented else 'Experimental' }}' title='{{ 'Documented' if method.documented else 'Undocumented' }}'></a>
|
||||
<a class='fn' href='{{ method.name }}.html'>{{ method.name }}</a>
|
||||
</td>
|
||||
<td class='docblock short'>
|
||||
<p>{{ method.short_description|parse_links }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1 @@
|
||||
document.write("{{ currdate }}");
|
||||
@@ -0,0 +1 @@
|
||||
{% extends '_base.html' %}
|
||||
@@ -0,0 +1,87 @@
|
||||
{% extends '_base.html' %}
|
||||
|
||||
|
||||
{% block document_title -%}
|
||||
ALE API Documentation
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class='docblock'>
|
||||
<h1 id="the-ale-engine-api" class='section-header'>
|
||||
<a href="#the-ale-engine-api">The ALE Lua Engine© API</a>
|
||||
</h1>
|
||||
<p>
|
||||
The ALE Lua Engine© API allows you to add your own Lua code to be executed when certain events (called "hooks") occur.
|
||||
</p>
|
||||
<p>
|
||||
Add a new in-game command, give life to creatures with new AI, or even light players who try to duel on fire!
|
||||
If the hook exists, you can script it.
|
||||
</p>
|
||||
|
||||
<h2 id="about-ale" class='section-header'>
|
||||
<a href="#about-ale">About ALE</a>
|
||||
</h2>
|
||||
<p>
|
||||
ALE is a <a href="http://www.lua.org/">Lua</a> engine for World of Warcraft emulators.
|
||||
These pages are for <a href="https://www.azerothcore.org/">AzerothCore</a>'s version, but ALE also supports
|
||||
<a href="https://cmangos.net/">CMaNGOS</a>/<a href="https://www.getmangos.eu/">MaNGOS</a> and <a href="https://www.trinitycore.org/">TrinityCore</a>.
|
||||
</p>
|
||||
<p>
|
||||
If you come from the TypeScript / JavaScript world, or would prefer to use a typed language instead of Lua, check out <a href="https://github.com/azerothcore/eluna-ts">ale-ts</a>!
|
||||
</p>
|
||||
<p>
|
||||
You can get support in the <code>#ale-ac</code> channel of <a href="https://discord.com/invite/sqkPb623">AzerothCore's Discord server</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="installation" class='section-header'>
|
||||
<a href="#installation">How to Install</a>
|
||||
</h2>
|
||||
<p>
|
||||
<ol>
|
||||
<li>If you haven't already, clone AzerothCore from <a href="https://github.com/azerothcore/azerothcore-wotlk">our GitHub repository</a></li>
|
||||
<li>Go to the modules directory and run the following command: <pre>git clone https://github.com/azerothcore/mod-ale.git mod-ale</pre></li>
|
||||
<li>Run CMake</li>
|
||||
<li>Build AzerothCore</li>
|
||||
</ol>
|
||||
</p>
|
||||
|
||||
<!-- <h2 id="tutorials-and-guides" class='section-header'>
|
||||
<a href="#tutorials-and-guides">Tutorials & Guides</a>
|
||||
</h2>
|
||||
<p>
|
||||
We haven't written tutorials yet, but when we do, we'll put the links here.
|
||||
</p> -->
|
||||
|
||||
<h2 id="about-this-documentation" class='section-header'>
|
||||
<a href="#about-this-documentation">About this Documentation</a>
|
||||
</h2>
|
||||
<p>
|
||||
The layout, CSS, and Javascript code for this documentation was borrowed from <a href="http://doc.rust-lang.org/">doc.rust-lang.org</a>.
|
||||
</p>
|
||||
<p>
|
||||
The documentation generator was originally written by <a href="https://github.com/Patman64">Patman64</a> and is maintained by the ALE team.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 id='modules' class='section-header'><a href="#modules">Classes</a></h2>
|
||||
<table>
|
||||
{%- for class in classes %}
|
||||
<tr>
|
||||
<td>
|
||||
{%- if class.fully_documented %}
|
||||
<a class='stability Stable' title='Fully Documented'></a>
|
||||
{%- elif class.fully_undocumented %}
|
||||
<a class='stability Experimental' title='Fully Undocumented'></a>
|
||||
{%- else %}
|
||||
<a class='stability Unstable' title='Partially Documented'></a>
|
||||
{%- endif %}
|
||||
<a class='mod' href='{{ root(class.name + '/index.html') }}'>{{ class.name }}</a>
|
||||
</td>
|
||||
<td class='docblock short'>
|
||||
<p>{{ class.short_description|parse_links }}</p>
|
||||
</td>
|
||||
</tr>
|
||||
{%- endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,95 @@
|
||||
{% extends '_base.html' %}
|
||||
|
||||
|
||||
{% block title -%}
|
||||
{{ current_class.name }}:{{ current_method.name }} - ALE
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block description -%}
|
||||
API documentation for the {{ current_class.name }}:{{ current_method.name }} method in the ALE engine.
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block document_title -%}
|
||||
Method
|
||||
<a class="mod" href="{{ root(current_class.name + '/index.html') }}">
|
||||
{{- current_class.name -}}
|
||||
</a>:<a class="fn" href="{{ root(current_class.name + '/' + current_method.name + '.html') }}">
|
||||
{{- current_method.name -}}
|
||||
</a>
|
||||
{%- endblock %}
|
||||
|
||||
|
||||
{% block sidebar %}
|
||||
<h2>{{ current_class.name }} Methods</h2>
|
||||
<script src="sidebar.js"></script>
|
||||
<script>
|
||||
// Highlight current method by adding "current" class to it
|
||||
var element = document.getElementById("{{ current_class.name + ':' + current_method.name }}");
|
||||
if (element) {
|
||||
var classname = "current";
|
||||
arr = element.className.split(" ");
|
||||
if (arr.indexOf(classname) == -1) {
|
||||
element.className += " " + classname;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class='docblock'>
|
||||
{%- if current_method.documented %}
|
||||
{{ current_method.description|parse_links }}
|
||||
{%- else %}
|
||||
<p>This method is <em>undocumented</em>. <strong>Use at your own risk.</strong></p>
|
||||
<p>For temporary documentation, please check the <a href="https://github.com/azerothcore/mod-ale/blob/master/src/LuaEngine/LuaFunctions.cpp">LuaFunctions</a> source file.</p>
|
||||
{%- endif %}
|
||||
|
||||
<h2 id="synopsis" class='section-header'>
|
||||
<a href="#synopsis">Synopsis</a>
|
||||
</h2>
|
||||
{%- for prototype in current_method.prototypes %}
|
||||
<p>
|
||||
<code>{{ prototype }}</code>
|
||||
</p>
|
||||
{%- endfor %}
|
||||
|
||||
<h2 id="arguments" class='section-header'>
|
||||
<a href="#arguments">Arguments</a>
|
||||
</h2>
|
||||
<p>
|
||||
{%- if current_method.parameters|length > 0 %}
|
||||
{%- for param in current_method.parameters %}
|
||||
<dl>
|
||||
<dt><code>{{ param.data_type|escape|parse_data_type }} {{ param.name if param.data_type != '...' }} {{- ' (' + param.default_value + ')' if param.default_value }}</code></dt>
|
||||
<dd class="docblock">{{ param.description|parse_links if param.description else '<em>See method description.</em>' }}</dd>
|
||||
</dl>
|
||||
{%- endfor %}
|
||||
{%- elif not current_method.documented %}
|
||||
Unknown.
|
||||
{%- else %}
|
||||
None.
|
||||
{%- endif %}
|
||||
</p>
|
||||
|
||||
<h2 id="returns" class='section-header'>
|
||||
<a href="#returns">Returns</a>
|
||||
</h2>
|
||||
<p>
|
||||
{%- if current_method.returned|length > 0 %}
|
||||
{%- for returned in current_method.returned %}
|
||||
<dl>
|
||||
<dt><code>{{ returned.data_type|escape|parse_data_type }} {{ returned.name }}</code></dt>
|
||||
<dd class="docblock">{{ returned.description|parse_links if returned.description else '<em>See method description.</em>' }}</dd>
|
||||
</dl>
|
||||
{%- endfor %}
|
||||
{%- elif not current_method.documented %}
|
||||
Unknown.
|
||||
{%- else %}
|
||||
Nothing.
|
||||
{%- endif %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
var searchIndex = {};
|
||||
|
||||
{% for class in classes -%}
|
||||
searchIndex["{{ class.name }}"] = {
|
||||
"items": [
|
||||
[0, "", "{{ class.name }}", "{{ class.short_description|replace('\n', ' ')|replace('\"', '"')|parse_links|replace('"', '\\"') }}"],
|
||||
{%- for method in class.methods %}
|
||||
[3, "{{ method.name }}", "", "{{ method.short_description|replace('\n', ' ')|replace('\"', '"')|parse_links|replace('"', '\\"') }}"],
|
||||
{%- endfor %}
|
||||
],
|
||||
"paths": []
|
||||
};
|
||||
{%- endfor %}
|
||||
|
||||
initSearch(searchIndex);
|
||||
@@ -0,0 +1,5 @@
|
||||
document.write(`
|
||||
{%- for method in current_class.methods %}
|
||||
<a id="{{ current_class.name + ':' + method.name }}" class="fn" href="{{ root(current_class.name + '/' + method.name + '.html') }}">{{ method.name }}</a>
|
||||
{%- endfor %}
|
||||
`);
|
||||
@@ -0,0 +1,165 @@
|
||||
# Documentation generation
|
||||
Eluna uses a custom made documentation generator to create it's [web documentation](http://elunaluaengine.github.io/).
|
||||
The generator is written in python by Patman. It works by parsing Eluna's source files for comments and then generates the HTML and javascript for the documentation based on them.
|
||||
|
||||
This page guides you through generating the web documentation locally and explains the standards of the documentation comments for you to help us improve our documentation. To contribute with your documentation changes, create a [pull request](https://help.github.com/articles/using-pull-requests/)
|
||||
|
||||
# Generating locally
|
||||
- install [python](https://www.python.org/)(2)
|
||||
- when installing, tick to install the path variable
|
||||
- you may need restart afterwards for the installation to properly take effect
|
||||
- install a package manager like [pip](https://pip.pypa.io/en/latest/)
|
||||
- if you installed pip and it does not work, restart or try easy_install command
|
||||
- install the dependencies with manager
|
||||
- [Jinja2](https://pypi.python.org/pypi/Jinja2)
|
||||
- [typedecorator](https://pypi.python.org/pypi/typedecorator)
|
||||
- [markdown](https://pypi.python.org/pypi/Markdown)
|
||||
- Run in cmd `python -m ALEDoc` when at `\LuaEngine\docs\`
|
||||
|
||||
# Documenting
|
||||
You can document functions in the Eluna source code. To find examples simply open a method header file like `PlayerMethods.h` and see the comments.
|
||||
|
||||
## Templates
|
||||
Here are some basic templates for a function documentation. When defining a parameter or a return value the type and value name are mandatory, unless the parameter type is `...`, which is used for variable arguments; do not include a name in this case.
|
||||
|
||||
```c++
|
||||
/**
|
||||
* Short description (about 80 characters long).
|
||||
*
|
||||
* @param Type paramName
|
||||
* @return Type returnName
|
||||
*/
|
||||
```
|
||||
|
||||
```c++
|
||||
/**
|
||||
* Short description (about 80 characters long).
|
||||
*
|
||||
* @param Type paramName = defaultValue : parameter description
|
||||
* @return Type returnName : return value description
|
||||
*/
|
||||
```
|
||||
|
||||
This is a template for a function that takes in different parameters. When defining a parameter or a return value, the type and value name are mandatory.
|
||||
|
||||
```c++
|
||||
/**
|
||||
* Short description (about 80 characters long).
|
||||
*
|
||||
* @proto returnValue = (object)
|
||||
* @proto returnValue = (x, y, z)
|
||||
* @param [WorldObject] object = defaultValue : parameter description
|
||||
* @param float x = defaultValue : parameter description
|
||||
* @param float y = defaultValue : parameter description
|
||||
* @param float z = defaultValue : parameter description
|
||||
* @return Type returnName : return value description
|
||||
*/
|
||||
```
|
||||
|
||||
## Standard
|
||||
A documentation comment block will always start with `/**` and end with `*/`.
|
||||
All lines start with `*` character followed by one space before any content.
|
||||
|
||||
The first paragrph is used as a short description of the function/class, so it should be kept to about 80 characters. The other paragraphs can be as long as desired.
|
||||
|
||||
All paragraphs in the description (including the first) should start with a capital letter and end with a period.
|
||||
**Paragraphs must be separated by an empty line**, e.g.:
|
||||
|
||||
```c++
|
||||
/**
|
||||
* This is a short description (about 80 characters).
|
||||
*
|
||||
* Here's another paragraph with more info. NOTE THE EMPTY LINE BETWEEN THE PARAGRAPHS.
|
||||
* This does need to be short, and this line is still part of the same paragraph because
|
||||
* there is no empty line.
|
||||
*/
|
||||
```
|
||||
|
||||
The parameter and return value descriptions should start with a lowercase letter and not end with a period. If more than one sentence is needed, start the *first* without a capital letter and end the *last* without a period.
|
||||
|
||||
Any class, enum or function can be referenced (made a link to) with square brackets.
|
||||
`[Player]` will reference a player. `[WeatherType]` will reference an enum. `[Player:GetName]` will reference a function.
|
||||
|
||||
Use correct indentation with documentation comments.
|
||||
|
||||
```c++
|
||||
/**
|
||||
* Correct indentation.
|
||||
*/
|
||||
```
|
||||
|
||||
```c++
|
||||
/**
|
||||
* Invalid indentation.
|
||||
*/
|
||||
```
|
||||
|
||||
## Markdown
|
||||
You can use [markdown](http://pythonhosted.org//Markdown/) in your descriptions.
|
||||
For syntax see http://daringfireball.net/projects/markdown/syntax and http://pythonhosted.org//Markdown/#differences
|
||||
|
||||
```
|
||||
/**
|
||||
* Description.
|
||||
*
|
||||
* - list item
|
||||
* - list item
|
||||
* - list item
|
||||
*
|
||||
*
|
||||
* // Codeblock
|
||||
* // Code goes here.
|
||||
* // Note the 4-space indent.
|
||||
*
|
||||
*
|
||||
* `code line`
|
||||
*
|
||||
* *italic*
|
||||
* **bold**
|
||||
*/
|
||||
```
|
||||
|
||||
**The above markdown code produces the output below:**
|
||||
|
||||
Description.
|
||||
|
||||
- list item
|
||||
- list item
|
||||
- list item
|
||||
|
||||
```
|
||||
// Codeblock
|
||||
// Code goes here.
|
||||
// Note the 4-space indent.
|
||||
```
|
||||
|
||||
`code line`
|
||||
|
||||
*italic*
|
||||
**bold**
|
||||
|
||||
## Types
|
||||
Here are some examples of possible types and most commonly used ones:
|
||||
|
||||
```
|
||||
string
|
||||
uint64
|
||||
uint32
|
||||
uint16
|
||||
uint8
|
||||
int64
|
||||
int32
|
||||
int16
|
||||
int8
|
||||
double
|
||||
float
|
||||
...
|
||||
[EnumName]
|
||||
[Player]
|
||||
[Creature]
|
||||
[GameObject]
|
||||
[Item]
|
||||
[Unit]
|
||||
[WorldObject]
|
||||
[Object]
|
||||
```
|
||||
@@ -0,0 +1,115 @@
|
||||
--
|
||||
-- Copyright (C) 2010 - 2016 Eluna Lua Engine <http://emudevs.com/>
|
||||
-- This program is free software licensed under GPL version 3
|
||||
-- Please see the included DOCS/LICENSE.md for more information
|
||||
--
|
||||
|
||||
-- filename.ext files are loaded before normal .lua files
|
||||
|
||||
--
|
||||
-- This extension allows saving data to specific object for it's lifetime in current runtime session
|
||||
-- Supports Map, Player, Creature, GameObject
|
||||
--
|
||||
-- SetData sets a value
|
||||
-- obj:SetData(key, val)
|
||||
--
|
||||
-- GetData gets the data table or a specific value by key from it
|
||||
-- local tbl = obj:GetData()
|
||||
-- local val = obj:GetData(key)
|
||||
--
|
||||
|
||||
local pairs = pairs
|
||||
|
||||
local variableStores = {
|
||||
Map = {},
|
||||
Player = {},
|
||||
Creature = {},
|
||||
GameObject = {},
|
||||
}
|
||||
|
||||
local function DestroyMapData(event, obj)
|
||||
local map = obj:GetMapId()
|
||||
local inst = obj:GetInstanceId()
|
||||
for k,v in pairs(variableStores) do
|
||||
local mapdata = v[map]
|
||||
if mapdata then
|
||||
mapdata[inst] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function DestroyObjData(event, obj)
|
||||
local otype = obj:GetObjectType()
|
||||
local guid = otype == "Map" and 1 or obj:GetGUIDLow()
|
||||
|
||||
if otype == "Player" then
|
||||
variableStores[otype][guid] = nil
|
||||
return
|
||||
end
|
||||
|
||||
local map = obj:GetMapId()
|
||||
local inst = obj:GetInstanceId()
|
||||
local mapdata = variableStores[otype][map]
|
||||
if mapdata then
|
||||
local instancedata = mapdata[inst]
|
||||
if instancedata then
|
||||
instancedata[guid] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function GetData(self, field)
|
||||
local otype = self:GetObjectType()
|
||||
local guid = otype == "Map" and 1 or self:GetGUIDLow()
|
||||
local varStore = variableStores[otype]
|
||||
|
||||
if otype == "Player" then
|
||||
varStore[guid] = varStore[guid] or {}
|
||||
if field ~= nil then
|
||||
return varStore[guid][field]
|
||||
end
|
||||
return varStore[guid]
|
||||
end
|
||||
|
||||
local map = self:GetMapId()
|
||||
local inst = self:GetInstanceId()
|
||||
varStore[map] = varStore[map] or {}
|
||||
varStore[map][inst] = varStore[map][inst] or {}
|
||||
varStore[map][inst][guid] = varStore[map][inst][guid] or {}
|
||||
|
||||
if field ~= nil then
|
||||
return varStore[map][inst][guid][field]
|
||||
end
|
||||
return varStore[map][inst][guid]
|
||||
end
|
||||
|
||||
local function SetData(self, field, val)
|
||||
local otype = self:GetObjectType()
|
||||
local guid = otype == "Map" and 1 or self:GetGUIDLow()
|
||||
local varStore = variableStores[otype]
|
||||
|
||||
if otype == "Player" then
|
||||
varStore[guid] = varStore[guid] or {}
|
||||
varStore[guid][field] = val
|
||||
return
|
||||
end
|
||||
|
||||
local map = self:GetMapId()
|
||||
local inst = self:GetInstanceId()
|
||||
varStore[map] = varStore[map] or {}
|
||||
varStore[map][inst] = varStore[map][inst] or {}
|
||||
varStore[map][inst][guid] = varStore[map][inst][guid] or {}
|
||||
|
||||
varStore[map][inst][guid][field] = val
|
||||
end
|
||||
|
||||
for k,v in pairs(variableStores) do
|
||||
_G[k].GetData = GetData
|
||||
_G[k].SetData = SetData
|
||||
end
|
||||
|
||||
RegisterPlayerEvent(4, DestroyObjData) -- logout
|
||||
RegisterServerEvent(31, DestroyObjData) -- creature delete
|
||||
RegisterServerEvent(32, DestroyObjData) -- gameobject delete
|
||||
RegisterServerEvent(17, DestroyMapData) -- map create
|
||||
RegisterServerEvent(18, DestroyMapData) -- map destroy
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010 Ignacio Burgueño
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,128 @@
|
||||
# StackTracePlus #
|
||||
|
||||
[](https://travis-ci.org/ignacio/StackTracePlus)
|
||||
|
||||
[StackTracePlus](https://github.com/ignacio/StackTracePlus) provides enhanced stack traces for [Lua 5.1, Lua 5.2][1] and [LuaJIT][2].
|
||||
|
||||
StackTracePlus can be used as a replacement for debug.traceback. It gives detailed information about locals, tries to guess
|
||||
function names when they're not available, etc, so, instead of
|
||||
|
||||
lua5.1.exe: D:\trunk_git\sources\stacktraceplus\test\test.lua:10: attempt to concatenate a nil value
|
||||
stack traceback:
|
||||
D:\trunk_git\sources\stacktraceplus\test\test.lua:10: in function <D:\trunk_git\sources\stacktraceplus\test\test.lua:7>
|
||||
(tail call): ?
|
||||
D:\trunk_git\sources\stacktraceplus\test\test.lua:15: in main chunk
|
||||
[C]: ?
|
||||
|
||||
you'll get
|
||||
|
||||
lua5.1.exe: D:\trunk_git\sources\stacktraceplus\test\test.lua:10: attempt to concatenate a nil value
|
||||
Stack Traceback
|
||||
===============
|
||||
(2) C function 'function: 00A8F418'
|
||||
(3) Lua function 'g' at file 'D:\trunk_git\sources\stacktraceplus\test\test.lua:10' (best guess)
|
||||
Local variables:
|
||||
fun = table module
|
||||
str = string: "hey"
|
||||
tb = table: 027DCBE0 {dummy:1, blah:true, foo:bar}
|
||||
(*temporary) = nil
|
||||
(*temporary) = string: "text"
|
||||
(*temporary) = string: "attempt to concatenate a nil value"
|
||||
(4) tail call
|
||||
(5) main chunk of file 'D:\trunk_git\sources\stacktraceplus\test\test.lua' at line 15
|
||||
(6) C function 'function: 002CA480'
|
||||
|
||||
## Usage #
|
||||
|
||||
StackTracePlus can be used as a replacement for `debug.traceback`, as an `xpcall` error handler or even from C code. Note that
|
||||
only the Lua 5.1 interpreter allows the traceback function to be replaced "on the fly". LuaJIT and Lua 5.2 always calls luaL_traceback internally so there is no easy way to override that.
|
||||
|
||||
```lua
|
||||
local STP = require "StackTracePlus"
|
||||
|
||||
debug.traceback = STP.stacktrace
|
||||
function test()
|
||||
local s = "this is a string"
|
||||
local n = 42
|
||||
local t = { foo = "bar" }
|
||||
local co = coroutine
|
||||
local cr = coroutine.create
|
||||
|
||||
error("an error")
|
||||
end
|
||||
test()
|
||||
```
|
||||
|
||||
That script will output (only with Lua 5.1):
|
||||
|
||||
lua5.1: example.lua:11: an error
|
||||
Stack Traceback
|
||||
===============
|
||||
(2) C function 'function: 006B5758'
|
||||
(3) global C function 'error'
|
||||
(4) Lua global 'test' at file 'example.lua:11'
|
||||
Local variables:
|
||||
s = string: "this is a string"
|
||||
n = number: 42
|
||||
t = table: 006E5220 {foo:bar}
|
||||
co = coroutine table
|
||||
cr = C function: 003C7080
|
||||
(5) main chunk of file 'example.lua' at line 14
|
||||
(6) C function 'function: 00637B30'
|
||||
|
||||
**StackTracePlus** is aware of the usual Lua libraries, like *coroutine*, *table*, *string*, *io*, etc and functions like
|
||||
*print*, *pcall*, *assert*, and so on.
|
||||
|
||||
You can also make STP aware of your own tables and functions by calling *add_known_function* and *add_known_table*.
|
||||
|
||||
```lua
|
||||
local STP = require "StackTracePlus"
|
||||
|
||||
debug.traceback = STP.stacktrace
|
||||
local my_table = {
|
||||
f = function() end
|
||||
}
|
||||
function my_function()
|
||||
end
|
||||
|
||||
function test(data, func)
|
||||
local s = "this is a string"
|
||||
|
||||
error("an error")
|
||||
end
|
||||
|
||||
STP.add_known_table(my_table, "A description for my_table")
|
||||
STP.add_known_function(my_function, "A description for my_function")
|
||||
|
||||
test( my_table, my_function )
|
||||
```
|
||||
|
||||
Will output:
|
||||
|
||||
lua5.1: ..\test\example2.lua:13: an error
|
||||
Stack Traceback
|
||||
===============
|
||||
(2) C function 'function: 0073AAA8'
|
||||
(3) global C function 'error'
|
||||
(4) Lua global 'test' at file '..\test\example2.lua:13'
|
||||
Local variables:
|
||||
data = A description for my_table
|
||||
func = Lua function 'A description for my_function' (defined at line 7 of chunk ..\test\example2.lua)
|
||||
s = string: "this is a string"
|
||||
(5) main chunk of file '..\test\example2.lua' at line 19
|
||||
(6) C function 'function: 00317B30'
|
||||
|
||||
|
||||
## Installation #
|
||||
The easiest way to install is with [LuaRocks][3].
|
||||
|
||||
- luarocks install stacktraceplus
|
||||
|
||||
If you don't want to use LuaRocks, just copy StackTracePlus.lua to Lua's path.
|
||||
|
||||
## License #
|
||||
**StackTracePlus** is available under the MIT license.
|
||||
|
||||
[1]: http://www.lua.org/
|
||||
[2]: http://luajit.org/
|
||||
[3]: http://luarocks.org/
|
||||
@@ -0,0 +1,411 @@
|
||||
-- tables
|
||||
local _G = _G
|
||||
local string, io, debug, coroutine = string, io, debug, coroutine
|
||||
|
||||
-- functions
|
||||
local tostring, print, require = tostring, print, require
|
||||
local next, assert = next, assert
|
||||
local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs
|
||||
local error = error
|
||||
|
||||
assert(debug, "debug table must be available at this point")
|
||||
|
||||
local io_open = io.open
|
||||
local string_gmatch = string.gmatch
|
||||
local string_sub = string.sub
|
||||
local table_concat = table.concat
|
||||
|
||||
local _M = {
|
||||
max_tb_output_len = 70 -- controls the maximum length of the 'stringified' table before cutting with ' (more...)'
|
||||
}
|
||||
|
||||
-- this tables should be weak so the elements in them won't become uncollectable
|
||||
local m_known_tables = { [_G] = "_G (global table)" }
|
||||
local function add_known_module(name, desc)
|
||||
local ok, mod = pcall(require, name)
|
||||
if ok then
|
||||
m_known_tables[mod] = desc
|
||||
end
|
||||
end
|
||||
|
||||
add_known_module("string", "string module")
|
||||
add_known_module("io", "io module")
|
||||
add_known_module("os", "os module")
|
||||
add_known_module("table", "table module")
|
||||
add_known_module("math", "math module")
|
||||
add_known_module("package", "package module")
|
||||
add_known_module("debug", "debug module")
|
||||
add_known_module("coroutine", "coroutine module")
|
||||
|
||||
-- lua5.2
|
||||
add_known_module("bit32", "bit32 module")
|
||||
-- luajit
|
||||
add_known_module("bit", "bit module")
|
||||
add_known_module("jit", "jit module")
|
||||
|
||||
|
||||
local m_user_known_tables = {}
|
||||
|
||||
local m_known_functions = {}
|
||||
for _, name in ipairs{
|
||||
-- Lua 5.2, 5.1
|
||||
"assert",
|
||||
"collectgarbage",
|
||||
"dofile",
|
||||
"error",
|
||||
"getmetatable",
|
||||
"ipairs",
|
||||
"load",
|
||||
"loadfile",
|
||||
"next",
|
||||
"pairs",
|
||||
"pcall",
|
||||
"print",
|
||||
"rawequal",
|
||||
"rawget",
|
||||
"rawlen",
|
||||
"rawset",
|
||||
"require",
|
||||
"select",
|
||||
"setmetatable",
|
||||
"tonumber",
|
||||
"tostring",
|
||||
"type",
|
||||
"xpcall",
|
||||
|
||||
-- Lua 5.1
|
||||
"gcinfo",
|
||||
"getfenv",
|
||||
"loadstring",
|
||||
"module",
|
||||
"newproxy",
|
||||
"setfenv",
|
||||
"unpack",
|
||||
-- TODO: add table.* etc functions
|
||||
} do
|
||||
if _G[name] then
|
||||
m_known_functions[_G[name]] = name
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
local m_user_known_functions = {}
|
||||
|
||||
local function safe_tostring (value)
|
||||
local ok, err = pcall(tostring, value)
|
||||
if ok then return err else return ("<failed to get printable value>: '%s'"):format(err) end
|
||||
end
|
||||
|
||||
-- Private:
|
||||
-- Parses a line, looking for possible function definitions (in a very na?ve way)
|
||||
-- Returns '(anonymous)' if no function name was found in the line
|
||||
local function ParseLine(line)
|
||||
assert(type(line) == "string")
|
||||
--print(line)
|
||||
local match = line:match("^%s*function%s+(%w+)")
|
||||
if match then
|
||||
--print("+++++++++++++function", match)
|
||||
return match
|
||||
end
|
||||
match = line:match("^%s*local%s+function%s+(%w+)")
|
||||
if match then
|
||||
--print("++++++++++++local", match)
|
||||
return match
|
||||
end
|
||||
match = line:match("^%s*local%s+(%w+)%s+=%s+function")
|
||||
if match then
|
||||
--print("++++++++++++local func", match)
|
||||
return match
|
||||
end
|
||||
match = line:match("%s*function%s*%(") -- this is an anonymous function
|
||||
if match then
|
||||
--print("+++++++++++++function2", match)
|
||||
return "(anonymous)"
|
||||
end
|
||||
return "(anonymous)"
|
||||
end
|
||||
|
||||
-- Private:
|
||||
-- Tries to guess a function's name when the debug info structure does not have it.
|
||||
-- It parses either the file or the string where the function is defined.
|
||||
-- Returns '?' if the line where the function is defined is not found
|
||||
local function GuessFunctionName(info)
|
||||
--print("guessing function name")
|
||||
if type(info.source) == "string" and info.source:sub(1,1) == "@" then
|
||||
local file, err = io_open(info.source:sub(2), "r")
|
||||
if not file then
|
||||
print("file not found: "..tostring(err)) -- whoops!
|
||||
return "?"
|
||||
end
|
||||
local line
|
||||
for i = 1, info.linedefined do
|
||||
line = file:read("*l")
|
||||
end
|
||||
if not line then
|
||||
print("line not found") -- whoops!
|
||||
return "?"
|
||||
end
|
||||
return ParseLine(line)
|
||||
else
|
||||
local line
|
||||
local lineNumber = 0
|
||||
for l in string_gmatch(info.source, "([^\n]+)\n-") do
|
||||
lineNumber = lineNumber + 1
|
||||
if lineNumber == info.linedefined then
|
||||
line = l
|
||||
break
|
||||
end
|
||||
end
|
||||
if not line then
|
||||
print("line not found") -- whoops!
|
||||
return "?"
|
||||
end
|
||||
return ParseLine(line)
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- Dumper instances are used to analyze stacks and collect its information.
|
||||
--
|
||||
local Dumper = {}
|
||||
|
||||
Dumper.new = function(thread)
|
||||
local t = { lines = {} }
|
||||
for k,v in pairs(Dumper) do t[k] = v end
|
||||
|
||||
t.dumping_same_thread = (thread == coroutine.running())
|
||||
|
||||
-- if a thread was supplied, bind it to debug.info and debug.get
|
||||
-- we also need to skip this additional level we are introducing in the callstack (only if we are running
|
||||
-- in the same thread we're inspecting)
|
||||
if type(thread) == "thread" then
|
||||
t.getinfo = function(level, what)
|
||||
if t.dumping_same_thread and type(level) == "number" then
|
||||
level = level + 1
|
||||
end
|
||||
return debug.getinfo(thread, level, what)
|
||||
end
|
||||
t.getlocal = function(level, loc)
|
||||
if t.dumping_same_thread then
|
||||
level = level + 1
|
||||
end
|
||||
return debug.getlocal(thread, level, loc)
|
||||
end
|
||||
else
|
||||
t.getinfo = debug.getinfo
|
||||
t.getlocal = debug.getlocal
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
-- helpers for collecting strings to be used when assembling the final trace
|
||||
function Dumper:add (text)
|
||||
self.lines[#self.lines + 1] = text
|
||||
end
|
||||
function Dumper:add_f (fmt, ...)
|
||||
self:add(fmt:format(...))
|
||||
end
|
||||
function Dumper:concat_lines ()
|
||||
return table_concat(self.lines)
|
||||
end
|
||||
|
||||
---
|
||||
-- Private:
|
||||
-- Iterates over the local variables of a given function.
|
||||
--
|
||||
-- @param level The stack level where the function is.
|
||||
--
|
||||
function Dumper:DumpLocals (level)
|
||||
local prefix = "\t "
|
||||
local i = 1
|
||||
|
||||
if self.dumping_same_thread then
|
||||
level = level + 1
|
||||
end
|
||||
|
||||
local name, value = self.getlocal(level, i)
|
||||
if not name then
|
||||
return
|
||||
end
|
||||
self:add("\tLocal variables:\r\n")
|
||||
while name do
|
||||
if type(value) == "number" then
|
||||
self:add_f("%s%s = number: %g\r\n", prefix, name, value)
|
||||
elseif type(value) == "boolean" then
|
||||
self:add_f("%s%s = boolean: %s\r\n", prefix, name, tostring(value))
|
||||
elseif type(value) == "string" then
|
||||
self:add_f("%s%s = string: %q\r\n", prefix, name, value)
|
||||
elseif type(value) == "userdata" then
|
||||
self:add_f("%s%s = %s\r\n", prefix, name, safe_tostring(value))
|
||||
elseif type(value) == "nil" then
|
||||
self:add_f("%s%s = nil\r\n", prefix, name)
|
||||
elseif type(value) == "table" then
|
||||
if m_known_tables[value] then
|
||||
self:add_f("%s%s = %s\r\n", prefix, name, m_known_tables[value])
|
||||
elseif m_user_known_tables[value] then
|
||||
self:add_f("%s%s = %s\r\n", prefix, name, m_user_known_tables[value])
|
||||
else
|
||||
local txt = "{"
|
||||
for k,v in pairs(value) do
|
||||
txt = txt..safe_tostring(k)..":"..safe_tostring(v)
|
||||
if #txt > _M.max_tb_output_len then
|
||||
txt = txt.." (more...)"
|
||||
break
|
||||
end
|
||||
if next(value, k) then txt = txt..", " end
|
||||
end
|
||||
self:add_f("%s%s = %s %s\r\n", prefix, name, safe_tostring(value), txt.."}")
|
||||
end
|
||||
elseif type(value) == "function" then
|
||||
local info = self.getinfo(value, "nS")
|
||||
local fun_name = info.name or m_known_functions[value] or m_user_known_functions[value]
|
||||
if info.what == "C" then
|
||||
self:add_f("%s%s = C %s\r\n", prefix, name, (fun_name and ("function: " .. fun_name) or tostring(value)))
|
||||
else
|
||||
local source = info.short_src
|
||||
if source:sub(2,7) == "string" then
|
||||
source = source:sub(9) -- uno m?s, por el espacio que viene (string "Baragent.Main", por ejemplo)
|
||||
end
|
||||
--for k,v in pairs(info) do print(k,v) end
|
||||
fun_name = fun_name or GuessFunctionName(info)
|
||||
self:add_f("%s%s = Lua function '%s' (defined at line %d of chunk %s)\r\n", prefix, name, fun_name, info.linedefined, source)
|
||||
end
|
||||
elseif type(value) == "thread" then
|
||||
self:add_f("%sthread %q = %s\r\n", prefix, name, tostring(value))
|
||||
end
|
||||
i = i + 1
|
||||
name, value = self.getlocal(level, i)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- Public:
|
||||
-- Collects a detailed stack trace, dumping locals, resolving function names when they're not available, etc.
|
||||
-- This function is suitable to be used as an error handler with pcall or xpcall
|
||||
--
|
||||
-- @param thread An optional thread whose stack is to be inspected (defaul is the current thread)
|
||||
-- @param message An optional error string or object.
|
||||
-- @param level An optional number telling at which level to start the traceback (default is 1)
|
||||
--
|
||||
-- Returns a string with the stack trace and a string with the original error.
|
||||
--
|
||||
function _M.stacktrace(thread, message, level)
|
||||
if type(thread) ~= "thread" then
|
||||
-- shift parameters left
|
||||
thread, message, level = nil, thread, message
|
||||
end
|
||||
|
||||
thread = thread or coroutine.running()
|
||||
|
||||
level = level or 1
|
||||
|
||||
local dumper = Dumper.new(thread)
|
||||
|
||||
local original_error
|
||||
|
||||
if type(message) == "table" then
|
||||
dumper:add("an error object {\r\n")
|
||||
local first = true
|
||||
for k,v in pairs(message) do
|
||||
if first then
|
||||
dumper:add(" ")
|
||||
first = false
|
||||
else
|
||||
dumper:add(",\r\n ")
|
||||
end
|
||||
dumper:add(safe_tostring(k))
|
||||
dumper:add(": ")
|
||||
dumper:add(safe_tostring(v))
|
||||
end
|
||||
dumper:add("\r\n}")
|
||||
original_error = dumper:concat_lines()
|
||||
elseif type(message) == "string" then
|
||||
dumper:add(message)
|
||||
original_error = message
|
||||
end
|
||||
|
||||
dumper:add("\r\n")
|
||||
dumper:add[[
|
||||
Stack Traceback
|
||||
===============
|
||||
]]
|
||||
--print(error_message)
|
||||
|
||||
local level_to_show = level
|
||||
if dumper.dumping_same_thread then level = level + 1 end
|
||||
|
||||
local info = dumper.getinfo(level, "nSlf")
|
||||
while info do
|
||||
if info.what == "main" then
|
||||
if string_sub(info.source, 1, 1) == "@" then
|
||||
dumper:add_f("(%d) main chunk of file '%s' at line %d\r\n", level_to_show, string_sub(info.source, 2), info.currentline)
|
||||
else
|
||||
dumper:add_f("(%d) main chunk of %s at line %d\r\n", level_to_show, info.short_src, info.currentline)
|
||||
end
|
||||
elseif info.what == "C" then
|
||||
--print(info.namewhat, info.name)
|
||||
--for k,v in pairs(info) do print(k,v, type(v)) end
|
||||
local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name or tostring(info.func)
|
||||
dumper:add_f("(%d) %s C function '%s'\r\n", level_to_show, info.namewhat, function_name)
|
||||
--dumper:add_f("%s%s = C %s\r\n", prefix, name, (m_known_functions[value] and ("function: " .. m_known_functions[value]) or tostring(value)))
|
||||
elseif info.what == "tail" then
|
||||
--print("tail")
|
||||
--for k,v in pairs(info) do print(k,v, type(v)) end--print(info.namewhat, info.name)
|
||||
dumper:add_f("(%d) tail call\r\n", level_to_show)
|
||||
dumper:DumpLocals(level)
|
||||
elseif info.what == "Lua" then
|
||||
local source = info.short_src
|
||||
local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name
|
||||
if source:sub(2, 7) == "string" then
|
||||
source = source:sub(9)
|
||||
end
|
||||
local was_guessed = false
|
||||
if not function_name or function_name == "?" then
|
||||
--for k,v in pairs(info) do print(k,v, type(v)) end
|
||||
function_name = GuessFunctionName(info)
|
||||
was_guessed = true
|
||||
end
|
||||
-- test if we have a file name
|
||||
local function_type = (info.namewhat == "") and "function" or info.namewhat
|
||||
if info.source and info.source:sub(1, 1) == "@" then
|
||||
dumper:add_f("(%d) Lua %s '%s' at file '%s:%d'%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "")
|
||||
elseif info.source and info.source:sub(1,1) == '#' then
|
||||
dumper:add_f("(%d) Lua %s '%s' at template '%s:%d'%s\r\n", level_to_show, function_type, function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "")
|
||||
else
|
||||
dumper:add_f("(%d) Lua %s '%s' at line %d of chunk '%s'\r\n", level_to_show, function_type, function_name, info.currentline, source)
|
||||
end
|
||||
dumper:DumpLocals(level)
|
||||
else
|
||||
dumper:add_f("(%d) unknown frame %s\r\n", level_to_show, info.what)
|
||||
end
|
||||
|
||||
level = level + 1
|
||||
level_to_show = level_to_show + 1
|
||||
info = dumper.getinfo(level, "nSlf")
|
||||
end
|
||||
|
||||
return dumper:concat_lines(), original_error
|
||||
end
|
||||
|
||||
--
|
||||
-- Adds a table to the list of known tables
|
||||
function _M.add_known_table(tab, description)
|
||||
if m_known_tables[tab] then
|
||||
error("Cannot override an already known table")
|
||||
end
|
||||
m_user_known_tables[tab] = description
|
||||
end
|
||||
|
||||
--
|
||||
-- Adds a function to the list of known functions
|
||||
function _M.add_known_function(fun, description)
|
||||
if m_known_functions[fun] then
|
||||
error("Cannot override an already known function")
|
||||
end
|
||||
m_user_known_functions[fun] = description
|
||||
end
|
||||
|
||||
return _M
|
||||
@@ -0,0 +1,14 @@
|
||||
--
|
||||
-- Copyright (C) 2010 - 2016 Eluna Lua Engine <http://emudevs.com/>
|
||||
-- This program is free software licensed under GPL version 3
|
||||
-- Please see the included DOCS/LICENSE.md for more information
|
||||
--
|
||||
|
||||
-- filename.ext files are loaded before normal .lua files
|
||||
|
||||
-- Randomize random
|
||||
math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,6)))
|
||||
|
||||
-- Set debug.traceback to use StackTracePlus to print full stack trace
|
||||
local trace = require("StackTracePlus")
|
||||
debug.traceback = trace.stacktrace
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#include "Hooks.h"
|
||||
#include "HookHelpers.h"
|
||||
#include "LuaEngine.h"
|
||||
#include "BindingMap.h"
|
||||
#include "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<AllCreatureEvents>(EVENT);\
|
||||
if (!AllCreatureEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(EVENT, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto key = EventKey<AllCreatureEvents>(EVENT);\
|
||||
if (!AllCreatureEventBindings->HasBindingsFor(key))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnAllCreatureAddToWorld(Creature* creature)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_ADD);
|
||||
Push(creature);
|
||||
CallAllFunctions(AllCreatureEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureRemoveFromWorld(Creature* creature)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_REMOVE);
|
||||
Push(creature);
|
||||
CallAllFunctions(AllCreatureEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_SELECT_LEVEL);
|
||||
Push(cinfo);
|
||||
Push(creature);
|
||||
CallAllFunctions(AllCreatureEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureBeforeSelectLevel(const CreatureTemplate* cinfo, Creature* creature, uint8& level)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_BEFORE_SELECT_LEVEL);
|
||||
Push(cinfo);
|
||||
Push(creature);
|
||||
Push(level);
|
||||
int levelIndex = lua_gettop(L);
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 3);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
level = CHECKVAL<uint8>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(level, levelIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureAuraApply(Creature* me, Aura* aura)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_AURA_APPLY);
|
||||
Push(me);
|
||||
Push(aura);
|
||||
CallAllFunctions(AllCreatureEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureAuraRemove(Creature* me, Aura* aura, AuraRemoveMode mode)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_AURA_REMOVE);
|
||||
Push(me);
|
||||
Push(aura);
|
||||
Push(mode);
|
||||
CallAllFunctions(AllCreatureEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureHeal(Creature* me, Unit* target, uint32& gain)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_HEAL);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(gain);
|
||||
|
||||
int gainIndex = lua_gettop(L);
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
gain = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(gain, gainIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureDamage(Creature* me, Unit* target, uint32& damage)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_DAMAGE);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureModifyPeriodicDamageAurasTick(Creature* me, Unit* target, uint32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(spellInfo);
|
||||
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureModifyMeleeDamage(Creature* me, Unit* target, uint32& damage)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_MODIFY_MELEE_DAMAGE);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureModifySpellDamageTaken(Creature* me, Unit* target, int32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(spellInfo);
|
||||
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<int32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnAllCreatureModifyHealReceived(Creature* me, Unit* target, uint32& heal, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(ALL_CREATURE_EVENT_ON_MODIFY_HEAL_RECEIVED);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(heal);
|
||||
Push(spellInfo);
|
||||
|
||||
int healIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
heal = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(heal, healIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
uint32 ALE::OnAllCreatureDealDamage(Creature* me, Unit* target, uint32 damage, DamageEffectType damagetype)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(ALL_CREATURE_EVENT_ON_DEAL_DAMAGE, damage);
|
||||
uint32 result = damage;
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(damagetype);
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(AllCreatureEventBindings, key, 4);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
result = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(result, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#include "Hooks.h"
|
||||
#include "HookHelpers.h"
|
||||
#include "LuaEngine.h"
|
||||
#include "BindingMap.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto key = EventKey<BGEvents>(EVENT);\
|
||||
if (!BGEventBindings->HasBindingsFor(key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnBGStart(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId)
|
||||
{
|
||||
START_HOOK(BG_EVENT_ON_START);
|
||||
Push(bg);
|
||||
Push(bgId);
|
||||
Push(instanceId);
|
||||
CallAllFunctions(BGEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnBGEnd(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId, TeamId winner)
|
||||
{
|
||||
START_HOOK(BG_EVENT_ON_END);
|
||||
Push(bg);
|
||||
Push(bgId);
|
||||
Push(instanceId);
|
||||
Push(winner);
|
||||
CallAllFunctions(BGEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnBGCreate(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId)
|
||||
{
|
||||
START_HOOK(BG_EVENT_ON_CREATE);
|
||||
Push(bg);
|
||||
Push(bgId);
|
||||
Push(instanceId);
|
||||
CallAllFunctions(BGEventBindings, key);
|
||||
}
|
||||
|
||||
void ALE::OnBGDestroy(BattleGround* bg, BattleGroundTypeId bgId, uint32 instanceId)
|
||||
{
|
||||
START_HOOK(BG_EVENT_ON_PRE_DESTROY);
|
||||
Push(bg);
|
||||
Push(bgId);
|
||||
Push(instanceId);
|
||||
CallAllFunctions(BGEventBindings, key);
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* Copyright (C) 2010 - 2025 Eluna Lua Engine <https://elunaluaengine.github.io/>
|
||||
* This program is free software licensed under GPL version 3
|
||||
* Please see the included DOCS/LICENSE.md for more information
|
||||
*/
|
||||
|
||||
#include "Hooks.h"
|
||||
#include "HookHelpers.h"
|
||||
#include "LuaEngine.h"
|
||||
#include "BindingMap.h"
|
||||
#include "ALEIncludes.h"
|
||||
#include "ALETemplate.h"
|
||||
|
||||
using namespace Hooks;
|
||||
|
||||
#define START_HOOK(EVENT, CREATURE) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return;\
|
||||
auto entry_key = EntryKey<CreatureEvents>(EVENT, CREATURE->GetEntry());\
|
||||
auto unique_key = UniqueObjectKey<CreatureEvents>(EVENT, CREATURE->GET_GUID(), CREATURE->GetInstanceId());\
|
||||
if (!CreatureEventBindings->HasBindingsFor(entry_key))\
|
||||
if (!CreatureUniqueBindings->HasBindingsFor(unique_key))\
|
||||
return;\
|
||||
LOCK_ALE
|
||||
|
||||
#define START_HOOK_WITH_RETVAL(EVENT, CREATURE, RETVAL) \
|
||||
if (!ALEConfig::GetInstance().IsALEEnabled())\
|
||||
return RETVAL;\
|
||||
auto entry_key = EntryKey<CreatureEvents>(EVENT, CREATURE->GetEntry());\
|
||||
auto unique_key = UniqueObjectKey<CreatureEvents>(EVENT, CREATURE->GET_GUID(), CREATURE->GetInstanceId());\
|
||||
if (!CreatureEventBindings->HasBindingsFor(entry_key))\
|
||||
if (!CreatureUniqueBindings->HasBindingsFor(unique_key))\
|
||||
return RETVAL;\
|
||||
LOCK_ALE
|
||||
|
||||
void ALE::OnDummyEffect(WorldObject* pCaster, uint32 spellId, SpellEffIndex effIndex, Creature* pTarget)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_DUMMY_EFFECT, pTarget);
|
||||
Push(pCaster);
|
||||
Push(spellId);
|
||||
Push(effIndex);
|
||||
Push(pTarget);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
bool ALE::OnQuestAccept(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_QUEST_ACCEPT, pCreature, false);
|
||||
Push(pPlayer);
|
||||
Push(pCreature);
|
||||
Push(pQuest);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
bool ALE::OnQuestReward(Player* pPlayer, Creature* pCreature, Quest const* pQuest, uint32 opt)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_QUEST_REWARD, pCreature, false);
|
||||
Push(pPlayer);
|
||||
Push(pCreature);
|
||||
Push(pQuest);
|
||||
Push(opt);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::GetDialogStatus(const Player* pPlayer, const Creature* pCreature)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_DIALOG_STATUS, pCreature);
|
||||
Push(pPlayer);
|
||||
Push(pCreature);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::OnAddToWorld(Creature* pCreature)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_ADD, pCreature);
|
||||
Push(pCreature);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::OnRemoveFromWorld(Creature* pCreature)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_REMOVE, pCreature);
|
||||
Push(pCreature);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
bool ALE::OnSummoned(Creature* pCreature, Unit* pSummoner)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SUMMONED, pCreature, false);
|
||||
Push(pCreature);
|
||||
Push(pSummoner);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
bool ALE::UpdateAI(Creature* me, const uint32 diff)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_AIUPDATE, me, false);
|
||||
Push(me);
|
||||
Push(diff);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
//Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
|
||||
//Called at creature aggro either by MoveInLOS or Attack Start
|
||||
bool ALE::EnterCombat(Creature* me, Unit* target)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_ENTER_COMBAT, me, false);
|
||||
Push(me);
|
||||
Push(target);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called at any Damage from any attacker (before damage apply)
|
||||
bool ALE::DamageTaken(Creature* me, Unit* attacker, uint32& damage)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_DAMAGE_TAKEN, me, false);
|
||||
bool result = false;
|
||||
Push(me);
|
||||
Push(attacker);
|
||||
Push(damage);
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 3);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 2);
|
||||
|
||||
if (lua_isboolean(L, r + 0) && lua_toboolean(L, r + 0))
|
||||
result = true;
|
||||
|
||||
if (lua_isnumber(L, r + 1))
|
||||
{
|
||||
damage = ALE::CHECKVAL<uint32>(L, r + 1);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
return result;
|
||||
}
|
||||
|
||||
//Called at creature death
|
||||
bool ALE::JustDied(Creature* me, Unit* killer)
|
||||
{
|
||||
On_Reset(me);
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_DIED, me, false);
|
||||
Push(me);
|
||||
Push(killer);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
//Called at creature killing another unit
|
||||
bool ALE::KilledUnit(Creature* me, Unit* victim)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_TARGET_DIED, me, false);
|
||||
Push(me);
|
||||
Push(victim);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when the creature summon successfully other creature
|
||||
bool ALE::JustSummoned(Creature* me, Creature* summon)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_JUST_SUMMONED_CREATURE, me, false);
|
||||
Push(me);
|
||||
Push(summon);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when a summoned creature is despawned
|
||||
bool ALE::SummonedCreatureDespawn(Creature* me, Creature* summon)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SUMMONED_CREATURE_DESPAWN, me, false);
|
||||
Push(me);
|
||||
Push(summon);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
//Called at waypoint reached or PointMovement end
|
||||
bool ALE::MovementInform(Creature* me, uint32 type, uint32 id)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_REACH_WP, me, false);
|
||||
Push(me);
|
||||
Push(type);
|
||||
Push(id);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called before EnterCombat even before the creature is in combat.
|
||||
bool ALE::AttackStart(Creature* me, Unit* target)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_PRE_COMBAT, me, false);
|
||||
Push(me);
|
||||
Push(target);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called for reaction at stopping attack at no attackers or targets
|
||||
bool ALE::EnterEvadeMode(Creature* me)
|
||||
{
|
||||
On_Reset(me);
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_LEAVE_COMBAT, me, false);
|
||||
Push(me);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when creature is spawned or respawned (for reseting variables)
|
||||
bool ALE::JustRespawned(Creature* me)
|
||||
{
|
||||
On_Reset(me);
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SPAWN, me, false);
|
||||
Push(me);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called at reaching home after evade
|
||||
bool ALE::JustReachedHome(Creature* me)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_REACH_HOME, me, false);
|
||||
Push(me);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called at text emote receive from player
|
||||
bool ALE::ReceiveEmote(Creature* me, Player* player, uint32 emoteId)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_RECEIVE_EMOTE, me, false);
|
||||
Push(me);
|
||||
Push(player);
|
||||
Push(emoteId);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// called when the corpse of this creature gets removed
|
||||
bool ALE::CorpseRemoved(Creature* me, uint32& respawnDelay)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_CORPSE_REMOVED, me, false);
|
||||
bool result = false;
|
||||
Push(me);
|
||||
Push(respawnDelay);
|
||||
int respawnDelayIndex = lua_gettop(L);
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 2);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 2, 2);
|
||||
|
||||
if (lua_isboolean(L, r + 0) && lua_toboolean(L, r + 0))
|
||||
result = true;
|
||||
|
||||
if (lua_isnumber(L, r + 1))
|
||||
{
|
||||
respawnDelay = ALE::CHECKVAL<uint32>(L, r + 1);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(respawnDelay, respawnDelayIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
|
||||
CleanUpStack(2);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ALE::MoveInLineOfSight(Creature* me, Unit* who)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_MOVE_IN_LOS, me, false);
|
||||
Push(me);
|
||||
Push(who);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called on creature initial spawn, respawn, death, evade (leave combat)
|
||||
void ALE::On_Reset(Creature* me) // Not an override, custom
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_RESET, me);
|
||||
Push(me);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when hit by a spell
|
||||
bool ALE::SpellHit(Creature* me, WorldObject* caster, SpellInfo const* spell)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_HIT_BY_SPELL, me, false);
|
||||
Push(me);
|
||||
Push(caster);
|
||||
Push(spell->Id); // Pass spell object?
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when spell hits a target
|
||||
bool ALE::SpellHitTarget(Creature* me, WorldObject* target, SpellInfo const* spell)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SPELL_HIT_TARGET, me, false);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(spell->Id); // Pass spell object?
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
bool ALE::SummonedCreatureDies(Creature* me, Creature* summon, Unit* killer)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_SUMMONED_CREATURE_DIED, me, false);
|
||||
Push(me);
|
||||
Push(summon);
|
||||
Push(killer);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when owner takes damage
|
||||
bool ALE::OwnerAttackedBy(Creature* me, Unit* attacker)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_OWNER_ATTACKED_AT, me, false);
|
||||
Push(me);
|
||||
Push(attacker);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
// Called when owner attacks something
|
||||
bool ALE::OwnerAttacked(Creature* me, Unit* target)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_OWNER_ATTACKED, me, false);
|
||||
Push(me);
|
||||
Push(target);
|
||||
return CallAllFunctionsBool(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureAuraApply(Creature* me, Aura* aura)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_AURA_APPLY, me);
|
||||
Push(me);
|
||||
Push(aura);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureAuraRemove(Creature* me, Aura* aura, AuraRemoveMode mode)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_AURA_REMOVE, me);
|
||||
Push(me);
|
||||
Push(aura);
|
||||
Push(mode);
|
||||
CallAllFunctions(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureHeal(Creature* me, Unit* target, uint32& gain)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_HEAL, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(gain);
|
||||
|
||||
int gainIndex = lua_gettop(L);
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
gain = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(gain, gainIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureDamage(Creature* me, Unit* target, uint32& damage)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_DAMAGE, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureModifyPeriodicDamageAurasTick(Creature* me, Unit* target, uint32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_MODIFY_PERIODIC_DAMAGE_AURAS_TICK, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(spellInfo);
|
||||
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureModifyMeleeDamage(Creature* me, Unit* target, uint32& damage)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_MODIFY_MELEE_DAMAGE, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
|
||||
int damageIndex = lua_gettop(L);
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 3);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 3, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(3);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureModifySpellDamageTaken(Creature* me, Unit* target, int32& damage, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_MODIFY_SPELL_DAMAGE_TAKEN, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(spellInfo);
|
||||
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
damage = CHECKVAL<int32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(damage, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
void ALE::OnCreatureModifyHealReceived(Creature* me, Unit* target, uint32& heal, SpellInfo const* spellInfo)
|
||||
{
|
||||
START_HOOK(CREATURE_EVENT_ON_MODIFY_HEAL_RECEIVED, me);
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(heal);
|
||||
Push(spellInfo);
|
||||
|
||||
int healIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 4);
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
heal = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(heal, healIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
}
|
||||
|
||||
uint32 ALE::OnCreatureDealDamage(Creature* me, Unit* target, uint32 damage, DamageEffectType damagetype)
|
||||
{
|
||||
START_HOOK_WITH_RETVAL(CREATURE_EVENT_ON_DEAL_DAMAGE, me, damage);
|
||||
uint32 result = damage;
|
||||
Push(me);
|
||||
Push(target);
|
||||
Push(damage);
|
||||
Push(damagetype);
|
||||
int damageIndex = lua_gettop(L) - 1;
|
||||
int n = SetupStack(CreatureEventBindings, CreatureUniqueBindings, entry_key, unique_key, 4);
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
int r = CallOneFunction(n--, 4, 1);
|
||||
|
||||
if (lua_isnumber(L, r))
|
||||
{
|
||||
result = CHECKVAL<uint32>(L, r);
|
||||
// Update the stack for subsequent calls.
|
||||
ReplaceArgument(result, damageIndex);
|
||||
}
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
CleanUpStack(4);
|
||||
return result;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user