ModularJobs
Developer

Persistence

Repositories, the connect-only MySQL policy, and schema verification.

Policy

ModularJobs supports MySQL 8 only and never runs DDL from a runtime process — neither the Paper plugin nor the Rust REST API creates tables.

  • Source of truth: modularjobs-paper/src/main/resources/sql/mysql.sql
  • Apply: ./scripts/apply-mysql-schema.sh
  • Verification: SchemaPresence checks required tables and migration-sensitive columns on connect. Missing tables or current_node_key columns fail enable; runtime never repairs the schema.

Lab/integration tests may apply SQL explicitly; production paths stay connect-only. Do not reintroduce boot-time DDL or other dialects.

Repositories

RepositoryBacking table(s)Notes
Configured job trees(config-backed)Flat jobs.yml nodes grouped into complete root-owned Job trees
PlayerJobStateRepositoryjob_progression, archive_job_progressionRoot tree key, active node key, tree-wide XP; archive on leave
RelationalJobTaskRepositoryImpljob_tasks, job_task_payables, payable_recordsDirect node task definitions applied through editor paths
TimedBoostRepositorytime_boosts, time_boost_identityTimed boost persistence
PlayerUpgradeRepositoryplayer_upgradesSkill points, unlocked nodes, node levels

SQL lives in classpath resources under modularjobs-paper/src/main/resources/sql/<table>/ (.sql per operation) — repositories do not inline their operation queries.

Schema notes

  • Money/XP are DECIMAL(38,10); key-bearing identifiers use bounded VARCHAR(191).
  • job_task_payables.job_task_id → job_tasks.task_id ON DELETE CASCADE.
  • editor_sessions stores the payload as JSON with expires_at and updated_at; the REST API is the session authority.
  • player_upgrades keeps total_skill_points, unlocked_nodes (legacy migration), and authoritative node_levels.
  • job_progression.job_key is the root tree key; current_node_key is the active root/specialization node. XP is tree-wide.

Connection pooling

HikariCP pools are keyed by jdbc-url + username — identical sections share one pool. database.yml sections: payable, timed-boost, progression, upgrades.

Job and task ownership

  • jobs.yml stays flat. Root sections own max level, leveling curve, and payout curves; child sections declare parent and inherit those tree-wide rules.
  • A task row's job_key identifies its owning node. At runtime, tasks resolve root-to-active-node; a descendant replaces the whole task with the same action/context key while unrelated ancestor tasks remain.
  • The editor exports direct definitions per node. It does not materialize inherited tasks into child rows.
  • The plugin does not seed task rows. An empty MySQL task table remains empty until the editor/repository path or reviewed SQL writes definitions.
  • The fisher → fisherman rename still needs an operator-managed data update.

Concurrency invariants

  • Multi-XP awards in one payment reload player job state per payable so awards accumulate.
  • Write-back flush failure retains pending writes — no older-XP clobber.
  • Kill multi-damage pays each qualifying contributor above the cutoff.

Test strategies

  • Pure unit tests for curves/mappers — no DB.
  • MockBukkit 26.2 for listener/payment/Bukkit-touching tests.
  • Repository SQL tests need a live MySQL (MODULARJOBS_TEST_MYSQL_* or default localhost:13306); they skip when unavailable.

On this page