PlayerGames

Central hub of the PlayerGames gamification ecosystem for Moodle — full documentation.

English | Português

View on GitHub Download .zip

Moodle Plugin CI Moodle License Status PlayerGames Ecosystem Core Component

⚠️ 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:

  1. Ecosystem Dashboard — a visual overview of all Player plugins installed on the site, with status, dependencies, and quick-access links.
  2. 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.
  3. 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.
  4. Companion Sidebar Blockblock_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

⏳ In Development / Planned

🤖 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_aihub is optional. Without it, cartridge AI generation still works if the site has core_ai configured; 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');
}

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
    }
  ]
}

🧠 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

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

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

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

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:

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:

🦊 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:

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:

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:

Suitable for:

🕹️ PlayerGames Ecosystem

PlayerGames is the hub of a broader gamification ecosystem. Together, these plugins transform Moodle into an immersive experience:

📦 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.

  1. Download the .zip file or clone this repository.
  2. Extract the folder into your Moodle local/ directory.
  3. Rename the folder to playergames (if necessary). Final path: your-moodle/local/playergames/
  4. Visit Site administration → Notifications to complete installation.
  5. Go to Site administration → Plugins → Local plugins → PlayerGames to configure season, XP, avatar and game-rule settings.
  6. Create the first season via the Season Management page.
  7. Optional: install local_aihub if you want AI-assisted cartridge generation with personal/site API keys — see AI Provider Chain.

📖 Usage

For Administrators

  1. Install the plugin and complete the Moodle upgrade step.
  2. 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.
  3. Create the first season in the Season Management page, setting name, start/end dates, and the per-game XP and plays-per-day rewards.
  4. Optionally tune the Level Ladder page — adjust XP thresholds and titles, restore the default ladder, or generate a longer linear one.
  5. Upload or generate a content cartridge in the Cartridge page.
  6. Monitor gamification impact in the Engagement Meter page.

For Teachers

  1. If local_aihub is installed, access its My AI Keys page to configure a personal API key (optional — site keys work if the admin configured them).
  2. Use the PlayerGames hub or any Player plugin — AI features will use the configured key chain automatically.

For Students

  1. Visit the Player Hub to see your XP, level, ranking position, streak, and daily missions.
  2. Play the daily mini-games to earn XP.
  3. 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)

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:

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.