⚠️ This plugin is under active development. It is not yet published on the Moodle Plugin Directory. Some features described below are planned and not yet implemented.
PlayerGames (local_playergames) is the central hub of the PlayerGames gamification
ecosystem for Moodle. It serves four main purposes:
- Ecosystem Dashboard — a visual overview of all Player plugins installed on the site, with status, dependencies, and quick-access links.
- Player Hub — a site-wide gamification platform for students and/or other Moodle users (teachers, managers, administrators), with XP, levels, seasons, missions, achievements, avatars and daily streak. The administrator configures which groups participate.
- Daily Mini-games — concept-reinforcement mini-games and a daily check-in powered by content cartridges (JSON, optionally AI-generated via the companion local_aihub plugin), working as a Duolingo-style learning loop.
- Companion Sidebar Block — block_playergames mirrors a player’s profile (avatar, level, XP, streak, today’s games, ranking) on the site front page and Dashboard.
Use the sidebar to jump to any section on this page.
Source code: github.com/jeanlucio/moodle-local_playergames
✨ Features
✅ Implemented
- 🗺️ Ecosystem Dashboard: SVG overview of all Player plugins — installed, missing, dependencies, status, and quick-action links for admins.
- 🎮 Player Hub — XP & Levels: Site-wide XP across configurable seasons. Each mini-game awards a fixed amount of XP, and admins set how many scoring plays per day each game allows — XP is earned exclusively through gameplay, so teachers with different course loads and students enrolled in different numbers of courses all compete on equal footing.
- 🪜 Player Hub — Configurable Level Ladder: Admins edit the per-level XP thresholds and titles from the Level Ladder page — restore the default 5-tier ladder or generate a longer linear progression with one click.
- 📅 Player Hub — Daily Check-in: Earn XP just for visiting the hub once a day, capped per season. Optionally counts toward the daily streak.
- 🏆 Player Hub — Season Ranking: Leaderboard with privacy controls (opt-in), tie-breaks and a staff/student split — see Ranking Behavior.
- 📘 Player Hub — Learning XP: An optional, separate XP pool mirrored from a student’s
per-course activity in
block_playerhud, with its own opt-in ranking — see Learning XP. - 🔥 Player Hub — Streak & Freeze: Daily streak tracking. Freeze consumables prevent streak loss and are earned via missions; the daily check-in can keep the streak alive when configured.
- 🎯 Player Hub — Missions: Daily, streak-based, cumulative XP, and victory-based missions with configurable XP rewards.
- 🏅 Player Hub — Achievements: Permanent achievements that persist across seasons.
- 🏷️ Player Hub — Titles: Level-based titles visible in Moodle profiles, forums, and courses.
- 🦊 Player Hub — Avatar Collection: Emoji avatars unlocked permanently by the highest level a player has ever reached, grouped into 4 configurable tiers — see Avatars.
- 🕐 Unified Activity Log: A single, chronological log of every XP, streak and freeze event (season XP, learning XP, freeze earned/used, streak broken), with a shared “how it works” help modal explaining season XP, learning XP, avatars and rankings.
- 📦 Cartridge System: Content source for the mini-games. Supports multiple active cartridges
simultaneously.
- All games: manual creation (inline editor), JSON upload, or AI generation (see AI Provider Chain).
- PlayerQuiz: also accepts the Moodle Question Bank (multiple-choice questions only).
- 🧠 PlayerQuiz: Daily multiple-choice mini-game using concepts from the active cartridge — see How PlayerQuiz Works.
- 🔡 PlayerGuess: Daily Wordle-style mini-game — guess the term letter by letter — see How PlayerGuess Works.
- 🧩 PlayerGames Block: Companion sidebar block (
block_playergames) showing the user’s equipped avatar, level, XP, streak, today’s games, and ranking position on the site front page and Dashboard, linking to the full Player Hub — see PlayerGames Ecosystem. - 📅 Season Management: Create, close, and auto-renew seasons with configuration snapshots. Historical data is preserved when a season closes.
- 🔐 Privacy (GDPR): Complete Privacy Provider — metadata declaration, export and deletion of all stored personal data; shared cartridges are preserved with the uploader anonymised.
- 🧪 Automated Tests: 232-case PHPUnit suite, green across the full CI matrix (see the Automated Tests section).
⏳ In Development / Planned
- 📝 PlayerFill: Crossword-style mini-game — numbered positions; the same number shares the same letter across multiple words; solving one word reveals letters in others (cascade effect). Grid generated in PHP without external libraries.
- ⚔️ PlayerBattle: Match-3 RPG mini-game (8×8 grid) with turn-based combat against a boss powered by Phaser 3. Combining mana pieces charges a question; correct answer → triple damage; wrong answer → player takes damage.
- 📦 Phaser Centralized:
local_playergameswill servephaser.min.jsto all Player plugins vialocal_playergames_get_phaser_url(), removing duplicated copies from each plugin. - 🛡️ Publication Polish: Full accessibility audit and broader Behat acceptance coverage (PHPUnit suite and PHPCS compliance are already in place).
🤖 AI Provider Chain
Key storage and provider transport (Gemini, Groq, DeepSeek, OpenAI-compatible) live in a dedicated companion plugin, local_aihub. PlayerGames itself only builds the cartridge generation prompt (topic, language, concept count, difficulty, categories) and parses the AI’s JSON response back into concepts, so it stays free of key management while still supporting AI-assisted cartridge generation wherever the Hub is installed.
Resolution order
| Priority | Source | Notes |
|---|---|---|
| 1 | local_aihub | Tried first when installed. Resolves personal keys, then site-wide keys, across Gemini / Groq / DeepSeek / OpenAI-compatible providers. |
| 2 | Moodle core_ai |
Institutional fallback, used only if the Hub is not installed or returns no usable source. Uses whichever providers the admin configured in Site administration → AI → AI providers. |
A real provider failure (e.g. an invalid key configured in the Hub) is preserved and surfaced to the user — it is never silently masked as “no AI source available”.
Installing
local_aihubis optional. Without it, cartridge AI generation still works if the site hascore_aiconfigured; PlayerGames only loses the BYOK personal-key option (each teacher bringing their own Gemini/Groq/DeepSeek/OpenAI key) that the Hub provides.
Integration API for other plugins
cartridge\ai_generator exposes a small, stable API that other plugins in the ecosystem can
call directly:
use local_playergames\cartridge\ai_generator;
$gen = new ai_generator();
if ($gen->has_key()) {
// Free-form prompt, routed through the same chain as cartridge generation.
$text = $gen->generate_text('Optional system instruction', 'Your prompt here');
}
has_key(): bool—trueif eitherlocal_aihubhas a usable key orcore_aihas a configured provider.generate_text(string $system, string $user, bool $jsonmode = false): string— sends a free system+user prompt through the chain and returns the raw text response.send(string $prompt): array— lower-level variant returning the full result array (success,data,message,provider).generate(string $topic, string $language, int $count, int $difficulty, array $categorynames = [], string $context = ''): array— the structured cartridge-concept generator; use only when you need concept arrays in{term, definition, category, difficulty}format.
Every AI generation triggered from PlayerGames is tagged in the Hub’s own usage log with the
component (local_playergames) and a short description of what was generated (e.g. the cartridge
topic), so site administrators can see AI usage per consuming plugin from the Hub’s admin report.
📦 Cartridge Format
Content cartridges are JSON files that power all mini-games. The same pack feeds PlayerQuiz, PlayerGuess, PlayerFill, and PlayerBattle:
{
"name": "Gamification Fundamentals",
"version": "1.0",
"language": "en",
"concepts": [
{
"term": "gamification",
"definition": "Use of game elements in non-game contexts",
"category": "fundamentals",
"difficulty": 2
}
]
}
- Validated by the importer before saving
- Terms for PlayerGuess: filtered by configurable length (default 4–8 letters) and must be a single alphabetic word (no spaces, digits or hyphens) — see How PlayerGuess Works
- Quiz-type cartridges: each question accepts an optional
generalfeedbackstring, shown to the player after they answer; PlayerQuiz can also draw from the Moodle Question Bank instead of or alongside cartridges - Admin can have multiple active cartridges simultaneously
- Multi-language: each cartridge declares its
language
🧠 How PlayerQuiz Works
PlayerQuiz is the first daily mini-game. It draws multiple-choice questions from the active cartridges (and, optionally, the Moodle Question Bank) and shows them one at a time.
The play loop
- One question is shown at a time, with a per-question countdown.
- Wrong answer (or the timer running out) → the correct option is revealed, then the next question loads.
- Correct answer → the play ends and XP is awarded.
- With an attempt limit set, running out of attempts ends the play in failure (no XP) and may start a cooldown.
Because a play ends on the first correct answer, a player rarely answers the whole pool — most questions in a session are just a buffer that provides variety and room to be wrong before repeating.
Two independent limits (this trips people up)
| Setting | Axis | Meaning |
|---|---|---|
Games per day (xp_games_quiz) |
the day | How many plays earn XP per day. Also sets the daily XP cap: XP per game × games per day. |
Questions per session (quiz_session_size) |
inside one play | How many questions are drawn into a single play (the “deck” of that round). |
Example — with XP per game = 25, games per day = 3, questions per session = 20: a player can complete 3 plays today (cap 75 XP), and each play is built from a fresh draw of 20 questions. The two numbers answer different questions: how many rounds today vs how big each round’s deck is.
Fresh questions and repeats
- Each page load builds a brand-new shuffled session — there is no saved “session of the day”.
- Questions are remembered as “seen today” only when a play is completed with a correct answer. Those are then excluded from the other plays of the same day, so completed plays never repeat questions.
- Leaving mid-play or failing records nothing — reopening the page gives a fresh random draw that may include the same questions.
- The “seen today” memory is per day: the next day starts clean and may re-draw questions from previous days. There is no cross-day exclusion.
- Reloading never grants extra XP — it only changes which questions appear; XP is still capped by games per day and only counts on a correct answer.
Admin settings
Site administration → Plugins → Local plugins → PlayerGames → Game rules
| Setting | Default | What it does |
|---|---|---|
| Time per question (seconds) | 120 (2 min) | Countdown per question. Reaching zero counts as a wrong answer and advances. 0 disables the timer. |
| Maximum attempts | 0 (unlimited) | Wrong answers (including timeouts) that end a play without XP. 0 keeps the original behaviour — the game continues until the player answers correctly. |
| Cooldown after failing (minutes) | 0 | When a play ends in failure, the quiz is locked for this many minutes. Persisted server-side (a page reload does not bypass it). Only meaningful together with an attempt limit. |
| Questions per session | 20 | Size of each play’s draw, chosen from a capped list (5–50). |
How the timer, attempts and cooldown interlock
- A timer expiry is treated exactly like a wrong answer.
- A play can only fail when a maximum-attempts limit is set; with unlimited attempts (
0) the game loops until a correct answer, so the cooldown never triggers. - A failed play awards no XP and does not consume a daily play — the cooldown is the only
throttle, so with cooldown
0the player may retry immediately.
Why the session size is capped
Every question of a session is rendered into the page HTML at once (the browser just reveals them one by one). A very large session would format and ship hundreds of questions the player will never see — wasted server work and a heavy page. The capped list (max 50) keeps plenty of variety without that cost. Truly large sessions would require a different architecture (loading questions on demand), which is not how the game works today.
🔡 How PlayerGuess Works
PlayerGuess is a daily Wordle-style mini-game: guess a term from the active cartridge, letter by letter, within a limited number of attempts.
The play loop
- The board shows the term’s definition (as a hint) and its length, but not the term itself.
- Each guess must be the exact same length as the target and contain only letters.
- After each guess, every letter is marked correct (right letter, right position), present (right letter, wrong position) or absent — the classic Wordle feedback, computed server-side so the term is never exposed to the browser before the round ends.
- Correct guess → the round ends in a win and XP is awarded.
- Running out of attempts → the round ends in a loss and the term is revealed.
- Guessing and validation always happen server-side against the day’s assigned concept — the client never receives or trusts anything about the target term until the round is over.
One concept per day, shared assignment pipeline
Every night, a scheduled task assigns one random concept to PlayerGuess (and, independently, to PlayerQuiz) from the currently active cartridges — the same pipeline used across the daily mini-games. For PlayerGuess specifically, only concepts whose term:
- has a normalised length within the admin-configured range (default 4–8 letters), and
- is a single alphabetic word — no spaces, digits or hyphens
are eligible. This filter is applied identically at assignment time and again when validating a guess, so a term that happens to fit the length window but contains, say, a hyphen is never assigned to a PlayerGuess day.
Admin settings
Site administration → Plugins → Local plugins → PlayerGames → Game rules
| Setting | Default | What it does |
|---|---|---|
| Maximum attempts | 6 | Guesses allowed before the round ends in a loss and the term is revealed. |
| Minimum term length | 4 | Shortest term eligible for the daily assignment. |
| Maximum term length | 8 | Longest term eligible for the daily assignment. |
Site administration → Plugins → Local plugins → PlayerGames → XP and rewards
| Setting | Default | What it does |
|---|---|---|
| XP per Guess game | 25 | XP awarded on a winning round. |
| Guess games per day | 1 | How many wins per day earn XP — once reached, the game shows “already played today”. |
Round state and its limits
The in-progress round (guesses made so far, whether it’s finished, whether it was won) is kept in the user’s session, not in a database table. It automatically resets if the stored round no longer matches today’s assigned concept (a new day, or a re-assigned concept). This means:
- Leaving the page and coming back within the same session resumes the round exactly where it was left, including a finished round — a win or a loss stays final for the rest of the day in that session.
- Only a win is recorded server-side (through the same daily-play accounting used by the other games); a loss is not written anywhere outside the session.
- Known limitation: because a loss leaves no server-side trace, logging out or continuing
from a different browser session gives a fresh, empty board for the same day’s term — the new
session has no memory of the earlier loss. A win is unaffected: it is already recorded, so the
“already played today” screen still applies. This mirrors a tradeoff
mod_playerwordsmakes for its own session-backed rounds, accepted in favour of not adding a dedicated database table for a same-day, single-device play.
🦊 Avatars
PlayerGames has its own emoji avatar collection, unlocked permanently by the highest level a player has ever reached — no shop, no file uploads, just a level-tier reward.
Catalog and tiers
The default catalog has 17 emoji avatars split across 4 tiers (5 in tier 1, 4 each in tiers 2–4): 🤖 👾 🦊 🐱 🐶 (tier 1), 🕵️ 🤺 🧚 🧜 (tier 2), 🧝 🧙 🦸 🥷 (tier 3), and 🧛 🦹 🐉 🦁 (tier 4).
Unlock rule
Unlocks are based on the highest level the player has ever reached, for life — not their current level, and not scoped to the current season. Every time a player earns enough XP to level up, PlayerGames records the new level only if it exceeds the best level stored for that player; closing a season, which resets season XP back to zero, never takes an already-unlocked avatar away.
Tier thresholds
| Tier | Default minimum level |
|---|---|
| 1 | 1 |
| 2 | 5 |
| 3 | 10 |
| 4 | 20 |
Admins can change each tier’s minimum level independently (Site administration → Plugins → Local plugins → PlayerGames → Avatars).
Equip and display
A player can equip any avatar they’ve unlocked (or unequip back to their default Moodle profile picture) from a tier-grouped picker — locked avatars show grayed out alongside their required level. The equipped avatar replaces the default profile picture in the Player Hub’s profile card and in the PlayerGames block’s widget, and stays in sync between the two since both read and write through the same avatar manager.
📘 Learning XP
Learning XP is a separate, optional XP pool that mirrors a student’s per-course XP earned in
block_playerhud into PlayerGames, purely for cross-course visibility — it is never summed
with season XP and never affects levels, avatar unlocks or the season ranking.
Where it comes from
When block_playerhud is installed and a student earns or loses course XP, PlayerGames listens
for that change and mirrors the delta into its own windowed learning XP total. Nothing needs
to be configured on the block_playerhud side beyond having it installed — the bridge is
optional and stays dormant if block_playerhud isn’t present.
The window
Learning XP is not a lifetime total: it only counts XP earned within a rolling window
(default 12 months, configurable in months; 0 means unlimited/lifetime). Data is kept in
monthly buckets, so the window ages correctly month by month, and a nightly scheduled task
recomputes every user’s windowed total to correct any drift and drop buckets that have aged out.
Visibility
Learning XP is shown only when both are true:
- the admin has turned it on (Site administration → Plugins → Local plugins → PlayerGames → Learning XP), and
- the site’s participant setting allows students (
studentsorboth).
Unlike most other student-facing features, visibility is not tied to whether the viewer is flagged as staff — a teacher who is also a genuine PlayerHUD-earning student in another course still sees their own learning XP.
Its own ranking
Learning XP has its own separate, opt-in ranking — a single top-50 list (not split by staff/student like the season ranking), gated by its own admin toggle. Players with zero windowed XP are excluded entirely rather than occupying a rank, and each player has an independent opt-in toggle from the season ranking’s own visibility setting. Position lookup for players outside the top 50 follows the same tie-break rule as the season ranking (highest XP first, then earliest to reach it).
Where it’s shown
Both the Player Hub page and the PlayerGames block’s sidebar widget read from the same manager and follow the same visibility and ranking rules, so the two surfaces are always consistent with each other.
🏆 Ranking Behavior
The season ranking is opt-in, split by participant group, and shows a player their own position even outside the visible top 50.
Opt-in and the top-50 list
Players choose whether to appear in the ranking from their profile. The visible list is the
top 50 by season XP, ordered by XP descending → the moment they reached that XP ascending →
user id ascending — a fully deterministic tie-break, so two players with the same XP are ranked
by who got there first.
Staff / student split
Rankings are split into two groups: students and staff (anyone with course-editing capability in at least one course, or the site-admin flag). Whether a viewer sees one or both groups depends on their own role and the site’s participant setting:
- A regular student or teacher sees only their own group’s ranking.
- An admin, or any staff member when the site is configured to allow both groups to participate, sees both the Students and Staff tabs.
Your own position, even outside the top 50
A player who opted into the ranking but isn’t in the visible top 50 still sees their own exact position — computed with a single indexed count query against the same ordering rule used for the visible list, not by scanning the whole ranking.
Scope
This page describes the season ranking (tied to season XP and reset when a season closes). Learning XP has its own separate, single (not staff/student-split) ranking with its own opt-in — see that section for details.
🎓 Educational Purpose
PlayerGames is designed to:
- Reinforce daily learning through spaced repetition (Duolingo-like loop)
- Motivate students to access the platform more frequently, building familiarity with the learning environment in a natural and engaging way
- Foster healthy competition between students and teachers across different courses, regardless of how many courses each participant is enrolled in or teaches
- Integrate students and teachers from different courses around a common gamified experience
- Promote active learning habits through daily challenges and streak mechanics
- Explore diverse knowledge areas through concept cartridges — from discipline-specific content to cross-cutting topics like foreign language learning
- Give institutional visibility into the impact of gamification (Engagement Meter)
Suitable for:
- Institutions with multiple gamified courses using block_playerhud
- Administrators and managers looking to measure gamification impact
- Any Moodle user group (students, teachers, managers, admins) that can be configured to participate in the Hub
🕹️ PlayerGames Ecosystem
PlayerGames is the hub of a broader gamification ecosystem. Together, these plugins transform Moodle into an immersive experience:
-
PlayerGames Block: Companion sidebar widget for the site front page and Dashboard — equipped avatar, level, XP, streak, today’s games and ranking position, all delegated to this plugin. Requires
local_playergames. 👉 github.com/jeanlucio/moodle-block_playergames -
AI Hub: Shared BYOK (bring your own key) broker — personal and site-wide Gemini/Groq/ DeepSeek/OpenAI-compatible keys, consumed by PlayerGames for AI-assisted cartridge generation. Optional: PlayerGames still works through Moodle’s own
core_aiwithout it. 👉 github.com/jeanlucio/moodle-local_aihub -
PlayerHUD Block: XP, levels, inventory, drops, quests, RPG classes, story, karma, and ranking inside each course. Its course XP can optionally mirror into PlayerGames’ Learning XP pool. 👉 github.com/jeanlucio/moodle-block_playerhud
-
PlayerHUD Filter: Enables item drops via shortcodes inside course content. 👉 github.com/jeanlucio/moodle-filter_playerhud
-
PlayerHUD Availability Restriction: Restricts access to course activities based on the student’s current level or collected items. 👉 github.com/jeanlucio/moodle-availability_playerhud
-
PlayerGroup: Lets students autonomously form their own groups directly from the activity page. 👉 github.com/jeanlucio/moodle-mod_playergroup
📦 Requirements
| Component | Version |
|---|---|
| Moodle | 4.5 – 5.2 |
| PHP | 8.1+ |
🛠️ Installation & Configuration
⚠️ This plugin is not yet published on the Moodle Plugin Directory. Install manually from this repository.
- Download the
.zipfile or clone this repository. - Extract the folder into your Moodle
local/directory. - Rename the folder to
playergames(if necessary). Final path:your-moodle/local/playergames/ - Visit Site administration → Notifications to complete installation.
- Go to Site administration → Plugins → Local plugins → PlayerGames to configure season, XP, avatar and game-rule settings.
- Create the first season via the Season Management page.
- Optional: install local_aihub if you want AI-assisted cartridge generation with personal/site API keys — see AI Provider Chain.
📖 Usage
For Administrators
- Install the plugin and complete the Moodle upgrade step.
- Optionally install local_aihub if you
want AI-assisted cartridge generation with personal/site keys (see
AI Provider Chain) — otherwise cartridges can still be created manually
or generated through Moodle’s own
core_ai. - Create the first season in the Season Management page, setting name, start/end dates, and the per-game XP and plays-per-day rewards.
- Optionally tune the Level Ladder page — adjust XP thresholds and titles, restore the default ladder, or generate a longer linear one.
- Upload or generate a content cartridge in the Cartridge page.
- Monitor gamification impact in the Engagement Meter page.
For Teachers
- If
local_aihubis installed, access its My AI Keys page to configure a personal API key (optional — site keys work if the admin configured them). - Use the PlayerGames hub or any Player plugin — AI features will use the configured key chain automatically.
For Students
- Visit the Player Hub to see your XP, level, ranking position, streak, and daily missions.
- Play the daily mini-games to earn XP.
- Toggle “Show in ranking” in your profile to control ranking visibility.
🧪 Automated Tests
PlayerGames ships with a PHPUnit suite covering the gamification engine, the cartridge pipeline, scheduled tasks, privacy and events. Every CI push runs the full matrix (Moodle 4.5 → 5.2, PostgreSQL & MariaDB).
PHPUnit — Unit & Integration Tests
| Test file | Cases | What is covered |
|---|---|---|
cartridge/importer_test.php |
10 | Concept and quiz import; type inference; difficulty clamping; category dedup; schema-error paths |
cartridge/exporter_test.php |
5 | Concept/quiz export structure and full import→export round-trip including root metadata |
cartridge/category_manager_test.php |
7 | Category CRUD, incrementing sortorder, idempotent ensure, ownership guard, concept null-on-delete |
cartridge/quiz_generator_test.php |
10 | Quiz response parsing, category/difficulty defaults, save_standalone persistence |
cartridge/ai_generator_test.php |
5 | Concept response parser: wrapped/bare/fenced JSON, invalid and missing-concepts errors; delegation to the AI Hub facade with a core_ai fallback |
hub/xp_manager_test.php |
17 | Level thresholds, per-game cap enforcement, uncapped mission award, level-up event, season ranking position/tie-break |
hub/avatar_manager_test.php |
10 | Default catalog seeding, tier thresholds, lifetime best-level tracking, unlock/equip rules, locked-equip rejection |
hub/learning_xp_manager_test.php |
22 | Windowed monthly buckets, visibility gating (independent of staff status), ranking opt-in and position, recompute correctness |
hub/daily_play_manager_test.php |
3 | Multiple plays split XP evenly; play beyond the daily quota rejected; last play trimmed to the exact cap |
hub/level_manager_test.php |
8 | Ladder seeding, XP/title lookups, save renumber + zero floor, restore defaults, linear generation and bounds |
hub/checkin_manager_test.php |
11 | Check-in insert + XP award, idempotency, season cap, optional streak advance, participant eligibility |
hub/served_questions_test.php |
3 | Per-day “already shown” set grouped by source, idempotent scoping, invalid items ignored |
hub/streak_manager_test.php |
12 | Streak start/continue/reset, freeze consumption, overnight break processing |
hub/season_manager_test.php |
8 | Season lifecycle, active/upcoming resolution, exclusive activation, snapshot, create_next |
hub/mission_manager_test.php |
7 | Mission sync, progress/completion with XP reward, daily and missed check-in resets |
hub/achievement_manager_test.php |
6 | Achievement sync, granting (first game/level/all-games-day), idempotency |
hub/title_manager_test.php |
2 | Level→title key clamping and translation |
observer_test.php |
5 | game_completed drives streak/mission/achievement; records streak even without a season; mirrors block_playerhud XP changes into Learning XP |
games/quiz_loader_test.php |
12 | Cartridge source: completeness filter, session size, active-only, id filter, metadata, random draw, fresh-question exclusion and pool reuse |
games/quiz_settings_test.php |
8 | Timer, max-attempts and cooldown configuration and their interaction |
games/guess_manager_test.php |
7 | Daily concept resolution, term normalisation, Wordle-style letter feedback (including duplicate letters), guess validation |
games/season_game_config_test.php |
7 | Source helpers; enabled-record lookup; per-season listing; default seeding and preservation |
external/set_avatar_test.php |
3 | Equip/unequip via the AJAX endpoint, rejecting a locked avatar |
external/set_ranking_visibility_test.php |
2 | Season ranking opt-in/opt-out toggle |
external/set_learning_ranking_visibility_test.php |
2 | Learning XP ranking opt-in/opt-out toggle |
task/assign_daily_games_test.php |
4 | Per-game concept assignment, idempotency, no-cartridge case, PlayerGuess eligibility filter |
task/reset_daily_missions_test.php |
1 | Daily mission reset + streak break orchestration |
task/close_expired_seasons_test.php |
2 | Closes expired season; auto-renew creates and activates next |
task/purge_old_scores_test.php |
2 | Retention-window purge; keep-within-window no-op |
task/recompute_learning_xp_test.php |
2 | Nightly window recompute prunes aged-out buckets and corrects cache drift |
privacy/provider_test.php |
8 | Metadata, contexts, userlist, export, and the three deletion paths |
local/access_test.php |
13 | Staff detection (site admin, course-editing capability), bulk staff-id resolution, hub visibility rules |
local/engagement_report_test.php |
4 | Empty metrics, course counting, player-course detection, scope split |
ecosystem/plugin_registry_test.php |
2 | Catalog structure and unique components |
ecosystem/plugin_status_test.php |
1 | Installed status keyed by component; hub reported installed |
event/events_test.php |
1 | All nine events trigger, are captured and render a description |
| Total | 232 |
vendor/bin/phpunit --testsuite local_playergames
Behat — Acceptance Tests
| Feature file | Scenarios | What is covered |
|---|---|---|
play_quiz.feature |
2 | The PlayerQuiz play flow end to end |
php admin/tool/behat/cli/init.php
vendor/bin/behat --tags=@local_playergames --profile=chrome
🔎 Third-party Service Disclosure
PlayerGames includes optional AI-assisted cartridge generation. This is entirely optional — all content can be created manually.
Architecture
AI generation is routed through the companion
local_aihub plugin when installed, which
owns the key storage and the provider transport; if the Hub is not installed, PlayerGames falls
back to Moodle’s own core_ai subsystem, whichever providers the site administrator configured
there.
Supported Providers (via local_aihub)
- Google Gemini — https://ai.google.dev/
- Groq — https://console.groq.com/
- DeepSeek — https://platform.deepseek.com/
- OpenAI-compatible APIs — Any provider following the OpenAI API format (e.g. OpenRouter, self-hosted models via LM Studio, Ollama proxy, etc.)
These services operate under their own terms of service and privacy policies. Full disclosure of exact model IDs and data destinations is documented in local_aihub’s own Third-party Service Disclosure.
Data Transmission
When AI generation is used, the prompt built by PlayerGames (topic, language, difficulty,
categories, and any optional reference text the teacher provides) is passed to whichever source
resolved the request (the Hub or core_ai) for processing. PlayerGames:
- Does not store prompts or AI responses beyond the cartridge concepts created inside Moodle
- Never includes student data in an AI prompt
No external communication occurs unless an AI feature is explicitly used.
Cost
None required by PlayerGames itself. If local_aihub is installed, any cost is whatever the
underlying provider charges through that plugin’s own BYOK keys; without it, PlayerGames falls
back to Moodle’s core_ai, which may be free if the site admin has configured a no-cost
institutional provider.
Demo Credentials
Not applicable — no credentials are required to install or use PlayerGames; AI-assisted cartridge generation is entirely opt-in.
📄 License
This project is licensed under the GNU General Public License v3 (GPLv3).
Copyright: 2026 Jean Lúcio
Support
Found a bug or have a question? Open an issue on the issue tracker.
Maintainer
Maintained by Jean Lúcio.