Rank title renames now reach every player (server ranks recomputed too)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DjMbLQujBHunCCu5GpsHaT
This commit is contained in:
2 files changed
+52
-3
No files matched your search
@@ -297,14 +297,25 @@ fn validate_rank_mapping(data: &serde_json::Map<String, Value>) -> AppResult<()>
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Titles and rank names are shown from a copy kept on each player, so they must be recomputed whenever a reward is
|
||||||
|
/// created, renamed, moved or deleted. Otherwise a rename on the admin page never reaches the launcher.
|
||||||
async fn refresh_global_titles(state: &AppState) -> AppResult<()> {
|
async fn refresh_global_titles(state: &AppState) -> AppResult<()> {
|
||||||
let players: Vec<(String, i64)> = sqlx::query_as("SELECT uuid, global_xp FROM user_levels").fetch_all(&state.db).await?;
|
let mut tx = state.db.begin().await?;
|
||||||
|
let players: Vec<(String, i64)> = sqlx::query_as("SELECT uuid, global_xp FROM user_levels").fetch_all(&mut *tx).await?;
|
||||||
for (uuid, xp) in players {
|
for (uuid, xp) in players {
|
||||||
let level = level_from_xp(xp).0;
|
let level = level_from_xp(xp).0;
|
||||||
let title: Option<String> = sqlx::query_scalar("SELECT reward_name FROM level_rewards WHERE level_type='global' AND reward_type='title' AND level_req<=? ORDER BY level_req DESC,id DESC LIMIT 1")
|
let title: Option<String> = sqlx::query_scalar("SELECT reward_name FROM level_rewards WHERE level_type='global' AND reward_type='title' AND level_req<=? ORDER BY level_req DESC,id DESC LIMIT 1")
|
||||||
.bind(level).fetch_optional(&state.db).await?;
|
.bind(level).fetch_optional(&mut *tx).await?;
|
||||||
sqlx::query("UPDATE user_levels SET title=? WHERE uuid=?").bind(title).bind(uuid).execute(&state.db).await?;
|
sqlx::query("UPDATE user_levels SET title=? WHERE uuid=?").bind(title).bind(uuid).execute(&mut *tx).await?;
|
||||||
}
|
}
|
||||||
|
let members: Vec<(i64, String, i64)> = sqlx::query_as("SELECT server_id, uuid, server_xp FROM server_levels").fetch_all(&mut *tx).await?;
|
||||||
|
for (server_id, uuid, xp) in members {
|
||||||
|
let level = level_from_xp(xp).0;
|
||||||
|
let rank: Option<String> = sqlx::query_scalar("SELECT reward_name FROM level_rewards WHERE level_type='server' AND server_id=? AND reward_type='title' AND level_req<=? ORDER BY level_req DESC,id DESC LIMIT 1")
|
||||||
|
.bind(server_id).bind(level).fetch_optional(&mut *tx).await?;
|
||||||
|
sqlx::query("UPDATE server_levels SET rank_name=? WHERE server_id=? AND uuid=?").bind(rank).bind(server_id).bind(uuid).execute(&mut *tx).await?;
|
||||||
|
}
|
||||||
|
tx.commit().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,12 +390,17 @@ pub async fn admin_update_reward(
|
|||||||
|
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"UPDATE level_rewards SET
|
"UPDATE level_rewards SET
|
||||||
|
level_type = COALESCE(?, level_type),
|
||||||
|
server_id = CASE WHEN ? = 1 THEN ? ELSE server_id END,
|
||||||
level_req = ?,
|
level_req = ?,
|
||||||
reward_type = ?,
|
reward_type = ?,
|
||||||
reward_name = ?,
|
reward_name = ?,
|
||||||
reward_data = ?
|
reward_data = ?
|
||||||
WHERE id = ?",
|
WHERE id = ?",
|
||||||
)
|
)
|
||||||
|
.bind(payload.level_type.clone())
|
||||||
|
.bind(payload.level_type.is_some() as i64)
|
||||||
|
.bind(payload.server_id)
|
||||||
.bind(level_req)
|
.bind(level_req)
|
||||||
.bind(payload.reward_type)
|
.bind(payload.reward_type)
|
||||||
.bind(reward_name)
|
.bind(reward_name)
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
mod common;
|
||||||
|
use common::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn renaming_a_rank_title_reaches_players() {
|
||||||
|
let t = setup().await;
|
||||||
|
let admin = t.login("admin", "supersecret").await;
|
||||||
|
let (_, user) = t.call("POST", "/api/admin/users", Some(&admin), Some(json!({"username":"Steve","password":"password123"}))).await;
|
||||||
|
assert!(user.get("id").is_some() || user.get("user").is_some(), "{user}");
|
||||||
|
let steve = t.login("Steve", "password123").await;
|
||||||
|
let uuid = t.uuid("Steve").await;
|
||||||
|
let (_, server) = t.call("POST", "/api/admin/servers", Some(&admin), Some(json!({"name":"SMP","instance_id":"smp"}))).await;
|
||||||
|
let sid = server["server"]["id"].as_i64().unwrap();
|
||||||
|
sqlx::query("INSERT INTO server_levels (server_id, uuid, server_xp, server_level, updated_at) VALUES (?, ?, 100000, 10, 'now') ON CONFLICT(server_id, uuid) DO UPDATE SET server_xp = 100000")
|
||||||
|
.bind(sid).bind(&uuid).execute(&t.db).await.unwrap();
|
||||||
|
sqlx::query("INSERT INTO user_levels (uuid, global_xp, global_level, updated_at) VALUES (?, 100000, 10, 'now') ON CONFLICT(uuid) DO UPDATE SET global_xp = 100000").bind(&uuid).execute(&t.db).await.unwrap();
|
||||||
|
|
||||||
|
let (s, made) = t.call("POST", "/api/admin/rewards", Some(&admin), Some(json!({"level_type":"server","server_id":sid,"level_req":2,"reward_type":"title","reward_name":"Squire"}))).await;
|
||||||
|
assert_eq!(s, StatusCode::OK, "{made}");
|
||||||
|
let id = made["id"].as_i64().unwrap();
|
||||||
|
let rank = |v: &Value| v["server_levels"][0]["rank_name"].clone();
|
||||||
|
let (_, levels) = t.call("GET", &format!("/api/v1/levels/player/{uuid}"), Some(&steve), None).await;
|
||||||
|
assert_eq!(rank(&levels), "Squire", "{levels}");
|
||||||
|
|
||||||
|
let (s, r) = t.call("PUT", &format!("/api/admin/rewards/{id}"), Some(&admin), Some(json!({"level_type":"server","server_id":sid,"level_req":2,"reward_type":"title","reward_name":"Knight"}))).await;
|
||||||
|
assert_eq!(s, StatusCode::OK, "{r}");
|
||||||
|
let (_, levels) = t.call("GET", &format!("/api/v1/levels/player/{uuid}"), Some(&steve), None).await;
|
||||||
|
assert_eq!(rank(&levels), "Knight", "a renamed rank shows up straight away");
|
||||||
|
|
||||||
|
assert_eq!(t.call("DELETE", &format!("/api/admin/rewards/{id}"), Some(&admin), None).await.0, StatusCode::OK);
|
||||||
|
let (_, levels) = t.call("GET", &format!("/api/v1/levels/player/{uuid}"), Some(&steve), None).await;
|
||||||
|
assert!(rank(&levels).is_null(), "{levels}");
|
||||||
|
}
|
||||||
Reference in new issue
Block a user