Admin progression controls: quest limits/rotation, XP rates, level curve, manual XP adjustments

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DjMbLQujBHunCCu5GpsHaT
This commit is contained in:
Claude committed 2026-09-30 17:25:12 +00:00
1 parent f2362def88
commit e3e25da616
10 files changed
+821 -43

No files matched your search

+14 -13
View File
@@ -395,6 +395,7 @@ pub struct Sync {
pub async fn sync(GameServer(server): GameServer, State(state): State<AppState>, Json(s): Json<Sync>) -> AppResult<Json<Value>> {
let at_now = now();
let mut tx = state.db.begin().await?;
let progression = crate::progression::load(&mut tx).await?;
let leveling_on = s.features.as_ref().and_then(|f| f.leveling_enabled).unwrap_or(true);
let mut xp_before = std::collections::HashMap::new();
if !leveling_on {
@@ -486,7 +487,7 @@ pub async fn sync(GameServer(server): GameServer, State(state): State<AppState>,
.execute(&mut *tx)
.await?;
update_progression_for_delta(&mut tx, server.id, &uuid, d, &at_now, s.features.as_ref()).await?;
update_progression_for_delta(&mut tx, server.id, &uuid, d, &at_now, s.features.as_ref(), &progression).await?;
}
let achievements_on = s.features.as_ref().and_then(|f| f.achievements_enabled).unwrap_or(true);
@@ -587,25 +588,22 @@ async fn update_progression_for_delta(
d: &StatDelta,
at_now: &str,
features: Option<&FeatureToggles>,
cfg: &crate::progression::Progression,
) -> AppResult<()> {
let leveling_on = features.and_then(|f| f.leveling_enabled).unwrap_or(true);
let quests_on = features.and_then(|f| f.quests_enabled).unwrap_or(true);
let achievements_on = features.and_then(|f| f.achievements_enabled).unwrap_or(true);
let global_mult = features.and_then(|f| f.global_xp_multiplier).unwrap_or(1.0).max(0.0);
let server_mult = features.and_then(|f| f.server_xp_multiplier).unwrap_or(1.5).max(0.0);
// Each game server's own multiplier stacks with the panel-wide one.
let global_mult = features.and_then(|f| f.global_xp_multiplier).unwrap_or(1.0).max(0.0) * cfg.global_xp_multiplier;
let server_mult = features.and_then(|f| f.server_xp_multiplier).unwrap_or(1.5).max(0.0) * cfg.server_xp_multiplier;
// 1. Calculate XP gains: Global XP and Server XP (only if leveling enabled)
if leveling_on {
let base_xp = (d.playtime_secs / 6)
+ (d.player_kills * 50)
+ (d.mob_kills * 15)
+ (d.blocks_broken / 5)
+ (d.blocks_placed / 5)
+ (d.messages * 2);
let base_xp = cfg.base_xp(d.playtime_secs, d.player_kills, d.mob_kills, d.blocks_broken, d.blocks_placed, d.messages);
let delta_xp = (base_xp as f64 * global_mult).round() as i64;
let server_delta_xp = (base_xp as f64 * server_mult).round() as i64;
let delta_xp = (base_xp * global_mult).round() as i64;
let server_delta_xp = (base_xp * server_mult).round() as i64;
if delta_xp > 0 {
sqlx::query(
@@ -679,6 +677,9 @@ async fn update_progression_for_delta(
let daily_key = crate::routes::quests::current_daily_key();
let weekly_key = crate::routes::quests::current_weekly_key();
// Only the quests this player was given for the period make progress.
let daily_ids = crate::progression::assigned_quest_ids(&mut **tx, cfg, uuid, "daily", &daily_key).await?;
let weekly_ids = crate::progression::assigned_quest_ids(&mut **tx, cfg, uuid, "weekly", &weekly_key).await?;
for (stat_name, amount) in stat_deltas {
if amount <= 0 {
@@ -693,7 +694,7 @@ async fn update_progression_for_delta(
.fetch_all(&mut **tx)
.await?;
for (qid, target) in d_quests {
for (qid, target) in d_quests.into_iter().filter(|(id, _)| daily_ids.contains(id)) {
sqlx::query(
"INSERT INTO user_quests (user_uuid, quest_id, period_key, progress, completed, claimed, updated_at)
VALUES (?, ?, ?, ?, ?, 0, ?)
@@ -721,7 +722,7 @@ async fn update_progression_for_delta(
.fetch_all(&mut **tx)
.await?;
for (qid, target) in w_quests {
for (qid, target) in w_quests.into_iter().filter(|(id, _)| weekly_ids.contains(id)) {
sqlx::query(
"INSERT INTO user_quests (user_uuid, quest_id, period_key, progress, completed, claimed, updated_at)
VALUES (?, ?, ?, ?, ?, 0, ?)