feat: complete in-game commands, GUIs, admin panel fixes, and launcher enhancements
This commit is contained in:
1 parent
a9ff92e1d1
commit
752f0cc43d
34 files changed
+4170
-181
No files matched your search
@@ -103,16 +103,56 @@ pub async fn get_user_levels_by_uuid(state: &AppState, uuid: &str) -> AppResult<
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Query rewards
|
||||
let rewards_rows: Vec<(i64, i64, String, String, String)> = sqlx::query_as(
|
||||
"SELECT id, level_req, reward_type, reward_name, reward_data
|
||||
FROM level_rewards
|
||||
ORDER BY level_req ASC",
|
||||
)
|
||||
.fetch_all(&state.db)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
let rewards = rewards_rows
|
||||
.into_iter()
|
||||
.map(|(id, req, rtype, rname, rdata)| {
|
||||
let data: Value = serde_json::from_str(&rdata).unwrap_or_default();
|
||||
let desc = data.get("description").and_then(|v| v.as_str()).unwrap_or("").to_string();
|
||||
let icon = data.get("icon").and_then(|v| v.as_str()).map(|s| s.to_string());
|
||||
scopenet_shared::LevelReward {
|
||||
id,
|
||||
level: req,
|
||||
reward_type: rtype,
|
||||
reward_value: rname,
|
||||
description: desc,
|
||||
icon,
|
||||
server_id: None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let rank_opt: Option<i64> = sqlx::query_scalar(
|
||||
"SELECT COUNT(*) + 1 FROM user_levels WHERE global_xp > ?",
|
||||
)
|
||||
.bind(global_xp)
|
||||
.fetch_optional(&state.db)
|
||||
.await
|
||||
.unwrap_or(Some(1));
|
||||
|
||||
Ok(UserLevelInfo {
|
||||
uuid: uuid.to_string(),
|
||||
global_xp,
|
||||
current_xp: global_xp,
|
||||
global_level,
|
||||
level: global_level,
|
||||
current_level_xp,
|
||||
next_level_xp,
|
||||
progress_pct,
|
||||
title,
|
||||
badges,
|
||||
server_levels,
|
||||
rank: rank_opt.map(|r| r as usize),
|
||||
rewards,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -168,13 +208,32 @@ pub async fn list_rewards(
|
||||
.into_iter()
|
||||
.map(|(id, ltype, sid, req, rtype, name, data, created)| {
|
||||
let parsed_data: Value = serde_json::from_str(&data).unwrap_or_default();
|
||||
let desc = parsed_data.get("description")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let icon = parsed_data.get("icon")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or(match rtype.as_str() {
|
||||
"title" => "👑",
|
||||
"profile_badge" | "badge" => "⭐",
|
||||
"cosmetic" => "✨",
|
||||
"item" => "🗡️",
|
||||
_ => "🎁",
|
||||
})
|
||||
.to_string();
|
||||
|
||||
serde_json::json!({
|
||||
"id": id,
|
||||
"level": req,
|
||||
"level_req": req,
|
||||
"level_type": ltype,
|
||||
"server_id": sid,
|
||||
"level_req": req,
|
||||
"reward_type": rtype,
|
||||
"reward_value": name,
|
||||
"reward_name": name,
|
||||
"description": if desc.is_empty() { format!("Granted at Level {req}") } else { desc },
|
||||
"icon": icon,
|
||||
"reward_data": parsed_data,
|
||||
"created_at": created
|
||||
})
|
||||
@@ -185,12 +244,17 @@ pub async fn list_rewards(
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CreateRewardPayload {
|
||||
pub level_type: String,
|
||||
pub struct RewardPayload {
|
||||
pub id: Option<i64>,
|
||||
pub level_type: Option<String>,
|
||||
pub server_id: Option<i64>,
|
||||
pub level_req: i64,
|
||||
#[serde(alias = "level")]
|
||||
pub level_req: Option<i64>,
|
||||
pub reward_type: String,
|
||||
pub reward_name: String,
|
||||
#[serde(alias = "reward_value")]
|
||||
pub reward_name: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub icon: Option<String>,
|
||||
pub reward_data: Option<Value>,
|
||||
}
|
||||
|
||||
@@ -198,21 +262,36 @@ pub struct CreateRewardPayload {
|
||||
pub async fn admin_create_reward(
|
||||
_admin: AdminUser,
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<CreateRewardPayload>,
|
||||
Json(payload): Json<RewardPayload>,
|
||||
) -> AppResult<Json<Value>> {
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
let data_str = serde_json::to_string(&payload.reward_data.unwrap_or_default()).unwrap_or_default();
|
||||
let level_req = payload.level_req.unwrap_or(2);
|
||||
let reward_name = payload.reward_name.unwrap_or_else(|| "Reward".into());
|
||||
let level_type = payload.level_type.unwrap_or_else(|| "global".into());
|
||||
|
||||
let mut data_obj = match payload.reward_data {
|
||||
Some(Value::Object(map)) => map,
|
||||
_ => serde_json::Map::new(),
|
||||
};
|
||||
if let Some(desc) = payload.description {
|
||||
data_obj.insert("description".into(), Value::String(desc));
|
||||
}
|
||||
if let Some(ico) = payload.icon {
|
||||
data_obj.insert("icon".into(), Value::String(ico));
|
||||
}
|
||||
data_obj.insert("title".into(), Value::String(reward_name.clone()));
|
||||
let data_str = serde_json::to_string(&data_obj).unwrap_or_default();
|
||||
|
||||
let id: i64 = sqlx::query_scalar(
|
||||
"INSERT INTO level_rewards (level_type, server_id, level_req, reward_type, reward_name, reward_data, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
RETURNING id",
|
||||
)
|
||||
.bind(payload.level_type)
|
||||
.bind(level_type)
|
||||
.bind(payload.server_id)
|
||||
.bind(payload.level_req)
|
||||
.bind(level_req)
|
||||
.bind(payload.reward_type)
|
||||
.bind(payload.reward_name)
|
||||
.bind(reward_name)
|
||||
.bind(data_str)
|
||||
.bind(&now)
|
||||
.fetch_one(&state.db)
|
||||
@@ -221,6 +300,48 @@ pub async fn admin_create_reward(
|
||||
Ok(Json(serde_json::json!({ "id": id, "ok": true })))
|
||||
}
|
||||
|
||||
/// Admin update reward.
|
||||
pub async fn admin_update_reward(
|
||||
_admin: AdminUser,
|
||||
Path(id): Path<i64>,
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<RewardPayload>,
|
||||
) -> AppResult<Json<Value>> {
|
||||
let level_req = payload.level_req.unwrap_or(2);
|
||||
let reward_name = payload.reward_name.unwrap_or_else(|| "Reward".into());
|
||||
|
||||
let mut data_obj = match payload.reward_data {
|
||||
Some(Value::Object(map)) => map,
|
||||
_ => serde_json::Map::new(),
|
||||
};
|
||||
if let Some(desc) = payload.description {
|
||||
data_obj.insert("description".into(), Value::String(desc));
|
||||
}
|
||||
if let Some(ico) = payload.icon {
|
||||
data_obj.insert("icon".into(), Value::String(ico));
|
||||
}
|
||||
data_obj.insert("title".into(), Value::String(reward_name.clone()));
|
||||
let data_str = serde_json::to_string(&data_obj).unwrap_or_default();
|
||||
|
||||
sqlx::query(
|
||||
"UPDATE level_rewards SET
|
||||
level_req = ?,
|
||||
reward_type = ?,
|
||||
reward_name = ?,
|
||||
reward_data = ?
|
||||
WHERE id = ?",
|
||||
)
|
||||
.bind(level_req)
|
||||
.bind(payload.reward_type)
|
||||
.bind(reward_name)
|
||||
.bind(data_str)
|
||||
.bind(id)
|
||||
.execute(&state.db)
|
||||
.await?;
|
||||
|
||||
Ok(Json(serde_json::json!({ "id": id, "ok": true })))
|
||||
}
|
||||
|
||||
/// Admin delete reward.
|
||||
pub async fn admin_delete_reward(
|
||||
_admin: AdminUser,
|
||||
|
||||
Reference in new issue
Block a user