The PlayerHUD Filter is a required companion plugin for the PlayerHUD Block. It enables the insertion of collectible item drops, trade offers, and the compact HUD widget directly inside Moodle course content using shortcodes.
This filter allows teachers to embed interactive drop elements within pages, labels, books, and other HTML-supported activities, integrating seamlessly with the PlayerHUD gamification system.
👈 Use the sidebar to jump to any section on this page.
✨ Features
- 📍 Item Drops: insert collectible item drops directly into course content — pages, labels, books, forums, and any other HTML-supported activity.
- 🔢 Per-Collection Quantity Display: a drop’s card shows the actual number of units it
grants per collection (e.g.
x2), read through the block’s item-quantity engine when present and falling back to single-unit display on an older installed block version. - 🏪 Trade Widgets: embed a PlayerHUD NPC shop trade card inline, resolved by a short lookup code rather than a raw database ID.
- 🧩 Shortcode-Based Integration: three shortcodes (
[PLAYERHUD_WIDGET],[PLAYERHUD_DROP ...],[PLAYERHUD_TRADE ...]) — see Usage for the full syntax. - 🎮 Compact HUD Widget: the same player HUD shown in the block (avatar, XP, level, recent items, ranking, karma bar) embeddable anywhere a shortcode is accepted.
- ⚡ Real-Time Interaction: AJAX-based collection via Moodle’s
core/ajax, with no page redirect. - 🎒 Seamless Inventory Integration: collected items post directly into the PlayerHUD inventory system, respecting the same cooldown, limit, and secret-item rules as the block.
- 🚀 Zero N+1 Rendering: every drop/trade code on a page is bulk-loaded in a single pass before rendering, regardless of how many shortcodes appear in the same content.
- 🔐 Server-Side Validation: recharge time (cooldown), collection limits, gamification
opt-out, and the
block/playerhud:viewcapability are all enforced at render time — a shortcode never leaks item names, XP, or trade contents to a user who shouldn’t see them. - 📱 Mobile-Compatible Rendering: shortcodes render a lightweight fallback (or nothing, for the widget) inside the Moodle app, where the AJAX collection flow does not apply.
🕹️ PlayerGames Ecosystem
This plugin is part of the PlayerGames gamification ecosystem. Together, these plugins transform Moodle into an immersive experience:
-
PlayerHUD Block (Required): the gamification engine — XP, levels, inventory, quests, trade, and RPG progression. This filter has no effect without it.
-
PlayerHUD Availability Restriction: restricts access to course activities based on the student’s current level or collected items.
👉 https://github.com/jeanlucio/moodle-availability_playerhud
-
PlayerGroup: lets students autonomously form their own groups directly from the activity page — no teacher intervention needed.
📦 Requirements
| Component | Version |
|---|---|
| Moodle | 4.5+ |
| PHP | Compatible with your Moodle version |
| Required dependency | PlayerHUD Block |
🛠️ Installation
-
Ensure the PlayerHUD Block is installed first — the filter depends on the block and will not install without it.
- Download the
.zipfile or clone this repository. - Extract the folder into your Moodle
filter/directory. - Rename the folder to
playerhud(if necessary). Final path:your-moodle/filter/playerhud/ - Visit Site administration > Notifications to complete installation.
- Enable the filter in: Site administration > Plugins > Filters > Manage filters.
📖 Usage
- Ensure the PlayerHUD Block is added and configured in the course.
- Enable the PlayerHUD Filter (see Installation).
- Insert one of the shortcodes below inside any content area that runs Moodle filters — pages, labels, book chapters, forum posts, etc.
- Item drops and trade widgets render dynamically within the course; students collect/trade according to the rules defined in the PlayerHUD Block’s Management Panel.
Shortcodes are stripped out (rendered as empty) for guests, on the site front page, for a user
whose block/playerhud:view capability is prohibited, and for a student who has paused their
own gamification — in every one of those cases nothing about the underlying item or trade is
ever sent to the browser.
Shortcode Reference
[PLAYERHUD_WIDGET]
Renders the compact PlayerHUD widget: avatar, XP/level, recent items stash, ranking badge, and (when RPG mode is enabled) the karma bar and class portrait. Takes no attributes.
[PLAYERHUD_WIDGET]
[PLAYERHUD_DROP code=... mode=... text=... button_text=... button_emoji=...]
Renders a collectible item drop trigger.
| Attribute | Required | Values | Default | Description |
|---|---|---|---|---|
code |
Yes | Alphanumeric | — | The drop’s unique collection code, generated when the drop is created in the Management Panel. |
mode |
No | card, text, image |
card |
Visual presentation: a self-contained card with icon and button, an inline text link, or an icon-only clickable image. |
text |
No | Any string | The item’s name | Custom label shown next to the trigger (ignored for secret items until collected). |
button_text |
No | Any string | “Take” | Overrides the collect button’s label (card/text modes). |
button_emoji |
No | Any emoji | 🖐 | Overrides the collect button’s leading emoji (card mode). |
[PLAYERHUD_DROP code=XPTO123]
[PLAYERHUD_DROP code=XPTO123 mode=text text="Grab the sword"]
[PLAYERHUD_DROP code=XPTO123 mode=image]
[PLAYERHUD_DROP code=XPTO123 button_text="Collect!" button_emoji="⚔️"]
A secret item (marked as such in the Management Panel) always renders as a generic mystery
placeholder — name, description, and XP hidden — until the student actually collects it, even
if a custom text attribute is supplied.
An item can optionally be restricted to specific RPG classes; a student outside the allowed classes never sees the shortcode’s output at all (not even a placeholder).
[PLAYERHUD_TRADE code=...]
Renders an inline NPC shop trade card, with live affordability checking against the current user’s inventory.
| Attribute | Required | Values | Description |
|---|---|---|---|
code |
Yes | 6-character code shown in the trade’s Management Panel entry | Identifies the trade to render. |
[PLAYERHUD_TRADE code=A1B2C3]
The trade code is a short lookup convenience, not a security boundary — access to the trade
itself is always re-validated server-side (sesskey, capability, and group checks) when the
student actually performs it.
Notes
- Multiple shortcodes on the same page are all resolved in a single bulk-loading pass — adding more drops to a page does not add proportionally more database queries.
- Inside the Moodle mobile app,
[PLAYERHUD_DROP ...]and[PLAYERHUD_TRADE ...]render nothing (the AJAX collection flow is web-only); students are directed to the block’s own Backpack view instead.
🧪 Automated Tests
The filter ships with unit/integration (PHPUnit) and browser acceptance (Behat) tests, executed on every CI push against the full Moodle 4.5 → 5.2 matrix (PostgreSQL & MariaDB).
PHPUnit — Unit & Integration Tests
| Test file | Cases |
|---|---|
filter_test.php |
28 |
| Total | 28 |
vendor/bin/phpunit --testsuite filter_playerhud
Overall line coverage (PHPUnit + Xdebug): 79%.
Behat — Acceptance Tests
| Feature file | Scenarios |
|---|---|
filter_playerhud_modals.feature |
6 |
| Total | 6 |
php admin/tool/behat/cli/init.php
vendor/bin/behat --tags=@filter_playerhud --profile=chrome
Full test-by-test breakdown and coverage table →
🔐 Security & Compliance
- Capability-based access control: every shortcode re-checks
block/playerhud:viewat render time — a user who is denied that capability sees the shortcode stripped out entirely, the same as a guest or a paused player, even though the deterministic checks (login, course, gamification status) all pass. - Server-side enforcement: recharge time (cooldown) and collection limits are always validated server-side; the shortcode’s visual state (ready/cooldown/collected) is a display concern only, never the actual gate.
require_sesskey()protection: the collect and trade-processing endpoints the shortcodes link to (collect.php,process_trade.php, both owned by the PlayerHUD Block) require a valid session key on every request.- Safe deserialization: the block’s stored
configdatais read withunserialize_object(), restricting the payload tostdClass— a crafted configuration can never trigger arbitrary object instantiation or a POP-gadget chain the way a bareunserialize()could. - XSS-hardened rendering: item and RPG-class descriptions are always sanitised with
format_text()before being handed to the client, and every icon/emoji fallback in the Mustache templates uses double-mustache (escaped) output — a description was previously shipped raw in one rendering path; this has been fixed and is covered by a dedicated regression test. - Shortcode reentrancy guard: an item or RPG-class description containing the filter’s own
[PLAYERHUD_DROP ...]/[PLAYERHUD_WIDGET]shortcode is rendered withformat_text(..., ['filter' => false]), preventing it from re-enteringtext_filter::filter()and recursing until the request exhausts its memory limit — Moodle’s own filter chain has no reentrancy guard of its own. - Zero N+1 by construction: bulk pre-loading (not per-shortcode queries) means a page with many drops cannot be turned into a denial-of-service vector through query amplification.
- Moodle External API compliant: the collect flow it links to is exposed as a proper external function with its own parameter/return validation and capability gate.
- Privacy-aware: see Privacy Provider below — this plugin stores no data of its own.
- Mobile-compatible: shortcodes degrade safely inside the Moodle app instead of attempting an AJAX flow the app cannot support.
Privacy Provider
The PlayerHUD Filter implements Moodle’s null_provider — it only displays data owned and
stored by the PlayerHUD Block (items, drops, trades, inventory); it never persists any personal
data of its own. See the block’s own documentation for its full GDPR export/delete coverage.
📄 License
This project is licensed under the GNU General Public License v3 (GPLv3).
Copyright: 2026 Jean Lúcio