MESKA
Last updated
May be stale
This page was last updated ago, on 5 October 2026. Check what it says against the code before relying on it.
MESKA is CityRP's Metrics, Skills and Achievements framework. It stores player activity as metrics, derives skill experience and levels from those metrics, and evaluates achievements against persistent skill statistics.
Startup lifecycle
core/libraries/meska/init.lua loads MESKA in this order:
- Shared constants and helpers.
- Database and cache providers.
- Configuration and timers.
- Daily bonus, custom bonus, metrics, skills and achievements.
- MESKA's Garry's Mod hooks.
After CityRP reports DBConnected, MESKA:
- loads achievement definitions;
- loads daily bonuses;
- synchronizes configured metrics;
- emits
MESKA::ACHIEVEMENTS::READY; and - emits
MESKA::INTEGRATIONS::SETUPfor add-on skills.
New skills must register on MESKA::INTEGRATIONS::SETUP. Registering earlier can
race the database connection, while registering later can omit the skill from a
player's first metrics provisioning pass.
Skill configuration
meska.skills.setup(name, config) adds four configuration sections:
metrics: named counters and products;defaults: minimum and maximum level;perks: level-gated benefits; andguides: Skills-page guide entries.
It also registers the supplied achievements and synchronizes the metric configuration to the database.
Every metric includes:
{
lvl = 1,
exp = 1,
highlight = false,
requirements = {},
excludeFromProductEligibility = true
}
exp is the amount of skill XP granted per tracked unit. Metrics used only as
achievement counters should use exp = 0.
Persistence
MESKA uses these configured tables:
| Table | Purpose |
|---|---|
meska_metrics |
Per-skill metric definitions, defaults, perks and guides |
meska_metrics_user |
Player metrics and skill stats stored as JSON |
meska_achievements |
Achievement definitions |
meska_achievements_requirements |
Achievement requirements |
meska_achievements_rewards |
Achievement rewards |
meska_achievements_user |
Awarded achievements |
meska_dailybonus |
Global skill bonuses |
meska_custombonus |
Player-specific skill bonuses |
The database schema also uses meska_skills.name as the parent registry for
skill names, and meska_achievements_type.name for achievement types.
meska_metrics.skill, meska_achievements.skill and meska_achievements.type
carry foreign keys to those registries. MESKA creates a missing registry row
itself, through meska.skills.ensureRegistered and
meska.achievements.ensureType, before it writes metric configuration or an
achievement, so a fresh database needs no hand-inserted rows. Neither name
column has a unique index, so those helpers check for the row in the query;
an INSERT IGNORE against either table adds a duplicate row.
While a skill's registration is in flight, further ensureRegistered calls for it
issue no query and queue behind it. An insert that MariaDB aborts as a deadlock
victim is retried once and every queued callback then runs once; if the retry also
fails, MESKA logs an error naming the skill, runs no callback and tries afresh on
the next call.
Player data in meska_metrics_user has two JSON documents:
metrics.<skill>.<metric>stores metric counters; andstats.<skill>.expandstats.<skill>.lvlstore experience and level.
meska.metrics.initFor inserts missing JSON paths for a player.
meska.skills.loadFor reads those paths into ply.meska.skills and mirrors the
result to the client skills network variable.
Tracking and levels
Call:
meska.metrics.track(ply, "skill_name", "metric_name", "+1")
MESKA multiplies the tracked amount by the metric's exp, applies active daily
and custom bonuses, updates the player's metric and stats JSON, then calculates:
level = floor((experience ^ (1 / 3)) / 1.4)
The configured minimum and maximum are applied when skills load. A level
increase emits MESKA::SKILLS::LEVELUP.
Achievements
Achievement requirements are evaluated by
MESKA::Achievements::MeetsRequirements. The important requirement types are:
stats: compareskills[skill .. "_lvl"]or another skill statistic; andmetrics: compareskills[skill][metric].
MESKA scans connected players every five minutes. Systems may also call
meska.achievements.award(ply) after a metric update when immediate feedback is
important.
Requirement evaluation must treat a missing skill table as unmet. It must not index through a missing skill, because players connected during a hot reload can temporarily have an older MESKA cache.
Reset and debugging
MESKA does not currently register a chat or console command that resets player
achievements, skill XP or metrics. The Car Boosting
!boosting debug refresh|status|complete|fail actions exercise active Boosting
contracts; they do not revoke MESKA awards or clear persistent MESKA data.
Awarded achievements are rows in meska_achievements_user, while skill metrics
and stats are JSON fields in meska_metrics_user. A development reset therefore
needs a deliberately scoped database operation for one SteamID and the affected
skill or achievement, followed by a reconnect or explicit cache reload. This
should not be exposed as an unrestricted live-server command.
Base achievements
The general and fishing achievements live in
libraries/meska/achievements/server/base.lua and are written to the database
on every boot, before plugin skills register. Code is the source of truth: an
edit there reaches Live on its next boot, and a hand edit in the database does
not survive one. Each entry carries the id it holds on Live, so awarded rows
keep pointing at the same achievement on every database. Seasonal achievements
added by hand are untouched, since the sync only writes the aliases it knows.
Boosting Skill
Car Boosting registers boosting as its own skill. The Skills web page formats
that registry entry as Boosting Skill and groups Boosting achievements under
it.
The integration first ensures this parent row exists through
meska.skills.ensureRegistered("boosting", ...). Only after that callback does
it register metrics and achievements. It then re-provisions any connected
player whose cache predates the new skill.
This order prevents:
meska_metrics_meska_skills_name_fk
from rejecting the meska_metrics insert. MESKA's metric-requirement hook also
checks the intermediate skill table, preventing the later nil access previously
reported at hooks.lua:194.
Adding another skill
- Pick a lowercase stable skill identifier.
- Register on
MESKA::INTEGRATIONS::SETUP;meska.skills.setupcreates themeska_skillsregistry row. - Give counter-only metrics
exp = 0. - Use unique achievement aliases.
- Re-provision connected players if hot reload is supported.
- Confirm the skill, guides and achievements appear on the Skills page.
- Reconnect and confirm metrics, XP, levels and awards persist.
- Test with a player cache that does not yet contain the new skill.