Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ ALE is a powerful, AzerothCore-specific implementation of a Lua scripting engine
### Key Features
- **Native AzerothCore Integration**: Built specifically for AzerothCore's architecture
- **Enhanced API**: Extended functionality for AzerothCore, beyond the original Eluna specification.
- **Manifest-Based Loading**: FiveM-inspired manifest system for explicit control over module and file load order (see [Manifest System](docs/MANIFEST.md))
- **Community-Driven Development**: Actively maintained with community contributions

## ⚠️ Compatibility Notice
Expand Down Expand Up @@ -83,6 +84,7 @@ make -j$(nproc)
### 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
- **[Manifest System](docs/MANIFEST.md)** - Manifest-based script loading with explicit load order control
- **[Implementation Details](docs/IMPL_DETAILS.md)** - Advanced features and technical details

### Advanced Topics
Expand Down
26 changes: 26 additions & 0 deletions conf/mod_ale.conf.dist
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,32 @@ ALE.AutoReload = false
ALE.AutoReloadInterval = 1
ALE.BytecodeCache = true

###################################################################################################
# MANIFEST MODE (auto-detected)
#
# When a manifest.json file is found in the script folder (lua_scripts/),
# ALE automatically switches to manifest-based loading (inspired by FiveM).
# No config option needed - just create the manifest file.
#
# In manifest mode:
# - .ext extension files are NOT loaded
# - Modules are loaded in the order specified by the root manifest
# - Files within each module are loaded in the order specified by the module manifest
# - If no manifest.json exists, legacy recursive scanning is used
#
# Root manifest (lua_scripts/manifest.json):
# {
# "modules": ["module_a", "module_b"]
# }
#
# Module manifest (lua_scripts/module_a/manifest.json):
# {
# "files": ["init.lua", "handlers/player.lua"]
# }
#
# If a module has no manifest.json, all supported files in that
# directory are loaded recursively (legacy behavior within the module).

###################################################################################################
# LOGGING SYSTEM SETTINGS
#
Expand Down
31 changes: 31 additions & 0 deletions docs/IMPL_DETAILS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

- [Configuration](#-configuration)
- [Script Management](#-script-management)
- [Manifest System](#-manifest-system)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the Manifest System TOC anchor.

#-manifest-system will not match the rendered ## 📦 Manifest System heading, so the TOC link will not jump correctly.

♻️ Proposed fix
- - [Manifest System](`#-manifest-system`)
+ - [Manifest System](`#manifest-system`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- [Manifest System](#-manifest-system)
- [Manifest System](`#manifest-system`)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/IMPL_DETAILS.md` at line 20, Update the Manifest System TOC entry so its
link target matches the rendered heading anchor for the “Manifest System”
section. Adjust the Markdown link in the table of contents to point to the slug
generated by the `## 📦 Manifest System` heading, ensuring the in-page
navigation works correctly.

Source: Linters/SAST tools

- [Advanced Features](#-advanced-features)
- [Database Integration](#-database-integration)
- [Performance Tips](#-performance-tips)
Expand Down Expand Up @@ -73,6 +74,9 @@ Files with `.ext` extension load before standard `.lua` files:
> [!TIP]
> Instead of using `.ext`, prefer the standard Lua `require()` function for better maintainability.

> [!NOTE]
> In manifest mode, `.ext` files are **not loaded**. See [Manifest System](MANIFEST.md) for details.

#### Using Require

The entire script folder structure is added to Lua's require path:
Expand All @@ -87,6 +91,33 @@ require("helpers")

**Note:** Omit the `.lua` extension when using `require()`.

## 📦 Manifest System

ALE supports an optional manifest-based script loading system, inspired by FiveM's resource system. This is **auto-detected**: if a `manifest.json` file exists in your script folder, manifest mode is activated automatically.

### Key Differences from Legacy Mode

| Feature | Legacy Mode | Manifest Mode |
|---------|-------------|---------------|
| File discovery | Recursive scan | Only files listed in manifests |
| Load order | Alphabetical by path | Order defined in manifests |
| `.ext` files | Loaded first | Not loaded |
| Detection | Always | Auto-detected via `manifest.json` |

### How It Works

1. A root `manifest.json` in the script folder lists modules to load (in order)
2. Each module can have its own `manifest.json` listing files to load (in order)
3. If a module has no manifest, its files are scanned recursively

### Retrocompatibility

- No `manifest.json` → legacy mode (fully backward compatible)
- Gradual migration: add a root manifest first, then add module manifests as needed

> [!TIP]
> For complete documentation including examples, migration guide, and edge cases, see **[Manifest System](MANIFEST.md)**.

## 🎯 Advanced Features

### Automatic Type Conversion
Expand Down
292 changes: 292 additions & 0 deletions docs/MANIFEST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
<div align="center">

# 📦 ALE Manifest System

*Manifest-based script loading inspired by FiveM's resource system*

[![Discord](https://img.shields.io/badge/Discord-Join%20Us-7289DA?style=for-the-badge&logo=discord&logoColor=white)](https://discord.com/invite/ZKSVREE7)
[![AzerothCore](https://img.shields.io/badge/AzerothCore-Integrated-darkgreen?style=for-the-badge)](http://www.azerothcore.org/)

---
</div>

> [!IMPORTANT]
> The manifest system is **auto-detected**. If a `manifest.json` file exists in your script folder, ALE automatically switches to manifest mode. No config option needed.

## 📋 Table of Contents

- [Overview](#-overview)
- [How It Works](#-how-it-works)
- [Root Manifest](#-root-manifest)
- [Module Manifest](#-module-manifest)
- [Load Order](#-load-order)
- [Retrocompatibility](#-retrocompatibility)
- [Differences from Legacy Mode](#-differences-from-legacy-mode)
- [Examples](#-examples)
Comment on lines +18 to +25

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the Markdown anchors in the TOC and back-to-top link.

The fragments use a leading - (#-overview, #-how-it-works, etc.), but the rendered headings will not, so these links will not resolve.

♻️ Proposed fix
- [Overview](`#-overview`)
+ [Overview](`#overview`)
- [How It Works](`#-how-it-works`)
+ [How It Works](`#how-it-works`)
- [Root Manifest](`#-root-manifest`)
+ [Root Manifest](`#root-manifest`)
- [Module Manifest](`#-module-manifest`)
+ [Module Manifest](`#module-manifest`)
- [Load Order](`#-load-order`)
+ [Load Order](`#load-order`)
- [Retrocompatibility](`#-retrocompatibility`)
+ [Retrocompatibility](`#retrocompatibility`)
- [Differences from Legacy Mode](`#-differences-from-legacy-mode`)
+ [Differences from Legacy Mode](`#differences-from-legacy-mode`)
- [Examples](`#-examples`)
+ [Examples](`#examples`)
- [⬆ Back to Top](`#-ale-manifest-system`)
+ [⬆ Back to Top](`#ale-manifest-system`)

Also applies to: 291-291

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 19-19: Link fragments should be valid

(MD051, link-fragments)


[warning] 24-24: Link fragments should be valid

(MD051, link-fragments)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/MANIFEST.md` around lines 18 - 25, The TOC and back-to-top links in the
manifest documentation use incorrect Markdown fragments with a leading hyphen,
so they do not match the rendered heading anchors. Update the links in the table
of contents and the back-to-top reference to use the exact heading slugs for the
sections like Overview, How It Works, Root Manifest, Module Manifest, Load
Order, Retrocompatibility, Differences from Legacy Mode, and Examples.

Source: Linters/SAST tools


## 🚀 Overview

ALE supports two script loading modes:

| Mode | Trigger | Behavior |
|------|---------|----------|
| **Manifest mode** | `manifest.json` exists in script folder | Loads only modules and files specified in manifests, in the defined order |
| **Legacy mode** | No `manifest.json` found | Recursively scans all folders, loads all `.lua`/`.ext`/`.moon` files, sorted alphabetically |

The manifest system is inspired by [FiveM's `fxmanifest.lua`](https://docs.fivem.net/docs/scripting-reference/resource-manifest/) resource system. It gives you explicit control over:

- **Which modules** to load (not everything in the folder)
- **In what order** modules are loaded
- **Which files** within each module to load
- **In what order** files are loaded

This solves a common problem: in legacy mode, file loading order is determined by alphabetical sorting of file paths, which means a file in `handlers/` loads before `init.lua` (because `h` < `i`). With manifests, you control the order explicitly.

## ⚙️ How It Works

```
lua_scripts/
├── manifest.json ← Root manifest (lists modules to load, in order)
├── my_module/
│ ├── manifest.json ← Module manifest (lists files to load, in order)
│ ├── init.lua
│ ├── utils/
│ │ └── helpers.lua
│ └── handlers/
│ ├── player.lua
│ └── creature.lua
└── another_module/
├── manifest.json
└── main.lua
```

1. ALE checks if `manifest.json` exists in the script folder (configured via `ALE.ScriptPath`, default: `lua_scripts`)
2. If found, manifest mode is activated
3. The root manifest is parsed to get the ordered list of modules
4. For each module, its `manifest.json` is parsed to get the ordered list of files
5. Files are loaded in the exact order specified

## 📄 Root Manifest

The root manifest (`lua_scripts/manifest.json`) lists the modules to load, in order.

```json
{
"modules": [
"my_module",
"another_module",
"subfolder/third_module"
]
}
```

### Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `modules` | `string[]` | Yes | Ordered list of module directory names (relative to the script folder) |

### Module Names

- Module names are **relative paths** from the script folder
- Nested paths are supported: `"utils/library"` refers to `lua_scripts/utils/library/`
- Order matters: modules are loaded top-to-bottom

## 📦 Module Manifest

Each module can have its own `manifest.json` listing the files to load, in order.

```json
{
"files": [
"init.lua",
"utils/helpers.lua",
"handlers/player.lua",
"handlers/creature.lua"
]
}
```

### Fields

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `files` | `string[]` | Yes | Ordered list of file paths (relative to the module directory) |

### File Paths

- File paths are **relative to the module directory**
- Nested paths are supported: `"handlers/player.lua"` refers to `my_module/handlers/player.lua`
- Only the files listed are loaded - other files in the module directory are ignored
- Order matters: files are loaded top-to-bottom

### No Module Manifest (Fallback)

If a module directory does **not** contain a `manifest.json`, ALE falls back to recursive scanning within that module. This means:

- All supported files (`.lua`, `.moon`, `.out`) in the module and its subdirectories are loaded
- Files are NOT sorted (they are loaded in directory iteration order)
- This is useful for simple modules with a single file or when order doesn't matter

> [!TIP]
> For modules with dependencies between files (e.g., `init.lua` must load before handlers), always use a module manifest to guarantee load order.

## 🔢 Load Order

The load order is fully deterministic and controlled by the manifests:

```
Root manifest modules[0] → module manifest files[0], files[1], ...
Root manifest modules[1] → module manifest files[0], files[1], ...
...
```

### Example

With this root manifest:
```json
{ "modules": ["combat", "quests"] }
```

And `combat/manifest.json`:
```json
{ "files": ["init.lua", "handlers/player.lua"] }
```

The load order is:
1. `combat/init.lua`
2. `combat/handlers/player.lua`
3. `quests/...` (first file from quests module)

### Why Load Order Matters

In legacy mode, files are sorted alphabetically by full path. This means:

| Legacy order | Manifest order |
|---|---|
| `combat/handlers/player.lua` (h) | `combat/init.lua` (1st) |
| `combat/init.lua` (i) | `combat/handlers/player.lua` (2nd) |

With legacy mode, `handlers/player.lua` loads **before** `init.lua`, which breaks if `player.lua` depends on variables set up by `init.lua`. The manifest system solves this.

## 🔄 Retrocompatibility

The manifest system is fully retrocompatible:

- **No `manifest.json`** → Legacy mode is used (recursive scan, alphabetical sort, `.ext` files loaded)
- **`manifest.json` present** → Manifest mode is used automatically
- **Module without `manifest.json`** → That module falls back to recursive scanning

You can migrate gradually:
1. Start with legacy mode (no manifest)
2. Create a root `manifest.json` listing your existing folders as modules
3. Add module manifests one by one to control file order within each module

## ⚠️ Differences from Legacy Mode

| Feature | Legacy Mode | Manifest Mode |
|---------|-------------|---------------|
| File discovery | Recursive scan of all folders | Only files listed in manifests |
| Load order | Alphabetical by file path | Order defined in manifests |
| `.ext` files | Loaded (before `.lua` files) | **NOT loaded** |
| `.lua` files | Loaded | Loaded |
| `.moon` files | Loaded | Loaded |
| `.out` files | Loaded | Loaded |
| `.dll`/`.so` files | Loaded | Loaded |
| Hidden files/dirs | Skipped | Skipped |
| `require()` paths | All subdirectories added | Only module directories and file subdirectories added |
| Auto-reload | Watches `.lua`/`.ext`/`.moon` | Watches `.lua`/`.ext`/`.moon`/`.json` |

> [!WARNING]
> If you are using `.ext` extension files and want to switch to manifest mode, convert them to regular `.lua` files and list them in your module manifest instead.

## 📝 Examples

### Single Module

**`lua_scripts/manifest.json`:**
```json
{
"modules": ["my_scripts"]
}
```

**`lua_scripts/my_scripts/manifest.json`:**
```json
{
"files": [
"config.lua",
"main.lua"
]
}
```

### Multiple Modules with Dependencies

**`lua_scripts/manifest.json`:**
```json
{
"modules": [
"core_library",
"combat_system",
"quest_system",
"economy"
]
}
```

This ensures `core_library` loads first, then `combat_system`, etc.

### Module with Nested Directories

**`lua_scripts/combat_system/manifest.json`:**
```json
{
"files": [
"init.lua",
"utils/damage_calculator.lua",
"handlers/player_handler.lua",
"handlers/creature_handler.lua"
]
}
```

### Module Without Manifest (Fallback Scanning)

If `lua_scripts/simple_scripts/` has no `manifest.json`, all supported files in that directory and its subdirectories will be loaded automatically.

### Gradual Migration

1. **Before** (legacy mode - everything loads automatically):
```
lua_scripts/
├── lib/
│ └── utils.lua
├── combat/
│ ├── init.lua
│ └── handlers.lua
└── quests/
└── main.lua
```

2. **Step 1** - Add root manifest (modules load in order, but files within each module still scan recursively):
```json
{
"modules": ["lib", "combat", "quests"]
}
```

3. **Step 2** - Add module manifest to `combat/` to control file order:
```json
{
"files": ["init.lua", "handlers.lua"]
}
```

---

<div align="center">
<sub>Developed with ❤️ by the AzerothCore and ALE community</sub>

[⬆ Back to Top](#-ale-manifest-system)
</div>
Loading