Structuring
This commit is contained in:
Generated
+1171
-4
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,12 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.228", features = ["serde_derive"] }
|
serde = { version = "1.0.228", features = ["serde_derive"] }
|
||||||
|
serde_json = "1.0.149"
|
||||||
session-rs = "0.1.0"
|
session-rs = "0.1.0"
|
||||||
|
sqlx = { version = "0.8.6", features = [
|
||||||
|
"sqlite",
|
||||||
|
"runtime-tokio-native-tls",
|
||||||
|
"macros",
|
||||||
|
] }
|
||||||
tokio = "1.49.0"
|
tokio = "1.49.0"
|
||||||
ureq = { version = "3.2.0", features = ["json"] }
|
ureq = { version = "3.2.0", features = ["json"] }
|
||||||
|
|||||||
+74
-4
@@ -1,3 +1,7 @@
|
|||||||
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
use crate::user::get_put;
|
||||||
|
|
||||||
pub const CLOAKS: &[&str] = &[
|
pub const CLOAKS: &[&str] = &[
|
||||||
"mercedes_flow",
|
"mercedes_flow",
|
||||||
"glitch",
|
"glitch",
|
||||||
@@ -16,10 +20,76 @@ enum CosmeticKind {
|
|||||||
Cloak,
|
Cloak,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn buy(kind: CosmeticKind, id: &str) -> Result<String, String> {
|
// Buy a cosmetic
|
||||||
Ok(format!("Item bought successfully, congrats!"))
|
pub async fn buy(
|
||||||
|
kind: CosmeticKind,
|
||||||
|
uuid: &str,
|
||||||
|
id: &str,
|
||||||
|
pool: &SqlitePool,
|
||||||
|
) -> Result<String, String> {
|
||||||
|
let mut user = get_put(uuid, pool).await?;
|
||||||
|
|
||||||
|
match kind {
|
||||||
|
CosmeticKind::Hat => {
|
||||||
|
if !HATS.contains(&id) {
|
||||||
|
return Err("Hat does not exist".into());
|
||||||
|
}
|
||||||
|
if !user.hats.contains(&id.to_string()) {
|
||||||
|
user.hats.push(id.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CosmeticKind::Cloak => {
|
||||||
|
if !CLOAKS.contains(&id) {
|
||||||
|
return Err("Cloak does not exist".into());
|
||||||
|
}
|
||||||
|
if !user.cloaks.contains(&id.to_string()) {
|
||||||
|
user.cloaks.push(id.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlx::query("UPDATE users SET cloaks = ?, hats = ? WHERE uuid = ?")
|
||||||
|
.bind(serde_json::to_string(&user.cloaks).unwrap())
|
||||||
|
.bind(serde_json::to_string(&user.hats).unwrap())
|
||||||
|
.bind(uuid)
|
||||||
|
.execute(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
Ok(format!("Item {} bought successfully!", id))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn equip(kind: CosmeticKind, id: &str) -> Result<String, String> {
|
// Equip a cosmetic
|
||||||
Ok(format!("Item equipped"))
|
pub async fn equip(
|
||||||
|
kind: CosmeticKind,
|
||||||
|
uuid: &str,
|
||||||
|
id: &str,
|
||||||
|
pool: &SqlitePool,
|
||||||
|
) -> Result<String, String> {
|
||||||
|
let mut user = get_put(uuid, pool).await?;
|
||||||
|
|
||||||
|
match kind {
|
||||||
|
CosmeticKind::Hat => {
|
||||||
|
if !user.hats.contains(&id.to_string()) {
|
||||||
|
return Err("You don't own this hat".into());
|
||||||
|
}
|
||||||
|
user.hat = id.to_string();
|
||||||
|
}
|
||||||
|
CosmeticKind::Cloak => {
|
||||||
|
if !user.cloaks.contains(&id.to_string()) {
|
||||||
|
return Err("You don't own this cloak".into());
|
||||||
|
}
|
||||||
|
user.cloak = id.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlx::query("UPDATE users SET cloak = ?, hat = ? WHERE uuid = ?")
|
||||||
|
.bind(&user.cloak)
|
||||||
|
.bind(&user.hat)
|
||||||
|
.bind(uuid)
|
||||||
|
.execute(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
Ok(format!("Item {} equipped successfully!", id))
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-8
@@ -2,22 +2,41 @@ mod cosmetics;
|
|||||||
mod methods;
|
mod methods;
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
|
use std::{pin::Pin, sync::Arc};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use session_rs::server::SessionServer;
|
use session_rs::server::SessionServer;
|
||||||
|
use sqlx::SqlitePool;
|
||||||
use ureq::http::StatusCode;
|
use ureq::http::StatusCode;
|
||||||
|
|
||||||
use crate::user::User;
|
use crate::user::User;
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() -> session_rs::Result<()> {
|
async fn main() -> session_rs::Result<()> {
|
||||||
|
let pool = Arc::new(init_db().await);
|
||||||
let server = SessionServer::bind("127.0.0.1:8080").await?;
|
let server = SessionServer::bind("127.0.0.1:8080").await?;
|
||||||
|
|
||||||
server
|
server
|
||||||
.session_loop(async |session, _| {
|
.session_loop({
|
||||||
println!("Connected");
|
let pool = Arc::clone(&pool);
|
||||||
session.on::<methods::Auth, _>(authenticate).await;
|
move |session, _| {
|
||||||
|
let pool = Arc::clone(&pool);
|
||||||
|
|
||||||
Ok(())
|
Box::pin(async move {
|
||||||
|
println!("Connected");
|
||||||
|
|
||||||
|
session
|
||||||
|
.on::<methods::Auth, _>(move |req_id, token| {
|
||||||
|
let req_id = req_id.clone();
|
||||||
|
let token = token.clone();
|
||||||
|
|
||||||
|
authenticate(req_id.clone(), token, Arc::clone(&pool))
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok::<(), session_rs::Error>(())
|
||||||
|
}) as Pin<Box<dyn Future<Output = _> + Send>>
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -28,11 +47,15 @@ struct MinecraftAuthResponse {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn authenticate(_req_id: u32, session_token: String) -> Result<User, String> {
|
async fn authenticate(
|
||||||
|
_req_id: u32,
|
||||||
|
session_token: String,
|
||||||
|
pool: Arc<SqlitePool>,
|
||||||
|
) -> Result<User, String> {
|
||||||
let mut response = ureq::get("https://api.minecraftservices.com/minecraft/profile")
|
let mut response = ureq::get("https://api.minecraftservices.com/minecraft/profile")
|
||||||
.header("Authorization", &format!("Bearer {session_token}"))
|
.header("Authorization", &format!("Bearer {session_token}"))
|
||||||
.call()
|
.call()
|
||||||
.map_err(|_| format!("Failed to validate session"))?;
|
.map_err(|_| "Failed to validate session".to_string())?;
|
||||||
|
|
||||||
if response.status() != StatusCode::OK {
|
if response.status() != StatusCode::OK {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
@@ -44,7 +67,16 @@ async fn authenticate(_req_id: u32, session_token: String) -> Result<User, Strin
|
|||||||
let auth: MinecraftAuthResponse = response
|
let auth: MinecraftAuthResponse = response
|
||||||
.body_mut()
|
.body_mut()
|
||||||
.read_json()
|
.read_json()
|
||||||
.map_err(|_| format!("Unable to parse auth response"))?;
|
.map_err(|_| "Unable to parse auth response".to_string())?;
|
||||||
|
|
||||||
user::get_put(auth.id).await
|
user::get_put(&auth.id, &pool).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_db() -> SqlitePool {
|
||||||
|
let pool = SqlitePool::connect("sqlite:cosmetics.db").await.unwrap();
|
||||||
|
sqlx::query(include_str!("schema.sql"))
|
||||||
|
.execute(&pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
pool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
uuid TEXT PRIMARY KEY,
|
||||||
|
cloak TEXT DEFAULT "",
|
||||||
|
hat TEXT DEFAULT "",
|
||||||
|
cloaks TEXT DEFAULT '[]',
|
||||||
|
hats TEXT DEFAULT '[]'
|
||||||
|
);
|
||||||
+75
-4
@@ -1,4 +1,5 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sqlx::{Row, SqlitePool};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
@@ -8,8 +9,78 @@ pub struct User {
|
|||||||
pub hats: Vec<String>,
|
pub hats: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a user
|
pub const CLOAKS: &[&str] = &[
|
||||||
pub async fn get(uuid: String) -> Result<User, String> {}
|
"mercedes_flow",
|
||||||
|
"glitch",
|
||||||
|
"crimson_mark",
|
||||||
|
"bmw",
|
||||||
|
"amg",
|
||||||
|
"amg_petronas",
|
||||||
|
"ferrari",
|
||||||
|
"redbull",
|
||||||
|
];
|
||||||
|
|
||||||
/// Create a user if it doesn't exist
|
pub const HATS: &[&str] = &["horns_black", "horns_white", "halo", "halo_black"];
|
||||||
pub async fn get_put(uuid: String) -> Result<User, String> {}
|
|
||||||
|
pub enum CosmeticKind {
|
||||||
|
Hat,
|
||||||
|
Cloak,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize SQLite connection pool
|
||||||
|
pub async fn init_db(db_path: &str) -> SqlitePool {
|
||||||
|
let pool = SqlitePool::connect(db_path).await.unwrap();
|
||||||
|
sqlx::query(include_str!("schema.sql"))
|
||||||
|
.execute(&pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user by UUID
|
||||||
|
pub async fn get(uuid: &str, pool: &SqlitePool) -> Result<User, String> {
|
||||||
|
let row = sqlx::query("SELECT cloak, hat, cloaks, hats FROM users WHERE uuid = ?")
|
||||||
|
.bind(uuid)
|
||||||
|
.fetch_optional(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
if let Some(r) = row {
|
||||||
|
let user = User {
|
||||||
|
cloak: r.get::<String, _>("cloak"),
|
||||||
|
hat: r.get::<String, _>("hat"),
|
||||||
|
cloaks: serde_json::from_str(&r.get::<String, _>("cloaks")).unwrap_or_default(),
|
||||||
|
hats: serde_json::from_str(&r.get::<String, _>("hats")).unwrap_or_default(),
|
||||||
|
};
|
||||||
|
Ok(user)
|
||||||
|
} else {
|
||||||
|
Err("User not found".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create user if not exists
|
||||||
|
pub async fn get_put(uuid: &str, pool: &SqlitePool) -> Result<User, String> {
|
||||||
|
match get(uuid, pool).await {
|
||||||
|
Ok(user) => Ok(user),
|
||||||
|
Err(_) => {
|
||||||
|
let user = User {
|
||||||
|
cloak: "".into(),
|
||||||
|
hat: "".into(),
|
||||||
|
cloaks: vec![],
|
||||||
|
hats: vec![],
|
||||||
|
};
|
||||||
|
sqlx::query(
|
||||||
|
"INSERT INTO users (uuid, cloak, hat, cloaks, hats) VALUES (?, ?, ?, ?, ?)",
|
||||||
|
)
|
||||||
|
.bind(uuid)
|
||||||
|
.bind(&user.cloak)
|
||||||
|
.bind(&user.hat)
|
||||||
|
.bind(serde_json::to_string(&user.cloaks).unwrap())
|
||||||
|
.bind(serde_json::to_string(&user.hats).unwrap())
|
||||||
|
.execute(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
Ok(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user