From fe715d008ae189135c3d309d1f30c834f03348cb Mon Sep 17 00:00:00 2001 From: core Date: Sat, 29 Apr 2023 21:46:19 -0400 Subject: [PATCH] make pretty pt2 --- api/src/config.rs | 34 +- api/src/error.rs | 102 +++-- api/src/main.rs | 38 +- api/src/routes/beamin.rs | 103 +++-- api/src/routes/beamout.rs | 75 ++-- api/src/routes/callback.rs | 135 ++++--- api/src/routes/mod.rs | 4 +- api/src/routes/select_realm.rs | 44 +-- api/starkingdoms_api_entities/src/lib.rs | 2 +- .../m20230417_162824_create_table_users.rs | 25 +- ...17_164240_create_table_user_auth_realms.rs | 42 +- ...m20230420_144333_create_table_user_data.rs | 44 ++- protocol/src/api.rs | 4 +- protocol/src/legacy.rs | 29 +- protocol/src/lib.rs | 183 +++++---- server/build.rs | 25 +- server/src/api.rs | 61 ++- server/src/entity.rs | 18 +- server/src/handler.rs | 367 ++++++++++++------ server/src/macros.rs | 82 ++-- server/src/main.rs | 131 +++++-- server/src/manager.rs | 187 +++++---- server/src/orbit/constants.rs | 4 +- server/src/orbit/kepler.rs | 2 +- server/src/orbit/mod.rs | 6 +- server/src/orbit/newtonian.rs | 11 +- server/src/orbit/orbit.rs | 29 +- server/src/orbit/vis_viva.rs | 9 +- server/src/planet.rs | 82 ++-- server/src/timer.rs | 183 ++++++--- 30 files changed, 1248 insertions(+), 813 deletions(-) diff --git a/api/src/config.rs b/api/src/config.rs index 33b7b61676e3a93bb32cfc6daf746dd7b3e63e75..89e12f75c9461f420051aa3c9904bf7851415626 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -1,9 +1,9 @@ +use log::error; +use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; -use log::error; -use once_cell::sync::Lazy; -use serde::{Serialize, Deserialize}; pub static CONFIG: Lazy = Lazy::new(|| { let config_str = match fs::read_to_string("/etc/starkingdoms/config.toml") { @@ -31,7 +31,7 @@ pub struct StarkingdomsApiConfig { pub jwt_signing_secret: String, pub base: String, pub game: String, - pub realms: HashMap + pub realms: HashMap, } #[derive(Serialize, Deserialize, Debug)] @@ -50,13 +50,13 @@ pub struct StarkingdomsApiConfigDatabase { #[serde(default = "time_defaults")] pub max_lifetime: u64, #[serde(default = "sqlx_logging_default")] - pub sqlx_logging: bool + pub sqlx_logging: bool, } #[derive(Serialize, Deserialize, Debug)] pub struct StarkingdomsApiConfigServer { #[serde(default = "socketaddr_8080")] - pub bind: SocketAddr + pub bind: SocketAddr, } /* @@ -69,11 +69,21 @@ issuer = "https://api.e3t.cc" pub struct StarkingdomsApiConfigRealm { pub authorize_url: String, pub public_key: String, - pub issuer: String + pub issuer: String, } -fn max_connections_default() -> u32 { 100 } -fn min_connections_default() -> u32 { 5 } -fn time_defaults() -> u64 { 8 } -fn sqlx_logging_default() -> bool { true } -fn socketaddr_8080() -> SocketAddr { SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from([0, 0, 0, 0]), 8080)) } \ No newline at end of file +fn max_connections_default() -> u32 { + 100 +} +fn min_connections_default() -> u32 { + 5 +} +fn time_defaults() -> u64 { + 8 +} +fn sqlx_logging_default() -> bool { + true +} +fn socketaddr_8080() -> SocketAddr { + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from([0, 0, 0, 0]), 8080)) +} diff --git a/api/src/error.rs b/api/src/error.rs index 4659084897bfc2ad96b1ea48100492b840354dbb..953ffc003a3cafd83cc87052df77945cc4f0ee76 100644 --- a/api/src/error.rs +++ b/api/src/error.rs @@ -1,9 +1,9 @@ use actix_web::error::{JsonPayloadError, PayloadError}; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct APIErrorsResponse { - pub errors: Vec + pub errors: Vec, } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct APIError { @@ -11,10 +11,12 @@ pub struct APIError { pub message: String, #[serde(skip_serializing_if = "is_none")] #[serde(default)] - pub path: Option + pub path: Option, } -fn is_none(o: &Option) -> bool { o.is_none() } +fn is_none(o: &Option) -> bool { + o.is_none() +} impl From<&JsonPayloadError> for APIError { fn from(value: &JsonPayloadError) -> Self { @@ -71,58 +73,44 @@ impl From<&JsonPayloadError> for APIError { impl From<&PayloadError> for APIError { fn from(value: &PayloadError) -> Self { match value { - PayloadError::Incomplete(e) => { - APIError { - code: "ERR_UNEXPECTED_EOF".to_string(), - message: match e { - None => "Payload reached EOF but was incomplete".to_string(), - Some(e) => format!("Payload reached EOF but was incomplete: {}", e) - }, - path: None, - } - } - PayloadError::EncodingCorrupted => { - APIError { - code: "ERR_CORRUPTED_PAYLOAD".to_string(), - message: "Payload content encoding corrupted".to_string(), - path: None, - } - } - PayloadError::Overflow => { - APIError { - code: "ERR_PAYLOAD_OVERFLOW".to_string(), - message: "Payload reached size limit".to_string(), - path: None, - } - } - PayloadError::UnknownLength => { - APIError { - code: "ERR_PAYLOAD_UNKNOWN_LENGTH".to_string(), - message: "Unable to determine payload length".to_string(), - path: None, - } - } - PayloadError::Http2Payload(e) => { - APIError { - code: "ERR_HTTP2_ERROR".to_string(), - message: format!("HTTP/2 error: {}", e), - path: None, - } - } - PayloadError::Io(e) => { - APIError { - code: "ERR_IO_ERROR".to_string(), - message: format!("I/O error: {}", e), - path: None, - } - } - _ => { - APIError { - code: "ERR_UNKNOWN_ERROR".to_string(), - message: "An unknown error has occured".to_string(), - path: None, - } - } + PayloadError::Incomplete(e) => APIError { + code: "ERR_UNEXPECTED_EOF".to_string(), + message: match e { + None => "Payload reached EOF but was incomplete".to_string(), + Some(e) => format!("Payload reached EOF but was incomplete: {}", e), + }, + path: None, + }, + PayloadError::EncodingCorrupted => APIError { + code: "ERR_CORRUPTED_PAYLOAD".to_string(), + message: "Payload content encoding corrupted".to_string(), + path: None, + }, + PayloadError::Overflow => APIError { + code: "ERR_PAYLOAD_OVERFLOW".to_string(), + message: "Payload reached size limit".to_string(), + path: None, + }, + PayloadError::UnknownLength => APIError { + code: "ERR_PAYLOAD_UNKNOWN_LENGTH".to_string(), + message: "Unable to determine payload length".to_string(), + path: None, + }, + PayloadError::Http2Payload(e) => APIError { + code: "ERR_HTTP2_ERROR".to_string(), + message: format!("HTTP/2 error: {}", e), + path: None, + }, + PayloadError::Io(e) => APIError { + code: "ERR_IO_ERROR".to_string(), + message: format!("I/O error: {}", e), + path: None, + }, + _ => APIError { + code: "ERR_UNKNOWN_ERROR".to_string(), + message: "An unknown error has occured".to_string(), + path: None, + }, } } -} \ No newline at end of file +} diff --git a/api/src/main.rs b/api/src/main.rs index 4fc9566d7b8a1ffee83558fd3865ec334c5e543c..a8aa05548bd57033dcd3437aadd72fed74bfef76 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -1,23 +1,23 @@ -use std::error::Error; -use std::time::Duration; +use crate::config::CONFIG; +use crate::error::{APIError, APIErrorsResponse}; use actix_request_identifier::RequestIdentifier; -use actix_web::{App, HttpResponse, HttpServer}; use actix_web::http::header::HeaderValue; use actix_web::web::{Data, JsonConfig}; +use actix_web::{App, HttpResponse, HttpServer}; use log::{info, Level}; use sea_orm::{ConnectOptions, Database, DatabaseConnection}; -use tera::Tera; use starkingdoms_api_migration::{Migrator, MigratorTrait}; -use crate::config::CONFIG; -use crate::error::{APIError, APIErrorsResponse}; +use std::error::Error; +use std::time::Duration; +use tera::Tera; pub mod config; -pub mod routes; pub mod error; +pub mod routes; pub struct AppState { pub conn: DatabaseConnection, - pub templates: Tera + pub templates: Tera, } #[actix_web::main] @@ -46,7 +46,7 @@ async fn main() -> Result<(), Box> { let data = Data::new(AppState { conn: db, - templates: tera + templates: tera, }); HttpServer::new(move || { @@ -57,19 +57,23 @@ async fn main() -> Result<(), Box> { actix_web::error::InternalError::from_response( err, HttpResponse::BadRequest().json(APIErrorsResponse { - errors: vec![ - api_error - ], - }) - ).into() + errors: vec![api_error], + }), + ) + .into() + })) + .wrap(RequestIdentifier::with_generator(|| { + HeaderValue::from_str(&ulid::Ulid::new().to_string()).unwrap() })) - .wrap(RequestIdentifier::with_generator(|| HeaderValue::from_str(&ulid::Ulid::new().to_string()).unwrap())) .service(routes::select_realm::select_realm) .service(routes::callback::callback) .service(routes::beamin::beam_in) .service(routes::beamout::beam_out) .service(actix_files::Files::new("/static", "static")) - }).bind(CONFIG.server.bind)?.run().await?; + }) + .bind(CONFIG.server.bind)? + .run() + .await?; Ok(()) -} \ No newline at end of file +} diff --git a/api/src/routes/beamin.rs b/api/src/routes/beamin.rs index ba7c0aa21e10811c0af62d09e0b2a9eb2afb8e02..3ae385a566d2bbc8afac1251e3f2f42b7a9cff6d 100644 --- a/api/src/routes/beamin.rs +++ b/api/src/routes/beamin.rs @@ -1,6 +1,8 @@ -use std::collections::BTreeMap; -use actix_web::{HttpResponse, post}; +use crate::config::CONFIG; +use crate::error::{APIError, APIErrorsResponse}; +use crate::AppState; use actix_web::web::{Data, Json}; +use actix_web::{post, HttpResponse}; use hmac::digest::KeyInit; use hmac::Hmac; use jwt::VerifyWithKey; @@ -9,35 +11,31 @@ use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder}; use serde::{Deserialize, Serialize}; use sha2::Sha256; use starkingdoms_protocol::api::APISavedPlayerData; -use crate::AppState; -use crate::config::CONFIG; -use crate::error::{APIError, APIErrorsResponse}; +use std::collections::BTreeMap; #[derive(Serialize, Deserialize)] pub struct BeaminRequest { pub api_token: String, pub user_auth_realm_id: String, - pub user_auth_token: String + pub user_auth_token: String, } #[derive(Serialize, Deserialize)] pub struct BeaminResponse { pub save_id: String, - pub save: APISavedPlayerData + pub save: APISavedPlayerData, } #[post("/beamin")] pub async fn beam_in(data: Json, state: Data) -> HttpResponse { if !CONFIG.internal_tokens.contains(&data.api_token) { return HttpResponse::Unauthorized().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_BAD_TOKEN".to_string(), - message: "Missing or invalid api token".to_string(), - path: None, - } - ], - }) + errors: vec![APIError { + code: "ERR_BAD_TOKEN".to_string(), + message: "Missing or invalid api token".to_string(), + path: None, + }], + }); } let key: Hmac = Hmac::new_from_slice(CONFIG.jwt_signing_secret.as_bytes()).unwrap(); @@ -46,67 +44,64 @@ pub async fn beam_in(data: Json, state: Data) -> HttpRe Err(e) => { error!("verifying error: {}", e); return HttpResponse::Unauthorized().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_BAD_TOKEN".to_string(), - message: "Missing or invalid user token".to_string(), - path: None, - } - ], - }) + errors: vec![APIError { + code: "ERR_BAD_TOKEN".to_string(), + message: "Missing or invalid user token".to_string(), + path: None, + }], + }); } }; if !token.contains_key("user") || !token.contains_key("nonce") { return HttpResponse::Unauthorized().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_BAD_TOKEN".to_string(), - message: "Missing or invalid user token (missing scopes)".to_string(), - path: None, - } - ], + errors: vec![APIError { + code: "ERR_BAD_TOKEN".to_string(), + message: "Missing or invalid user token (missing scopes)".to_string(), + path: None, + }], }); } let user_id = token.get("user").unwrap(); - let user_savefile: Vec = match starkingdoms_api_entities::entity::user_savefile::Entity::find().filter( - starkingdoms_api_entities::entity::user_savefile::Column::User.eq(user_id)) - .order_by_desc(starkingdoms_api_entities::entity::user_savefile::Column::Timestamp).all(&state.conn).await { - Ok(sf) => sf, - Err(e) => { - error!("database error: {}", e); - return HttpResponse::InternalServerError().json(APIErrorsResponse { - errors: vec![ - APIError { + let user_savefile: Vec = + match starkingdoms_api_entities::entity::user_savefile::Entity::find() + .filter(starkingdoms_api_entities::entity::user_savefile::Column::User.eq(user_id)) + .order_by_desc(starkingdoms_api_entities::entity::user_savefile::Column::Timestamp) + .all(&state.conn) + .await + { + Ok(sf) => sf, + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![APIError { code: "ERR_DB_ERROR".to_string(), message: "Unable to fetch user savefiles".to_string(), path: None, - } - ], - }); - } - }; + }], + }); + } + }; if user_savefile.is_empty() { return HttpResponse::NoContent().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_NO_SAVES".to_string(), - message: "This user has no savefiles".to_string(), - path: None, - } - ], + errors: vec![APIError { + code: "ERR_NO_SAVES".to_string(), + message: "This user has no savefiles".to_string(), + path: None, + }], }); } let save = &user_savefile[0]; let save_id = &save.id; let save_data_str = &save.data; - let save_data: APISavedPlayerData = toml::from_str(save_data_str).expect("database contained corrupted player save data"); + let save_data: APISavedPlayerData = + toml::from_str(save_data_str).expect("database contained corrupted player save data"); HttpResponse::Ok().json(BeaminResponse { save_id: save_id.clone(), - save: save_data + save: save_data, }) -} \ No newline at end of file +} diff --git a/api/src/routes/beamout.rs b/api/src/routes/beamout.rs index 30a1019ee76bd9c003fb26e7dcddd6720e1207c7..f2866999ed4a176591429a4b7c5f7b3fcd684f80 100644 --- a/api/src/routes/beamout.rs +++ b/api/src/routes/beamout.rs @@ -1,7 +1,8 @@ -use std::collections::BTreeMap; -use std::time::{SystemTime, UNIX_EPOCH}; -use actix_web::{HttpResponse, post}; +use crate::config::CONFIG; +use crate::error::{APIError, APIErrorsResponse}; +use crate::AppState; use actix_web::web::{Data, Json}; +use actix_web::{post, HttpResponse}; use hmac::digest::KeyInit; use hmac::Hmac; use jwt::VerifyWithKey; @@ -9,18 +10,17 @@ use log::error; use sea_orm::{ActiveModelTrait, IntoActiveModel}; use serde::{Deserialize, Serialize}; use sha2::Sha256; -use ulid::Ulid; use starkingdoms_protocol::api::APISavedPlayerData; -use crate::AppState; -use crate::config::CONFIG; -use crate::error::{APIError, APIErrorsResponse}; +use std::collections::BTreeMap; +use std::time::{SystemTime, UNIX_EPOCH}; +use ulid::Ulid; #[derive(Serialize, Deserialize)] pub struct BeamoutRequest { pub api_token: String, pub user_auth_realm_id: String, pub user_auth_token: String, - pub data: APISavedPlayerData + pub data: APISavedPlayerData, } #[derive(Serialize, Deserialize)] @@ -30,14 +30,12 @@ pub struct BeamoutResponse {} pub async fn beam_out(data: Json, state: Data) -> HttpResponse { if !CONFIG.internal_tokens.contains(&data.api_token) { return HttpResponse::Unauthorized().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_BAD_TOKEN".to_string(), - message: "Missing or invalid api token".to_string(), - path: None, - } - ], - }) + errors: vec![APIError { + code: "ERR_BAD_TOKEN".to_string(), + message: "Missing or invalid api token".to_string(), + path: None, + }], + }); } let key: Hmac = Hmac::new_from_slice(CONFIG.jwt_signing_secret.as_bytes()).unwrap(); @@ -46,26 +44,22 @@ pub async fn beam_out(data: Json, state: Data) -> Http Err(e) => { error!("verifying error: {}", e); return HttpResponse::Unauthorized().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_BAD_TOKEN".to_string(), - message: "Missing or invalid user token".to_string(), - path: None, - } - ], - }) + errors: vec![APIError { + code: "ERR_BAD_TOKEN".to_string(), + message: "Missing or invalid user token".to_string(), + path: None, + }], + }); } }; if !token.contains_key("user") || !token.contains_key("nonce") { return HttpResponse::Unauthorized().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_BAD_TOKEN".to_string(), - message: "Missing or invalid user token (missing scopes)".to_string(), - path: None, - } - ], + errors: vec![APIError { + code: "ERR_BAD_TOKEN".to_string(), + message: "Missing or invalid user token (missing scopes)".to_string(), + path: None, + }], }); } @@ -74,7 +68,10 @@ pub async fn beam_out(data: Json, state: Data) -> Http id: format!("save-{}", Ulid::new().to_string()), user: token.get("user").unwrap().clone(), data: saved_data, - timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64, + timestamp: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() as i64, }; let savefile_active_model = savefile_model.into_active_model(); match savefile_active_model.insert(&state.conn).await { @@ -82,16 +79,14 @@ pub async fn beam_out(data: Json, state: Data) -> Http Err(e) => { error!("database error: {}", e); return HttpResponse::InternalServerError().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_DB_ERROR".to_string(), - message: "database failure".to_string(), - path: None, - } - ], + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "database failure".to_string(), + path: None, + }], }); } } HttpResponse::Ok().json(BeamoutResponse {}) -} \ No newline at end of file +} diff --git a/api/src/routes/callback.rs b/api/src/routes/callback.rs index 5b110e1ad3d002d7b4b0ab4fdacde69411abf4f4..2cbbf0af405b43437a2c67e0f48ea0261c3b37b6 100644 --- a/api/src/routes/callback.rs +++ b/api/src/routes/callback.rs @@ -1,7 +1,8 @@ -use std::collections::BTreeMap; -use std::time::{SystemTime, UNIX_EPOCH}; -use actix_web::{get, HttpResponse}; +use crate::config::CONFIG; +use crate::error::{APIError, APIErrorsResponse}; +use crate::AppState; use actix_web::web::{Data, Query}; +use actix_web::{get, HttpResponse}; use hmac::digest::KeyInit; use hmac::Hmac; use jwt::{PKeyWithDigest, SignWithKey, VerifyWithKey}; @@ -11,16 +12,15 @@ use openssl::pkey::PKey; use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter}; use serde::Deserialize; use sha2::Sha256; -use ulid::Ulid; use starkingdoms_api_entities::entity; -use crate::AppState; -use crate::config::CONFIG; -use crate::error::{APIError, APIErrorsResponse}; +use std::collections::BTreeMap; +use std::time::{SystemTime, UNIX_EPOCH}; +use ulid::Ulid; #[derive(Deserialize)] pub struct CallbackQueryParams { pub token: String, - pub realm: String + pub realm: String, } #[get("/callback")] @@ -33,20 +33,18 @@ pub async fn callback(query: Query, state: Data) None => { error!("realm not found"); return HttpResponse::Unauthorized().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_UNKNOWN_REALM".to_string(), - message: "Unknown realm".to_string(), - path: None, - } - ], - }) + errors: vec![APIError { + code: "ERR_UNKNOWN_REALM".to_string(), + message: "Unknown realm".to_string(), + path: None, + }], + }); } }; let rs256_pub_key = PKeyWithDigest { digest: MessageDigest::sha256(), - key: PKey::public_key_from_pem(realm.public_key.as_bytes()).unwrap() + key: PKey::public_key_from_pem(realm.public_key.as_bytes()).unwrap(), }; let token: BTreeMap = match query.token.verify_with_key(&rs256_pub_key) { @@ -59,35 +57,35 @@ pub async fn callback(query: Query, state: Data) let realm = match token.get("realm").ok_or(generic_unauthorized()) { Ok(r) => r, - Err(e) => return e + Err(e) => return e, }; let realm_local_id = match token.get("realm_native_id").ok_or(generic_unauthorized()) { Ok(r) => r, - Err(e) => return e + Err(e) => return e, }; - debug!("got authenticated realm native authorization: authenticated as {}:{}", realm, realm_local_id); + debug!( + "got authenticated realm native authorization: authenticated as {}:{}", + realm, realm_local_id + ); // see if a user on this realm already exists let maybe_user = match entity::user_auth_realm::Entity::find() - .filter( - entity::user_auth_realm::Column::RealmNativeId.eq(realm_local_id.clone()) - ) - .filter( - entity::user_auth_realm::Column::Realm.eq(realm.clone()) - ).one(&state.conn).await { + .filter(entity::user_auth_realm::Column::RealmNativeId.eq(realm_local_id.clone())) + .filter(entity::user_auth_realm::Column::Realm.eq(realm.clone())) + .one(&state.conn) + .await + { Ok(r) => r, Err(e) => { error!("database error: {}", e); return HttpResponse::InternalServerError().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_DB_ERROR".to_string(), - message: "Database error".to_string(), - path: None, - } - ], - }) + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "Database error".to_string(), + path: None, + }], + }); } }; @@ -98,17 +96,27 @@ pub async fn callback(query: Query, state: Data) claims.insert("nonce", Ulid::new().to_string()); let token_str = claims.sign_with_key(&key).unwrap(); let auth_url = format!("{}/?token={}&user={}", CONFIG.game, token_str, user.id); - return HttpResponse::Found().append_header(("Location", auth_url)).finish(); + return HttpResponse::Found() + .append_header(("Location", auth_url)) + .finish(); } // create the user let new_user = entity::user::Model { id: format!("user-{}", Ulid::new().to_string()), username: Ulid::new().to_string(), - created_on: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64, + created_on: SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() as i64, }; let new_user_realm = entity::user_auth_realm::Model { - id: format!("{}-{}:{}", new_user.id.clone(), realm.clone(), realm_local_id.clone()), + id: format!( + "{}-{}:{}", + new_user.id.clone(), + realm.clone(), + realm_local_id.clone() + ), realm: realm.clone(), realm_native_id: realm_local_id.clone(), user: new_user.id.clone(), @@ -119,7 +127,12 @@ pub async fn callback(query: Query, state: Data) claims.insert("user", new_user.id.clone()); claims.insert("nonce", Ulid::new().to_string()); let token_str = claims.sign_with_key(&key).unwrap(); - let auth_url = format!("{}/?token={}&user={}", CONFIG.game, token_str, new_user.id.clone()); + let auth_url = format!( + "{}/?token={}&user={}", + CONFIG.game, + token_str, + new_user.id.clone() + ); let new_user_active_model = new_user.into_active_model(); let new_user_realm_active_model = new_user_realm.into_active_model(); @@ -129,14 +142,12 @@ pub async fn callback(query: Query, state: Data) Err(e) => { error!("database error: {}", e); return HttpResponse::InternalServerError().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_DB_ERROR".to_string(), - message: "Database error".to_string(), - path: None, - } - ], - }) + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "Database error".to_string(), + path: None, + }], + }); } } @@ -145,28 +156,26 @@ pub async fn callback(query: Query, state: Data) Err(e) => { error!("database error: {}", e); return HttpResponse::InternalServerError().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_DB_ERROR".to_string(), - message: "Database error".to_string(), - path: None, - } - ], - }) + errors: vec![APIError { + code: "ERR_DB_ERROR".to_string(), + message: "Database error".to_string(), + path: None, + }], + }); } } - HttpResponse::Found().append_header(("Location", auth_url)).finish() + HttpResponse::Found() + .append_header(("Location", auth_url)) + .finish() } fn generic_unauthorized() -> HttpResponse { HttpResponse::Unauthorized().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_INVALID_STATE".to_string(), - message: "Unknown/invalid login state".to_string(), - path: None, - } - ], + errors: vec![APIError { + code: "ERR_INVALID_STATE".to_string(), + message: "Unknown/invalid login state".to_string(), + path: None, + }], }) -} \ No newline at end of file +} diff --git a/api/src/routes/mod.rs b/api/src/routes/mod.rs index dad8ba4f0227e26e1732f859e9be98335d7994b6..3d3536fcfd451b3fb023dd56b1efc52b7b1be877 100644 --- a/api/src/routes/mod.rs +++ b/api/src/routes/mod.rs @@ -1,4 +1,4 @@ pub mod beamin; -pub mod select_realm; +pub mod beamout; pub mod callback; -pub mod beamout; \ No newline at end of file +pub mod select_realm; diff --git a/api/src/routes/select_realm.rs b/api/src/routes/select_realm.rs index 844f992a4f265ab25cebb27f574db1c7dc6246ae..15b10e7227f182ab367300b6aea993729d6933ce 100644 --- a/api/src/routes/select_realm.rs +++ b/api/src/routes/select_realm.rs @@ -1,17 +1,17 @@ -use std::collections::HashMap; +use crate::config::{StarkingdomsApiConfigRealm, CONFIG}; +use crate::error::{APIError, APIErrorsResponse}; +use crate::AppState; +use actix_web::web::Data; use actix_web::{get, HttpResponse}; -use actix_web::web::{Data}; use log::error; -use serde::{Serialize}; +use serde::Serialize; +use std::collections::HashMap; use tera::Context; -use crate::AppState; -use crate::config::{CONFIG, StarkingdomsApiConfigRealm}; -use crate::error::{APIError, APIErrorsResponse}; #[derive(Serialize)] pub struct RealmsListTemplateContext { pub realms: HashMap, - pub back_to: String + pub back_to: String, } #[get("/select-realm")] @@ -24,14 +24,13 @@ pub async fn select_realm(state: Data) -> HttpResponse { Err(e) => { error!("[context] error creating render context: {}", e); return HttpResponse::InternalServerError().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_INTERNAL_SERVER_ERROR".to_string(), - message: "There was an error processing your request. Please try again later.".to_string(), - path: None, - } - ], - }) + errors: vec![APIError { + code: "ERR_INTERNAL_SERVER_ERROR".to_string(), + message: "There was an error processing your request. Please try again later." + .to_string(), + path: None, + }], + }); } }; match state.templates.render("select_realm.tera", &context) { @@ -39,14 +38,13 @@ pub async fn select_realm(state: Data) -> HttpResponse { Err(e) => { error!("[context] error creating render context: {}", e); HttpResponse::InternalServerError().json(APIErrorsResponse { - errors: vec![ - APIError { - code: "ERR_INTERNAL_SERVER_ERROR".to_string(), - message: "There was an error processing your request. Please try again later.".to_string(), - path: None, - } - ], + errors: vec![APIError { + code: "ERR_INTERNAL_SERVER_ERROR".to_string(), + message: "There was an error processing your request. Please try again later." + .to_string(), + path: None, + }], }) } } -} \ No newline at end of file +} diff --git a/api/starkingdoms_api_entities/src/lib.rs b/api/starkingdoms_api_entities/src/lib.rs index bccca666ad93af38f8ccf0c8540aa8bb6d00b112..e8c3d6a4c6dd76d0209422b1a8b1b5a09575d5b5 100644 --- a/api/starkingdoms_api_entities/src/lib.rs +++ b/api/starkingdoms_api_entities/src/lib.rs @@ -1 +1 @@ -pub mod entity; \ No newline at end of file +pub mod entity; diff --git a/api/starkingdoms_api_migration/src/m20230417_162824_create_table_users.rs b/api/starkingdoms_api_migration/src/m20230417_162824_create_table_users.rs index 903678e9b6a6354befd38f624ffbb074a9da55a8..b59a6c526ab2c50b1be421f105aa1b9da3bfd307 100644 --- a/api/starkingdoms_api_migration/src/m20230417_162824_create_table_users.rs +++ b/api/starkingdoms_api_migration/src/m20230417_162824_create_table_users.rs @@ -6,14 +6,21 @@ pub struct Migration; #[async_trait::async_trait] impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager.create_table( - Table::create() - .table(User::Table) - .col(ColumnDef::new(User::Id).string().primary_key().not_null()) - .col(ColumnDef::new(User::Username).string().unique_key().not_null()) - .col(ColumnDef::new(User::CreatedOn).big_unsigned().not_null()) - .to_owned() - ).await + manager + .create_table( + Table::create() + .table(User::Table) + .col(ColumnDef::new(User::Id).string().primary_key().not_null()) + .col( + ColumnDef::new(User::Username) + .string() + .unique_key() + .not_null(), + ) + .col(ColumnDef::new(User::CreatedOn).big_unsigned().not_null()) + .to_owned(), + ) + .await } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { @@ -29,5 +36,5 @@ pub enum User { Table, Id, Username, - CreatedOn + CreatedOn, } diff --git a/api/starkingdoms_api_migration/src/m20230417_164240_create_table_user_auth_realms.rs b/api/starkingdoms_api_migration/src/m20230417_164240_create_table_user_auth_realms.rs index 5867a88a6c68641563ce8a2860a129a0d468cb3f..97cc35488b66ceddf2fce555ca4427ffb210abb8 100644 --- a/api/starkingdoms_api_migration/src/m20230417_164240_create_table_user_auth_realms.rs +++ b/api/starkingdoms_api_migration/src/m20230417_164240_create_table_user_auth_realms.rs @@ -1,5 +1,5 @@ -use sea_orm_migration::prelude::*; use crate::m20230417_162824_create_table_users::User; +use sea_orm_migration::prelude::*; #[derive(DeriveMigrationName)] pub struct Migration; @@ -7,19 +7,31 @@ pub struct Migration; #[async_trait::async_trait] impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager.create_table( - Table::create() - .table(UserAuthRealm::Table) - .col(ColumnDef::new(UserAuthRealm::Id).string().not_null().primary_key()) - .col(ColumnDef::new(UserAuthRealm::Realm).string().not_null()) - .col(ColumnDef::new(UserAuthRealm::RealmNativeId).string().not_null()) - .col(ColumnDef::new(UserAuthRealm::User).string().not_null()) - .foreign_key( - ForeignKey::create() - .from(UserAuthRealm::Table, UserAuthRealm::User) - .to(User::Table, User::Id) - ).to_owned() - ).await + manager + .create_table( + Table::create() + .table(UserAuthRealm::Table) + .col( + ColumnDef::new(UserAuthRealm::Id) + .string() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(UserAuthRealm::Realm).string().not_null()) + .col( + ColumnDef::new(UserAuthRealm::RealmNativeId) + .string() + .not_null(), + ) + .col(ColumnDef::new(UserAuthRealm::User).string().not_null()) + .foreign_key( + ForeignKey::create() + .from(UserAuthRealm::Table, UserAuthRealm::User) + .to(User::Table, User::Id), + ) + .to_owned(), + ) + .await } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { @@ -36,5 +48,5 @@ pub enum UserAuthRealm { Id, Realm, RealmNativeId, - User + User, } diff --git a/api/starkingdoms_api_migration/src/m20230420_144333_create_table_user_data.rs b/api/starkingdoms_api_migration/src/m20230420_144333_create_table_user_data.rs index 2c74b50da75e4f8dfce5eead0108ff8cdee44ae8..c7e6f7bc4870f0e49f1c8d5f277237e54dadf1a1 100644 --- a/api/starkingdoms_api_migration/src/m20230420_144333_create_table_user_data.rs +++ b/api/starkingdoms_api_migration/src/m20230420_144333_create_table_user_data.rs @@ -1,5 +1,5 @@ -use sea_orm_migration::prelude::*; use crate::m20230417_164240_create_table_user_auth_realms::UserAuthRealm; +use sea_orm_migration::prelude::*; #[derive(DeriveMigrationName)] pub struct Migration; @@ -7,20 +7,32 @@ pub struct Migration; #[async_trait::async_trait] impl MigrationTrait for Migration { async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager.create_table( - Table::create() - .table(UserSavefile::Table) - .col(ColumnDef::new(UserSavefile::Id).string().not_null().primary_key()) - .col(ColumnDef::new(UserSavefile::User).string().not_null()) - .col(ColumnDef::new(UserSavefile::Data).string().not_null()) - .col(ColumnDef::new(UserSavefile::Timestamp).big_unsigned().not_null().unique_key()) - .foreign_key( - ForeignKey::create() - .from(UserSavefile::Table, UserSavefile::User) - .to(UserAuthRealm::Table, UserAuthRealm::Id) - ) - .to_owned() - ).await + manager + .create_table( + Table::create() + .table(UserSavefile::Table) + .col( + ColumnDef::new(UserSavefile::Id) + .string() + .not_null() + .primary_key(), + ) + .col(ColumnDef::new(UserSavefile::User).string().not_null()) + .col(ColumnDef::new(UserSavefile::Data).string().not_null()) + .col( + ColumnDef::new(UserSavefile::Timestamp) + .big_unsigned() + .not_null() + .unique_key(), + ) + .foreign_key( + ForeignKey::create() + .from(UserSavefile::Table, UserSavefile::User) + .to(UserAuthRealm::Table, UserAuthRealm::Id), + ) + .to_owned(), + ) + .await } async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { @@ -37,5 +49,5 @@ pub enum UserSavefile { Id, User, Data, - Timestamp + Timestamp, } diff --git a/protocol/src/api.rs b/protocol/src/api.rs index a79472e4349bdfe8aba0273fb4c0336b17fb935e..599a31817281ee52352c355d6b6ac1039895b92e 100644 --- a/protocol/src/api.rs +++ b/protocol/src/api.rs @@ -2,6 +2,4 @@ use serde::{Deserialize, Serialize}; // ALL FIELDS **MUST** BE WRAPPED IN Option<> #[derive(Serialize, Deserialize, Default, Clone, Debug)] -pub struct APISavedPlayerData { - -} \ No newline at end of file +pub struct APISavedPlayerData {} diff --git a/protocol/src/legacy.rs b/protocol/src/legacy.rs index dc181b3cfe29eafdc982c425e9b8e66fff28251a..117c5ff1ce6a2b309e680c9e2203f97bff5ea103 100644 --- a/protocol/src/legacy.rs +++ b/protocol/src/legacy.rs @@ -5,7 +5,7 @@ pub const PROTOCOL_VERSION: u32 = 1; #[derive(Serialize, Deserialize, Debug, Clone, Copy)] pub enum State { Handshake, - Play + Play, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -13,15 +13,15 @@ pub enum MessageC2S { Hello { version: u32, requested_username: String, - next_state: State + next_state: State, }, Goodbye { - reason: GoodbyeReason + reason: GoodbyeReason, }, Chat { - message: String + message: String, }, Ping {}, @@ -32,28 +32,27 @@ pub enum MessageS2C { Hello { version: u32, given_username: String, - next_state: State + next_state: State, }, Goodbye { - reason: GoodbyeReason + reason: GoodbyeReason, }, Chat { from: String, - message: String + message: String, }, Pong {}, PlayersUpdate { - players: Vec + players: Vec, }, - PlanetData { - planets: Vec - } + planets: Vec, + }, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -61,7 +60,7 @@ pub struct ProtocolPlayer { pub rotation: f64, pub x: f64, pub y: f64, - pub username: String + pub username: String, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -79,18 +78,18 @@ pub struct ProtocolPlanet { pub planet_type: PlanetType, pub x: f64, pub y: f64, - pub radius: f64 + pub radius: f64, } #[derive(Serialize, Deserialize, Debug, Clone)] pub enum PlanetType { - Earth + Earth, } impl PlanetType { pub fn as_texture_id(&self) -> String { match self { - PlanetType::Earth => "earth".to_string() + PlanetType::Earth => "earth".to_string(), } } } diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 44579527cdabaa19376af97b4c50ddbda5f4d5c9..d89f614570dc57046e0dc76182514c001e7817a7 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -1,9 +1,15 @@ -use std::error::Error; -use protobuf::{Enum, Message}; -use crate::message_c2s::{MessageC2SAuthenticateAndBeamOut, MessageC2SChat, MessageC2SGoodbye, MessageC2SHello, MessageC2SInput, MessageC2SMouseInput, MessageC2SPing}; -use crate::message_s2c::{MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong, MessageS2CModulesUpdate}; +use crate::message_c2s::{ + MessageC2SAuthenticateAndBeamOut, MessageC2SChat, MessageC2SGoodbye, MessageC2SHello, + MessageC2SInput, MessageC2SMouseInput, MessageC2SPing, +}; +use crate::message_s2c::{ + MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CModulesUpdate, + MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong, +}; use crate::planet::PlanetType; use crate::starkingdoms_protocol::PacketWrapper; +use protobuf::{Enum, Message}; +use std::error::Error; include!(concat!(env!("OUT_DIR"), "/protos/mod.rs")); pub const PROTOCOL_VERSION: u32 = 4; @@ -18,7 +24,7 @@ pub enum MessageC2S { Ping(MessageC2SPing), Input(MessageC2SInput), AuthenticateAndBeamOut(MessageC2SAuthenticateAndBeamOut), - MouseInput(MessageC2SMouseInput) + MouseInput(MessageC2SMouseInput), } #[derive(Debug)] @@ -41,26 +47,35 @@ impl TryFrom<&[u8]> for MessageC2S { let deser_pkt = match pkt.packet_id { _id if _id == message_c2s::message_c2shello::Packet_info::type_.value() as i64 => { MessageC2S::Hello(MessageC2SHello::parse_from_bytes(&pkt.packet_data)?) - }, + } _id if _id == message_c2s::message_c2sgoodbye::Packet_info::type_.value() as i64 => { MessageC2S::Goodbye(MessageC2SGoodbye::parse_from_bytes(&pkt.packet_data)?) - }, + } _id if _id == message_c2s::message_c2schat::Packet_info::type_.value() as i64 => { MessageC2S::Chat(MessageC2SChat::parse_from_bytes(&pkt.packet_data)?) - }, + } _id if _id == message_c2s::message_c2sping::Packet_info::type_.value() as i64 => { MessageC2S::Ping(MessageC2SPing::parse_from_bytes(&pkt.packet_data)?) - }, + } _id if _id == message_c2s::message_c2sinput::Packet_info::type_.value() as i64 => { MessageC2S::Input(MessageC2SInput::parse_from_bytes(&pkt.packet_data)?) - }, - _id if _id == message_c2s::message_c2sauthenticate_and_beam_out::Packet_info::type_.value() as i64 => { - MessageC2S::AuthenticateAndBeamOut(MessageC2SAuthenticateAndBeamOut::parse_from_bytes(&pkt.packet_data)?) - }, - _id if _id == message_c2s::message_c2smouse_input::Packet_info::type_.value() as i64 => { + } + _id if _id + == message_c2s::message_c2sauthenticate_and_beam_out::Packet_info::type_.value() + as i64 => + { + MessageC2S::AuthenticateAndBeamOut( + MessageC2SAuthenticateAndBeamOut::parse_from_bytes(&pkt.packet_data)?, + ) + } + _id if _id + == message_c2s::message_c2smouse_input::Packet_info::type_.value() as i64 => + { MessageC2S::MouseInput(MessageC2SMouseInput::parse_from_bytes(&pkt.packet_data)?) } - _id => { return Err(format!("Unrecognized C2S packet {}", _id).into()); } + _id => { + return Err(format!("Unrecognized C2S packet {}", _id).into()); + } }; Ok(deser_pkt) @@ -72,27 +87,34 @@ impl TryInto> for MessageC2S { fn try_into(self) -> Result, Self::Error> { let (pkt_id, pkt_bytes) = match self { - MessageC2S::Hello(p) => { - (message_c2s::message_c2shello::Packet_info::type_.value(), p.write_to_bytes()?) - } - MessageC2S::Goodbye(p) => { - (message_c2s::message_c2sgoodbye::Packet_info::type_.value(), p.write_to_bytes()?) - } - MessageC2S::Chat(p) => { - (message_c2s::message_c2schat::Packet_info::type_.value(), p.write_to_bytes()?) - } - MessageC2S::Ping(p) => { - (message_c2s::message_c2sping::Packet_info::type_.value(), p.write_to_bytes()?) - } - MessageC2S::Input(p) => { - (message_c2s::message_c2sping::Packet_info::type_.value(), p.write_to_bytes()?) - }, - MessageC2S::AuthenticateAndBeamOut(p) => { - (message_c2s::message_c2sauthenticate_and_beam_out::Packet_info::type_.value(), p.write_to_bytes()?) - }, - MessageC2S::MouseInput(p) => { - (message_c2s::message_c2smouse_input::Packet_info::type_.value(), p.write_to_bytes()?) - } + MessageC2S::Hello(p) => ( + message_c2s::message_c2shello::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageC2S::Goodbye(p) => ( + message_c2s::message_c2sgoodbye::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageC2S::Chat(p) => ( + message_c2s::message_c2schat::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageC2S::Ping(p) => ( + message_c2s::message_c2sping::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageC2S::Input(p) => ( + message_c2s::message_c2sping::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageC2S::AuthenticateAndBeamOut(p) => ( + message_c2s::message_c2sauthenticate_and_beam_out::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageC2S::MouseInput(p) => ( + message_c2s::message_c2smouse_input::Packet_info::type_.value(), + p.write_to_bytes()?, + ), }; let pkt = PacketWrapper { @@ -114,26 +136,38 @@ impl TryFrom<&[u8]> for MessageS2C { let deser_pkt = match pkt.packet_id { _id if _id == message_s2c::message_s2chello::Packet_info::type_.value() as i64 => { MessageS2C::Hello(MessageS2CHello::parse_from_bytes(&pkt.packet_data)?) - }, + } _id if _id == message_s2c::message_s2cgoodbye::Packet_info::type_.value() as i64 => { MessageS2C::Goodbye(MessageS2CGoodbye::parse_from_bytes(&pkt.packet_data)?) - }, + } _id if _id == message_s2c::message_s2cchat::Packet_info::type_.value() as i64 => { MessageS2C::Chat(MessageS2CChat::parse_from_bytes(&pkt.packet_data)?) - }, + } _id if _id == message_s2c::message_s2cpong::Packet_info::type_.value() as i64 => { MessageS2C::Pong(MessageS2CPong::parse_from_bytes(&pkt.packet_data)?) - }, - _id if _id == message_s2c::message_s2cplayers_update::Packet_info::type_.value() as i64 => { - MessageS2C::PlayersUpdate(MessageS2CPlayersUpdate::parse_from_bytes(&pkt.packet_data)?) - }, - _id if _id == message_s2c::message_s2cplanet_data::Packet_info::type_.value() as i64 => { + } + _id if _id + == message_s2c::message_s2cplayers_update::Packet_info::type_.value() as i64 => + { + MessageS2C::PlayersUpdate(MessageS2CPlayersUpdate::parse_from_bytes( + &pkt.packet_data, + )?) + } + _id if _id + == message_s2c::message_s2cplanet_data::Packet_info::type_.value() as i64 => + { MessageS2C::PlanetData(MessageS2CPlanetData::parse_from_bytes(&pkt.packet_data)?) - }, - _id if _id == message_s2c::message_s2cmodules_update::Packet_info::type_.value() as i64 => { - MessageS2C::ModulesUpdate(MessageS2CModulesUpdate::parse_from_bytes(&pkt.packet_data)?) - }, - _ => { return Err("Not a S2C packet".into()); } + } + _id if _id + == message_s2c::message_s2cmodules_update::Packet_info::type_.value() as i64 => + { + MessageS2C::ModulesUpdate(MessageS2CModulesUpdate::parse_from_bytes( + &pkt.packet_data, + )?) + } + _ => { + return Err("Not a S2C packet".into()); + } }; Ok(deser_pkt) @@ -145,27 +179,34 @@ impl TryInto> for MessageS2C { fn try_into(self) -> Result, Self::Error> { let (pkt_id, pkt_bytes) = match self { - MessageS2C::Hello(p) => { - (message_s2c::message_s2chello::Packet_info::type_.value(), p.write_to_bytes()?) - } - MessageS2C::Goodbye(p) => { - (message_s2c::message_s2cgoodbye::Packet_info::type_.value(), p.write_to_bytes()?) - } - MessageS2C::Chat(p) => { - (message_s2c::message_s2cchat::Packet_info::type_.value(), p.write_to_bytes()?) - } - MessageS2C::Pong(p) => { - (message_s2c::message_s2cpong::Packet_info::type_.value(), p.write_to_bytes()?) - } - MessageS2C::PlayersUpdate(p) => { - (message_s2c::message_s2cplayers_update::Packet_info::type_.value(), p.write_to_bytes()?) - } - MessageS2C::PlanetData(p) => { - (message_s2c::message_s2cplanet_data::Packet_info::type_.value(), p.write_to_bytes()?) - } - MessageS2C::ModulesUpdate(p) => { - (message_s2c::message_s2cmodules_update::Packet_info::type_.value(), p.write_to_bytes()?) - } + MessageS2C::Hello(p) => ( + message_s2c::message_s2chello::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageS2C::Goodbye(p) => ( + message_s2c::message_s2cgoodbye::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageS2C::Chat(p) => ( + message_s2c::message_s2cchat::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageS2C::Pong(p) => ( + message_s2c::message_s2cpong::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageS2C::PlayersUpdate(p) => ( + message_s2c::message_s2cplayers_update::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageS2C::PlanetData(p) => ( + message_s2c::message_s2cplanet_data::Packet_info::type_.value(), + p.write_to_bytes()?, + ), + MessageS2C::ModulesUpdate(p) => ( + message_s2c::message_s2cmodules_update::Packet_info::type_.value(), + p.write_to_bytes()?, + ), }; let pkt = PacketWrapper { @@ -183,7 +224,7 @@ impl planet::PlanetType { match self { PlanetType::Earth => "earth".to_string(), PlanetType::Moon => "moon".to_string(), - PlanetType::UNKNOWN => "missing".to_string() + PlanetType::UNKNOWN => "missing".to_string(), } } } diff --git a/server/build.rs b/server/build.rs index 2b8b935e7693bbfc010038646f28aa6aa28f99c3..0335e8952d8a061efcade4f4691cb01fd0497548 100644 --- a/server/build.rs +++ b/server/build.rs @@ -1,5 +1,5 @@ -use std::process::Command; use cargo_metadata::MetadataCommand; +use std::process::Command; fn main() { let path = std::env::var("CARGO_MANIFEST_DIR").unwrap(); @@ -14,16 +14,29 @@ fn main() { let version = root.version.to_string(); let version_name = root.metadata["version-name"].to_string().replace('"', ""); - let description = root.metadata["slp-description"].to_string().replace('"', ""); + let description = root.metadata["slp-description"] + .to_string() + .replace('"', ""); - let output = Command::new("git").args(["rev-parse", "--short", "HEAD"]).output().unwrap(); + let output = Command::new("git") + .args(["rev-parse", "--short", "HEAD"]) + .output() + .unwrap(); let git_hash = String::from_utf8(output.stdout).unwrap(); println!("cargo:rustc-env=STK_VERSION={}", version); println!("cargo:rustc-env=STK_VERSION_NAME={}", version_name); println!("cargo:rustc-env=STK_SLP_DESCRIPTION={}", description); - println!("cargo:rustc-env=STK_CHANNEL={}", std::env::var("STK_CHANNEL").unwrap_or("dev".to_string())); - println!("cargo:rustc-env=STK_BUILD={}-{}-{}", std::env::var("STK_CHANNEL").unwrap_or("dev".to_string()), std::env::var("STK_BUILD_NUM").unwrap_or("local".to_string()), git_hash); + println!( + "cargo:rustc-env=STK_CHANNEL={}", + std::env::var("STK_CHANNEL").unwrap_or("dev".to_string()) + ); + println!( + "cargo:rustc-env=STK_BUILD={}-{}-{}", + std::env::var("STK_CHANNEL").unwrap_or("dev".to_string()), + std::env::var("STK_BUILD_NUM").unwrap_or("local".to_string()), + git_hash + ); println!("cargo:rerun-if-changed=Cargo.toml"); println!("cargo:rerun-if-env-changed=STK_BUILD_NUM"); -} \ No newline at end of file +} diff --git a/server/src/api.rs b/server/src/api.rs index 7c649ab68c9f3df65b71d24270f63085397f10a8..e1734b42e7112a41c325b02a41e4f4aea02d9b9a 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -1,40 +1,53 @@ -use std::error::Error; use log::error; use reqwest::StatusCode; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use starkingdoms_protocol::api::APISavedPlayerData; +use std::error::Error; #[derive(Serialize, Deserialize)] pub struct BeaminRequest { pub api_token: String, pub user_auth_realm_id: String, - pub user_auth_token: String + pub user_auth_token: String, } #[derive(Serialize, Deserialize)] pub struct BeaminResponse { pub save_id: String, - pub save: APISavedPlayerData + pub save: APISavedPlayerData, } -pub async fn load_player_data_from_api(token: &str, user_id: &str, internal_token: &str) -> Result> { +pub async fn load_player_data_from_api( + token: &str, + user_id: &str, + internal_token: &str, +) -> Result> { let client = reqwest::Client::new(); let req_body = BeaminRequest { api_token: internal_token.to_owned(), user_auth_realm_id: user_id.to_owned(), - user_auth_token: token.to_owned() + user_auth_token: token.to_owned(), }; - let res = client.post(format!("{}/beamin", std::env::var("STK_API_URL").unwrap())).header("Content-Type", "application/json").body(serde_json::to_string(&req_body)?).send().await?; + let res = client + .post(format!("{}/beamin", std::env::var("STK_API_URL").unwrap())) + .header("Content-Type", "application/json") + .body(serde_json::to_string(&req_body)?) + .send() + .await?; if res.status() == StatusCode::NO_CONTENT { - return Ok(APISavedPlayerData::default()) + return Ok(APISavedPlayerData::default()); } if res.status() != StatusCode::OK { - error!("error with API call (status: {}, body: {})", res.status(), res.text().await?); - return Err("Error with API call".into()) + error!( + "error with API call (status: {}, body: {})", + res.status(), + res.text().await? + ); + return Err("Error with API call".into()); } let resp: BeaminResponse = serde_json::from_str(&res.text().await?)?; @@ -47,25 +60,39 @@ pub struct BeamoutRequest { pub api_token: String, pub user_auth_realm_id: String, pub user_auth_token: String, - pub data: APISavedPlayerData + pub data: APISavedPlayerData, } -pub async fn save_player_data_to_api(data: &APISavedPlayerData, token: &str, user_id: &str, internal_token: &str) -> Result<(), Box> { +pub async fn save_player_data_to_api( + data: &APISavedPlayerData, + token: &str, + user_id: &str, + internal_token: &str, +) -> Result<(), Box> { let client = reqwest::Client::new(); let req_body = BeamoutRequest { api_token: internal_token.to_owned(), user_auth_realm_id: user_id.to_owned(), user_auth_token: token.to_owned(), - data: data.to_owned() + data: data.to_owned(), }; - let res = client.post(format!("{}/beamout", std::env::var("STK_API_URL").unwrap())).header("Content-Type", "application/json").body(serde_json::to_string(&req_body)?).send().await?; + let res = client + .post(format!("{}/beamout", std::env::var("STK_API_URL").unwrap())) + .header("Content-Type", "application/json") + .body(serde_json::to_string(&req_body)?) + .send() + .await?; if res.status() != StatusCode::OK { - error!("error with API call (status: {}, body: {})", res.status(), res.text().await?); - return Err("Error with API call".into()) + error!( + "error with API call (status: {}, body: {})", + res.status(), + res.text().await? + ); + return Err("Error with API call".into()); } Ok(()) -} \ No newline at end of file +} diff --git a/server/src/entity.rs b/server/src/entity.rs index 95c09f0068db1681d1267c2272992d4b94d76d04..a46eecd01934eca25a1ff418f0191d4c449d8576 100644 --- a/server/src/entity.rs +++ b/server/src/entity.rs @@ -1,9 +1,13 @@ -use std::{sync::atomic::AtomicU32, collections::HashMap, net::SocketAddr}; +use std::{collections::HashMap, net::SocketAddr, sync::atomic::AtomicU32}; use nalgebra::Vector2; use starkingdoms_protocol::planet::PlanetType; -use crate::{planet::Planet, SCALE, manager::{ClientHandlerMessage, Player, Module, AttachedModule}}; +use crate::{ + manager::{AttachedModule, ClientHandlerMessage, Module, Player}, + planet::Planet, + SCALE, +}; pub type EntityId = u32; pub type Entities = HashMap; @@ -11,7 +15,9 @@ static mut ENTITY_ID_COUNT: AtomicU32 = AtomicU32::new(0); pub fn get_entity_id() -> EntityId { let last_entity_id = unsafe { &ENTITY_ID_COUNT }; let id = last_entity_id.fetch_add(1, std::sync::atomic::Ordering::AcqRel); - if id > 4_147_483_600 { panic!("No remaining entity ids") }; + if id > 4_147_483_600 { + panic!("No remaining entity ids") + }; id } @@ -23,7 +29,7 @@ pub struct EntityHandler { impl EntityHandler { pub fn new() -> EntityHandler { EntityHandler { - entities: Entities::new() + entities: Entities::new(), } } pub fn get_planets(&self) -> Vec { @@ -160,9 +166,7 @@ impl EntityHandler { }); } - ClientHandlerMessage::PlanetData { - planets - } + ClientHandlerMessage::PlanetData { planets } } } diff --git a/server/src/handler.rs b/server/src/handler.rs index a9ede0e1a968b799454c7fe5c2ab4cf8d6d79f11..75244fdae9eabe5f3a68cafdfe499de989e46c83 100644 --- a/server/src/handler.rs +++ b/server/src/handler.rs @@ -1,32 +1,44 @@ +use crate::api::{load_player_data_from_api, save_player_data_to_api}; +use crate::entity::{get_entity_id, Entity, EntityHandler}; +use crate::manager::{ + AttachedModule, ClientHandlerMessage, ClientManager, ModuleTemplate, PhysicsData, Player, +}; +use crate::{recv, send, SCALE}; +use async_std::net::TcpStream; +use async_std::{channel::Receiver, sync::RwLock}; +use async_tungstenite::WebSocketStream; +use futures::stream::{SplitSink, SplitStream}; +use futures::{FutureExt, SinkExt, StreamExt}; +use log::{debug, error, info, warn}; +use nalgebra::{point, vector}; +use rand::Rng; +use rapier2d_f64::prelude::{ + Collider, ColliderBuilder, MassProperties, RigidBodyBuilder, RigidBodyType, +}; +use starkingdoms_protocol::goodbye_reason::GoodbyeReason; +use starkingdoms_protocol::message_s2c::{ + MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CModulesUpdate, + MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong, +}; +use starkingdoms_protocol::module::ModuleType; +use starkingdoms_protocol::state::State; +use starkingdoms_protocol::{MessageC2S, MessageS2C, PROTOCOL_VERSION}; use std::error::Error; use std::f64::consts::PI; use std::net::SocketAddr; use std::sync::Arc; use std::time::{Duration, SystemTime}; -use futures::stream::{SplitSink, SplitStream}; -use futures::{FutureExt, SinkExt, StreamExt}; -use log::{error, info, debug, warn}; -use nalgebra::{vector, point}; -use rand::Rng; -use rapier2d_f64::prelude::{RigidBodyBuilder, RigidBodyType, ColliderBuilder, MassProperties, Collider}; -use starkingdoms_protocol::module::ModuleType; use tungstenite::Message; -use starkingdoms_protocol::goodbye_reason::GoodbyeReason; -use starkingdoms_protocol::message_s2c::{MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong, MessageS2CModulesUpdate}; -use starkingdoms_protocol::{MessageS2C, MessageC2S, PROTOCOL_VERSION}; -use starkingdoms_protocol::state::State; -use crate::entity::{EntityHandler, get_entity_id, Entity}; -use crate::manager::{ClientHandlerMessage, ClientManager, PhysicsData, Player, AttachedModule, ModuleTemplate}; -use crate::{send, recv, SCALE}; -use async_std::{sync::RwLock, channel::Receiver}; -use async_std::net::TcpStream; -use async_tungstenite::WebSocketStream; -use crate::api::{load_player_data_from_api, save_player_data_to_api}; -pub async fn handle_client(mgr: ClientManager, entities: Arc>, data: Arc>, - remote_addr: SocketAddr, rx: Receiver, - mut client_tx: SplitSink, Message>, mut client_rx: SplitStream> - ) -> Result<(), Box> { +pub async fn handle_client( + mgr: ClientManager, + entities: Arc>, + data: Arc>, + remote_addr: SocketAddr, + rx: Receiver, + mut client_tx: SplitSink, Message>, + mut client_rx: SplitStream>, +) -> Result<(), Box> { let mut state = State::Handshake; let mut username = String::new(); let mut ping_timeout = SystemTime::now() + Duration::from_secs(10); @@ -41,7 +53,8 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc { info!("client sent hello"); if !matches!(pkt.next_state.unwrap(), State::Play) { - error!("client sent unexpected state {:?} (expected: Play)", pkt.next_state); + error!( + "client sent unexpected state {:?} (expected: Play)", + pkt.next_state + ); let msg = MessageS2C::Goodbye(MessageS2CGoodbye { reason: GoodbyeReason::UnexpectedNextState.into(), special_fields: Default::default(), - }).try_into()?; + }) + .try_into()?; send!(client_tx, msg).await?; break; } // check version if pkt.version != PROTOCOL_VERSION { - error!("client sent incompatible version {} (expected: {})", pkt.version, PROTOCOL_VERSION); + error!( + "client sent incompatible version {} (expected: {})", + pkt.version, PROTOCOL_VERSION + ); let msg = MessageS2C::Goodbye(MessageS2CGoodbye { reason: GoodbyeReason::UnsupportedProtocol.into(), special_fields: Default::default(), - }).try_into()?; + }) + .try_into()?; send!(client_tx, msg).await?; break; } // determine if we can give them that username { - if mgr.usernames.read().await.values().any(|u| *u == pkt.requested_username) { - error!("client requested username {} but it is in use", pkt.requested_username); + if mgr + .usernames + .read() + .await + .values() + .any(|u| *u == pkt.requested_username) + { + error!( + "client requested username {} but it is in use", + pkt.requested_username + ); let msg: Vec = MessageS2C::Goodbye(MessageS2CGoodbye { reason: GoodbyeReason::UsernameTaken.into(), special_fields: Default::default(), - }).try_into()?; + }) + .try_into()?; send!(client_tx, msg).await?; break; } @@ -131,7 +166,10 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc() * PI * 2. }; let player_body = RigidBodyBuilder::new(RigidBodyType::Dynamic) - .translation(vector![angle.cos() * 2050. / SCALE, angle.sin() * 2050.0/SCALE]) + .translation(vector![ + angle.cos() * 2050. / SCALE, + angle.sin() * 2050.0 / SCALE + ]) .rotation(angle + PI / 2.) .build(); - let player_collider: Collider = ColliderBuilder::cuboid(25.0 / SCALE, 25.0 / SCALE) - .mass_properties(MassProperties::new(point![0.0, 0.0], 120.0, 122500.0)) - .build(); + let player_collider: Collider = + ColliderBuilder::cuboid(25.0 / SCALE, 25.0 / SCALE) + .mass_properties(MassProperties::new( + point![0.0, 0.0], + 120.0, + 122500.0, + )) + .build(); let player_handle = rigid_body_set.insert(player_body); - collider_set.insert_with_parent(player_collider, player_handle, &mut rigid_body_set); - + collider_set.insert_with_parent( + player_collider, + player_handle, + &mut rigid_body_set, + ); let mut player = Player { handle: player_handle, @@ -183,128 +233,191 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc d, Err(e) => { - warn!("[{}] * Beamin: ABORTED. API returned error: {}", remote_addr, e); - e_write_handle.entities.insert(get_entity_id(), Entity::Player(player)); + warn!( + "[{}] * Beamin: ABORTED. API returned error: {}", + remote_addr, e + ); + e_write_handle + .entities + .insert(get_entity_id(), Entity::Player(player)); continue; } }; - info!("[{}] Beamin: loaded player data! {:?}", remote_addr, player_data); + info!( + "[{}] Beamin: loaded player data! {:?}", + remote_addr, player_data + ); player.load_api_data(&player_data); } let player_id = get_entity_id(); - e_write_handle.entities.insert(player_id, Entity::Player(player)); - AttachedModule::attach_new(&mut data_handle, &mut e_write_handle, - player_id, player_id, - ModuleTemplate { - translation: vector![0.0, 50.0], - heading: 0.0, - mass_properties: MassProperties::new(point![0.0, 0.0], 120.0, 122500.0), - module_type: ModuleType::Cargo, - }, 0, angle); + e_write_handle + .entities + .insert(player_id, Entity::Player(player)); + AttachedModule::attach_new( + &mut data_handle, + &mut e_write_handle, + player_id, + player_id, + ModuleTemplate { + translation: vector![0.0, 50.0], + heading: 0.0, + mass_properties: MassProperties::new( + point![0.0, 0.0], + 120.0, + 122500.0, + ), + module_type: ModuleType::Cargo, + }, + 0, + angle, + ); data_handle.rigid_body_set = rigid_body_set; data_handle.collider_set = collider_set; debug!("running"); } - }, + } MessageC2S::Goodbye(pkt) => { info!("client sent goodbye: {:?}", pkt.reason); break; - }, + } _ => { - error!("client sent unexpected packet {:?} for state {:?}", pkt, state); + error!( + "client sent unexpected packet {:?} for state {:?}", + pkt, state + ); let msg = MessageS2C::Goodbye(MessageS2CGoodbye { reason: GoodbyeReason::UnexpectedPacket.into(), special_fields: Default::default(), - }).try_into()?; + }) + .try_into()?; send!(client_tx, msg).await?; break; } } } - State::Play => { - match pkt { - MessageC2S::Hello { .. } => { - error!("client sent unexpected packet {:?} for state {:?}", pkt, state); - let msg = MessageS2C::Goodbye(MessageS2CGoodbye { - reason: GoodbyeReason::UnexpectedPacket.into(), - special_fields: Default::default(), - }).try_into()?; - send!(client_tx, msg).await?; - break; - }, - MessageC2S::Goodbye(pkt) => { - info!("client sent goodbye: {:?}", pkt.reason); - break; - }, - MessageC2S::Chat(pkt) => { - info!("[{}] CHAT: [{}] {}", remote_addr, username, pkt.message); + State::Play => match pkt { + MessageC2S::Hello { .. } => { + error!( + "client sent unexpected packet {:?} for state {:?}", + pkt, state + ); + let msg = MessageS2C::Goodbye(MessageS2CGoodbye { + reason: GoodbyeReason::UnexpectedPacket.into(), + special_fields: Default::default(), + }) + .try_into()?; + send!(client_tx, msg).await?; + break; + } + MessageC2S::Goodbye(pkt) => { + info!("client sent goodbye: {:?}", pkt.reason); + break; + } + MessageC2S::Chat(pkt) => { + info!("[{}] CHAT: [{}] {}", remote_addr, username, pkt.message); - for (_addr, client_thread) in mgr.handlers.read().await.iter() { - match client_thread.tx.send(ClientHandlerMessage::ChatMessage { from: username.clone(), message: pkt.message.clone() }).await { - Ok(_) => (), - Err(e) => { - error!("unable to update a client thread: {}", e); - } + for (_addr, client_thread) in mgr.handlers.read().await.iter() { + match client_thread + .tx + .send(ClientHandlerMessage::ChatMessage { + from: username.clone(), + message: pkt.message.clone(), + }) + .await + { + Ok(_) => (), + Err(e) => { + error!("unable to update a client thread: {}", e); } } - }, - MessageC2S::Ping(_) => { - let msg = MessageS2C::Pong(MessageS2CPong { - special_fields: Default::default(), - }).try_into()?; - send!(client_tx, msg).await?; - ping_timeout = SystemTime::now() + Duration::from_secs(10); - }, - MessageC2S::Input(p) => { - let mut handle = entities.write().await; - let id = handle.get_player_id(remote_addr) - .expect("could not get player id"); - if let Entity::Player(ref mut me) = handle.entities.get_mut(&id) - .expect("player disconnected but continued to send packets") { - me.input.up = p.up_pressed; - me.input.down = p.down_pressed; - me.input.left = p.left_pressed; - me.input.right = p.right_pressed; - } - }, - MessageC2S::AuthenticateAndBeamOut(p) => { - info!("[{}] * Beaming out {} as {} with realm token {}", remote_addr, username, p.user_id, p.token); + } + } + MessageC2S::Ping(_) => { + let msg = MessageS2C::Pong(MessageS2CPong { + special_fields: Default::default(), + }) + .try_into()?; + send!(client_tx, msg).await?; + ping_timeout = SystemTime::now() + Duration::from_secs(10); + } + MessageC2S::Input(p) => { + let mut handle = entities.write().await; + let id = handle + .get_player_id(remote_addr) + .expect("could not get player id"); + if let Entity::Player(ref mut me) = handle + .entities + .get_mut(&id) + .expect("player disconnected but continued to send packets") + { + me.input.up = p.up_pressed; + me.input.down = p.down_pressed; + me.input.left = p.left_pressed; + me.input.right = p.right_pressed; + } + } + MessageC2S::AuthenticateAndBeamOut(p) => { + info!( + "[{}] * Beaming out {} as {} with realm token {}", + remote_addr, username, p.user_id, p.token + ); - let player = entities.read().await.get_player(remote_addr).expect("Player sending messages after disconnect"); + let player = entities + .read() + .await + .get_player(remote_addr) + .expect("Player sending messages after disconnect"); - if Some(p.token) != player.auth_token || Some(p.user_id) != player.auth_user { - warn!("[{}] invalid beamout packet, ignoring", remote_addr); - continue; - } + if Some(p.token) != player.auth_token || Some(p.user_id) != player.auth_user + { + warn!("[{}] invalid beamout packet, ignoring", remote_addr); + continue; + } - match save_player_data_to_api(&player.as_api_data(), &player.auth_token.unwrap(), &player.auth_user.unwrap(), &std::env::var("STK_API_KEY").unwrap()).await { - Ok(_) => { - info!("[{}] * Beamed out successfully", remote_addr); - let msg = MessageS2C::Goodbye(MessageS2CGoodbye { - reason: GoodbyeReason::Done.into(), - special_fields: Default::default(), - }).try_into()?; - send!(client_tx, msg).await?; - break; - } - Err(e) => { - error!("[{}] error beaming out: {}", remote_addr, e); - } + match save_player_data_to_api( + &player.as_api_data(), + &player.auth_token.unwrap(), + &player.auth_user.unwrap(), + &std::env::var("STK_API_KEY").unwrap(), + ) + .await + { + Ok(_) => { + info!("[{}] * Beamed out successfully", remote_addr); + let msg = MessageS2C::Goodbye(MessageS2CGoodbye { + reason: GoodbyeReason::Done.into(), + special_fields: Default::default(), + }) + .try_into()?; + send!(client_tx, msg).await?; + break; + } + Err(e) => { + error!("[{}] error beaming out: {}", remote_addr, e); } - }, - MessageC2S::MouseInput(p) => { - debug!("[{}] player input: {:?}", remote_addr, p); } } - } + MessageC2S::MouseInput(p) => { + debug!("[{}] player input: {:?}", remote_addr, p); + } + }, } } } diff --git a/server/src/macros.rs b/server/src/macros.rs index 56a3e2dccd8f87a095fe6236b9d76b5c488deea7..d7d402a89a78cace116c77e2c11bab7e17ddde8c 100644 --- a/server/src/macros.rs +++ b/server/src/macros.rs @@ -13,49 +13,13 @@ pub fn _generic_pkt_into(p: Vec) -> Message { #[macro_export] macro_rules! recv { - ($reader:expr) => { - { - if let Some(future_result) = $reader.next().now_or_never() { - if let Some(msg) = future_result { - match msg { - Ok(msg) => { - if msg.is_binary() { - match MessageC2S::try_from(msg.into_data().as_slice()) { - Ok(d) => Ok(Some(d)), - Err(e) => { - log::error!("error deserializing message: {}", e); - Ok(None) - } - } - } else { - Ok(None) - } - }, - Err(e) => { - log::error!("error receiving message: {}", e); - Ok(None) - } - } - } else { - log::error!("pipe closed"); - Err("Pipe closed") - } - } else { - Ok(None) - } - } - } -} - -#[macro_export] -macro_rules! recv_now { - ($reader:expr) => { - { - if let Some(msg) = $reader.next().await { + ($reader:expr) => {{ + if let Some(future_result) = $reader.next().now_or_never() { + if let Some(msg) = future_result { match msg { Ok(msg) => { if msg.is_binary() { - match MessageC2S::try_from(&msg.into_data()) { + match MessageC2S::try_from(msg.into_data().as_slice()) { Ok(d) => Ok(Some(d)), Err(e) => { log::error!("error deserializing message: {}", e); @@ -65,7 +29,7 @@ macro_rules! recv_now { } else { Ok(None) } - }, + } Err(e) => { log::error!("error receiving message: {}", e); Ok(None) @@ -75,6 +39,38 @@ macro_rules! recv_now { log::error!("pipe closed"); Err("Pipe closed") } + } else { + Ok(None) } - }; -} \ No newline at end of file + }}; +} + +#[macro_export] +macro_rules! recv_now { + ($reader:expr) => {{ + if let Some(msg) = $reader.next().await { + match msg { + Ok(msg) => { + if msg.is_binary() { + match MessageC2S::try_from(&msg.into_data()) { + Ok(d) => Ok(Some(d)), + Err(e) => { + log::error!("error deserializing message: {}", e); + Ok(None) + } + } + } else { + Ok(None) + } + } + Err(e) => { + log::error!("error receiving message: {}", e); + Ok(None) + } + } + } else { + log::error!("pipe closed"); + Err("Pipe closed") + } + }}; +} diff --git a/server/src/main.rs b/server/src/main.rs index da66407a4aa6329db4e0fd298cd468f8a8b9625f..c7658903f6934549b7519ee3ca3bbb3b219b40ec 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,37 +1,45 @@ -use std::error::Error; -use std::net::SocketAddr; -use async_std::io::WriteExt; -use async_std::sync::Arc; -use async_std::net::{TcpListener, TcpStream}; -use entity::{EntityHandler}; -use manager::PhysicsData; -use nalgebra::vector; -use rapier2d_f64::prelude::{MultibodyJointSet, ImpulseJointSet, ColliderSet, RigidBodySet, NarrowPhase, BroadPhase, IslandManager, CCDSolver, IntegrationParameters}; -use lazy_static::lazy_static; -use log::{error, info, Level, warn}; -use serde::{Deserialize, Serialize}; use crate::entity::Entity; +use crate::handler::handle_client; use crate::manager::{ClientHandler, ClientManager}; use crate::timer::timer_main; +use async_std::io::WriteExt; +use async_std::net::{TcpListener, TcpStream}; +use async_std::sync::Arc; use async_std::sync::RwLock; +use entity::EntityHandler; use futures::StreamExt; +use lazy_static::lazy_static; +use log::{error, info, warn, Level}; +use manager::PhysicsData; +use nalgebra::vector; +use rapier2d_f64::prelude::{ + BroadPhase, CCDSolver, ColliderSet, ImpulseJointSet, IntegrationParameters, IslandManager, + MultibodyJointSet, NarrowPhase, RigidBodySet, +}; +use serde::{Deserialize, Serialize}; use starkingdoms_protocol::PROTOCOL_VERSION; -use crate::handler::handle_client; +use std::error::Error; +use std::net::SocketAddr; pub mod handler; pub mod manager; pub mod timer; #[macro_use] pub mod macros; -pub mod planet; -pub mod orbit; -pub mod entity; pub mod api; +pub mod entity; +pub mod orbit; +pub mod planet; const SCALE: f64 = 1.0; -async fn handle_request(conn: TcpStream, remote_addr: SocketAddr, mgr: ClientManager, - entities: Arc>, physics_data: Arc>) { +async fn handle_request( + conn: TcpStream, + remote_addr: SocketAddr, + mgr: ClientManager, + entities: Arc>, + physics_data: Arc>, +) { match _handle_request(conn, remote_addr, mgr, entities, physics_data).await { Ok(_) => (), Err(e) => { @@ -40,8 +48,13 @@ async fn handle_request(conn: TcpStream, remote_addr: SocketAddr, mgr: ClientMan } } -async fn _handle_request(mut conn: TcpStream, remote_addr: SocketAddr, mgr: ClientManager, - entities: Arc>, physics_data: Arc>) -> Result<(), Box> { +async fn _handle_request( + mut conn: TcpStream, + remote_addr: SocketAddr, + mgr: ClientManager, + entities: Arc>, + physics_data: Arc>, +) -> Result<(), Box> { let mut peek_buf = [0u8; 9]; loop { @@ -56,20 +69,29 @@ async fn _handle_request(mut conn: TcpStream, remote_addr: SocketAddr, mgr: Clie let ping_resp = serde_json::to_string(&ServerPingResponse { version: ServerPingResponseVersion { name: env!("STK_VERSION_NAME").to_string(), // Set by build.rs - number: env!("STK_VERSION").to_string(), // Set by build.rs + number: env!("STK_VERSION").to_string(), // Set by build.rs protocol: PROTOCOL_VERSION, channel: env!("STK_CHANNEL").to_string(), - build: env!("STK_BUILD").to_string() + build: env!("STK_BUILD").to_string(), }, players: CMGR.usernames.read().await.len() as u32, description: env!("STK_SLP_DESCRIPTION").to_string(), - }).unwrap(); + }) + .unwrap(); - let resp_str = format!("HTTP/1.0 200 OK\nAccess-Control-Allow-Origin: *\nContent-Length: {}\n\n{}", ping_resp.len(), ping_resp); + let resp_str = format!( + "HTTP/1.0 200 OK\nAccess-Control-Allow-Origin: *\nContent-Length: {}\n\n{}", + ping_resp.len(), + ping_resp + ); let http_resp = resp_str.as_bytes(); conn.write_all(http_resp).await?; - info!("[{}] sent ping response (200 OK {} bytes)", remote_addr, ping_resp.len()); + info!( + "[{}] sent ping response (200 OK {} bytes)", + remote_addr, + ping_resp.len() + ); return Ok(()); } info!("[{}] incoming websocket connection", remote_addr); @@ -91,16 +113,28 @@ async fn _handle_request(mut conn: TcpStream, remote_addr: SocketAddr, mgr: Clie info!("[{}] passing to client handler", remote_addr); //forward the stream to the sink to achieve echo - match handle_client(mgr.clone(), entities.clone(), physics_data.clone(), remote_addr, rx, ws_write, ws_read).await { + match handle_client( + mgr.clone(), + entities.clone(), + physics_data.clone(), + remote_addr, + rx, + ws_write, + ws_read, + ) + .await + { Ok(_) => (), Err(e) if e.is::() => { - let e = e.downcast::().unwrap(); + let e = e + .downcast::() + .unwrap(); if matches!(*e, async_tungstenite::tungstenite::Error::ConnectionClosed) { info!("[{}] connection closed normally", remote_addr); } else { error!("[{}] error in client thread: {}", remote_addr, e); } - }, + } Err(e) => { error!("[{}] error in client thread: {}", remote_addr, e); } @@ -125,8 +159,14 @@ async fn _handle_request(mut conn: TcpStream, remote_addr: SocketAddr, mgr: Clie } }; if let Entity::Player(player) = entities.read().await.entities.get(&player_id).unwrap() { - rigid_body_set.remove(player.handle, &mut island_manager, &mut collider_set, - &mut impulse_joint_set, &mut multibody_joint_set, true); + rigid_body_set.remove( + player.handle, + &mut island_manager, + &mut collider_set, + &mut impulse_joint_set, + &mut multibody_joint_set, + true, + ); } data.rigid_body_set = rigid_body_set; data.collider_set = collider_set; @@ -144,11 +184,12 @@ lazy_static! { handlers: Arc::new(RwLock::new(Default::default())), usernames: Arc::new(RwLock::new(Default::default())), }; - static ref DATA: Arc> = Arc::new(RwLock::new(PhysicsData { + static ref DATA: Arc> = Arc::new(RwLock::new(PhysicsData { gravity: vector![0.0, 0.0], integration_parameters: IntegrationParameters { dt: 1.0 / 20.0, - ..Default::default() }, + ..Default::default() + }, island_manager: IslandManager::new(), broad_phase: BroadPhase::new(), narrow_phase: NarrowPhase::new(), @@ -156,7 +197,8 @@ lazy_static! { collider_set: ColliderSet::new(), impulse_joint_set: ImpulseJointSet::new(), multibody_joint_set: MultibodyJointSet::new(), - ccd_solver: CCDSolver::new(), })); + ccd_solver: CCDSolver::new(), + })); static ref ENTITIES: Arc> = Arc::new(RwLock::new(EntityHandler::new())); } @@ -164,10 +206,16 @@ lazy_static! { async fn main() { simple_logger::init_with_level(Level::Debug).expect("Unable to start logging service"); - info!("StarKingdoms server (v: {}, build {}) - initializing", env!("STK_VERSION"), env!("STK_BUILD")); + info!( + "StarKingdoms server (v: {}, build {}) - initializing", + env!("STK_VERSION"), + env!("STK_BUILD") + ); if std::env::var("STK_API_KEY").is_err() { - error!("Unable to read the API key from STK_API_KEY. Ensure it is set, and has a valid value."); + error!( + "Unable to read the API key from STK_API_KEY. Ensure it is set, and has a valid value." + ); std::process::exit(1); } if std::env::var("STK_API_URL").is_err() { @@ -197,8 +245,13 @@ async fn main() { }; while let Ok((stream, peer_addr)) = listener.accept().await { - async_std::task::spawn(handle_request(stream, peer_addr, CMGR.clone(), - ENTITIES.clone(), DATA.clone())); + async_std::task::spawn(handle_request( + stream, + peer_addr, + CMGR.clone(), + ENTITIES.clone(), + DATA.clone(), + )); } } @@ -206,7 +259,7 @@ async fn main() { pub struct ServerPingResponse { pub version: ServerPingResponseVersion, pub players: u32, - pub description: String + pub description: String, } #[derive(Serialize, Deserialize)] @@ -215,5 +268,5 @@ pub struct ServerPingResponseVersion { pub number: String, pub protocol: u32, pub channel: String, - pub build: String + pub build: String, } diff --git a/server/src/manager.rs b/server/src/manager.rs index 0b97253dcfdf5a00f886c7787b9f6d52f8e367e0..c7f29ee7b760f7c4294a8e00c0e97b176d548b76 100644 --- a/server/src/manager.rs +++ b/server/src/manager.rs @@ -1,16 +1,21 @@ -use std::collections::HashMap; -use std::net::SocketAddr; -use std::sync::Arc; +use async_std::channel::Sender; +use async_std::sync::RwLock; use nalgebra::point; use rapier2d_f64::na::Vector2; -use rapier2d_f64::prelude::{IntegrationParameters, PhysicsPipeline, IslandManager, BroadPhase, NarrowPhase, ImpulseJointSet, MultibodyJointSet, CCDSolver, RigidBodySet, ColliderSet, RigidBodyHandle, ImpulseJointHandle, RigidBodyBuilder, ColliderBuilder, FixedJointBuilder, Real, MassProperties, Isometry}; -use async_std::sync::RwLock; -use async_std::channel::Sender; +use rapier2d_f64::prelude::{ + BroadPhase, CCDSolver, ColliderBuilder, ColliderSet, FixedJointBuilder, ImpulseJointHandle, + ImpulseJointSet, IntegrationParameters, IslandManager, Isometry, MassProperties, + MultibodyJointSet, NarrowPhase, PhysicsPipeline, Real, RigidBodyBuilder, RigidBodyHandle, + RigidBodySet, +}; use starkingdoms_protocol::api::APISavedPlayerData; use starkingdoms_protocol::module::ModuleType; +use std::collections::HashMap; +use std::net::SocketAddr; +use std::sync::Arc; +use crate::entity::{get_entity_id, Entity, EntityHandler, EntityId}; use crate::SCALE; -use crate::entity::{EntityId, EntityHandler, Entity, get_entity_id}; #[derive(Clone)] pub struct ClientManager { @@ -33,13 +38,13 @@ impl Player { APISavedPlayerData {} } - pub fn load_api_data(&mut self, _data: &APISavedPlayerData) { - - } + pub fn load_api_data(&mut self, _data: &APISavedPlayerData) {} pub fn search_modules(&self, entities: &EntityHandler) -> Vec { let mut modules = Vec::new(); for attachment in self.children.iter().flatten() { - if let Entity::AttachedModule(child_module) = entities.entities.get(&attachment.child).unwrap() { + if let Entity::AttachedModule(child_module) = + entities.entities.get(&attachment.child).unwrap() + { modules.append(&mut child_module.search_modules(entities)); } } @@ -70,20 +75,29 @@ pub struct AttachedModule { pub children: [Option; 4], } impl AttachedModule { - pub fn attach(data: &mut PhysicsData, entities: &mut EntityHandler, parent: EntityId, - player_id: EntityId, module: Module, attachment_slot: usize) { + pub fn attach( + data: &mut PhysicsData, + entities: &mut EntityHandler, + parent: EntityId, + player_id: EntityId, + module: Module, + attachment_slot: usize, + ) { let mut entity_map = entities.entities.clone(); - let loose_id = entities.get_from_module(&module).expect("loose module does not exist"); - let loose_body = data.rigid_body_set.get(module.handle).expect("loose module does not exist"); - let parent_entity = entity_map.get_mut(&parent).expect("parent id does not exist"); + let loose_id = entities + .get_from_module(&module) + .expect("loose module does not exist"); + let loose_body = data + .rigid_body_set + .get(module.handle) + .expect("loose module does not exist"); + let parent_entity = entity_map + .get_mut(&parent) + .expect("parent id does not exist"); let parent_handle = match parent_entity { - Entity::Player(player) => { - player.handle - }, - Entity::AttachedModule(module) => { - module.handle - }, + Entity::Player(player) => player.handle, + Entity::AttachedModule(module) => module.handle, _ => { panic!("unexpected parent"); } @@ -99,12 +113,18 @@ impl AttachedModule { .rotation(loose_body.rotation().angle()) .build(); let attached_handle = data.rigid_body_set.insert(module_body); - data.collider_set.insert_with_parent(module_collider, attached_handle, &mut data.rigid_body_set); + data.collider_set.insert_with_parent( + module_collider, + attached_handle, + &mut data.rigid_body_set, + ); let attach_joint = FixedJointBuilder::new() .local_anchor1(point![0.0, 0.0]) .local_anchor2(point![0.0, 0.0]) .build(); - let attach_joint_handle = data.impulse_joint_set.insert(parent_handle, attached_handle, attach_joint, true); + let attach_joint_handle = + data.impulse_joint_set + .insert(parent_handle, attached_handle, attach_joint, true); let attached_module = AttachedModule { handle: attached_handle, module_type: module.module_type, @@ -118,40 +138,48 @@ impl AttachedModule { child: attached_id, connection: attach_joint_handle, }); - }, - Entity::AttachedModule(ref mut module) => { + } + Entity::AttachedModule(ref mut module) => { module.children[attachment_slot] = Some(Attachment { child: attached_id, connection: attach_joint_handle, }); - }, + } _ => { panic!("unexpected parent"); } }; entity_map.insert(attached_id, Entity::AttachedModule(attached_module)); // delete loose module - data.rigid_body_set.remove(module.handle, - &mut data.island_manager, - &mut data.collider_set, - &mut data.impulse_joint_set, - &mut data.multibody_joint_set, true); + data.rigid_body_set.remove( + module.handle, + &mut data.island_manager, + &mut data.collider_set, + &mut data.impulse_joint_set, + &mut data.multibody_joint_set, + true, + ); entities.entities.remove(&loose_id); } - pub fn attach_new(data: &mut PhysicsData, entities: &mut EntityHandler, parent: EntityId, - player_id: EntityId, module: ModuleTemplate, attachment_slot: usize, rotation: f64) { + pub fn attach_new( + data: &mut PhysicsData, + entities: &mut EntityHandler, + parent: EntityId, + player_id: EntityId, + module: ModuleTemplate, + attachment_slot: usize, + rotation: f64, + ) { let mut entity_map = entities.entities.clone(); //let loose_id = entities.get_from_module(&module).expect("loose module does not exist"); //let loose_body = data.rigid_body_set.get(module.handle).expect("loose module does not exist"); - let parent_entity = entity_map.get_mut(&parent).expect("parent id does not exist"); + let parent_entity = entity_map + .get_mut(&parent) + .expect("parent id does not exist"); let parent_handle = match parent_entity { - Entity::Player(player) => { - player.handle - }, - Entity::AttachedModule(module) => { - module.handle - }, + Entity::Player(player) => player.handle, + Entity::AttachedModule(module) => module.handle, _ => { panic!("unexpected parent"); } @@ -165,17 +193,23 @@ impl AttachedModule { .rotation(module.heading) .build(); let attached_handle = data.rigid_body_set.insert(module_body); - data.collider_set.insert_with_parent(module_collider, attached_handle, &mut data.rigid_body_set); + data.collider_set.insert_with_parent( + module_collider, + attached_handle, + &mut data.rigid_body_set, + ); let anchor = point![ - -0. / SCALE * rotation.cos() +100. / SCALE * rotation.sin(), - -0. / SCALE * rotation.sin() -100. / SCALE * rotation.cos() + -0. / SCALE * rotation.cos() + 100. / SCALE * rotation.sin(), + -0. / SCALE * rotation.sin() - 100. / SCALE * rotation.cos() ]; let attach_joint = FixedJointBuilder::new() .local_anchor1(anchor) .local_anchor2(point![0.0, 0.0 / SCALE]) .local_frame2(Isometry::rotation(rotation)) .build(); - let attach_joint_handle = data.impulse_joint_set.insert(parent_handle, attached_handle, attach_joint, true); + let attach_joint_handle = + data.impulse_joint_set + .insert(parent_handle, attached_handle, attach_joint, true); let attached_module = AttachedModule { handle: attached_handle, module_type: module.module_type, @@ -189,13 +223,13 @@ impl AttachedModule { child: attached_id, connection: attach_joint_handle, }); - }, - Entity::AttachedModule(ref mut module) => { + } + Entity::AttachedModule(ref mut module) => { module.children[attachment_slot] = Some(Attachment { child: attached_id, connection: attach_joint_handle, }); - }, + } _ => { panic!("unexpected parent"); } @@ -215,7 +249,9 @@ impl AttachedModule { pub fn search_modules(&self, entities: &EntityHandler) -> Vec { let mut modules = Vec::new(); for attachment in self.children.iter().flatten() { - if let Entity::AttachedModule(child_module) = entities.entities.get(&attachment.child).unwrap() { + if let Entity::AttachedModule(child_module) = + entities.entities.get(&attachment.child).unwrap() + { modules.append(&mut child_module.search_modules(entities)); } } @@ -234,19 +270,20 @@ pub struct PlayerInput { pub up: bool, pub left: bool, pub right: bool, - pub down: bool + pub down: bool, } #[derive(Clone)] pub struct ClientHandler { - pub tx: Sender + pub tx: Sender, } #[derive(Clone, Default)] pub struct PhysicsData { pub gravity: Vector2, pub integration_parameters: IntegrationParameters, - pub island_manager: IslandManager, pub broad_phase: BroadPhase, + pub island_manager: IslandManager, + pub broad_phase: BroadPhase, pub narrow_phase: NarrowPhase, pub rigid_body_set: RigidBodySet, pub collider_set: ColliderSet, @@ -256,28 +293,38 @@ pub struct PhysicsData { } impl PhysicsData { pub fn tick(&mut self, pipeline: &mut PhysicsPipeline) { - pipeline.step(&self.gravity, - &self.integration_parameters, - &mut self.island_manager, - &mut self.broad_phase, - &mut self.narrow_phase, - &mut self.rigid_body_set, - &mut self.collider_set, - &mut self.impulse_joint_set, - &mut self.multibody_joint_set, - &mut self.ccd_solver, - None, - &(), - &() - ); + pipeline.step( + &self.gravity, + &self.integration_parameters, + &mut self.island_manager, + &mut self.broad_phase, + &mut self.narrow_phase, + &mut self.rigid_body_set, + &mut self.collider_set, + &mut self.impulse_joint_set, + &mut self.multibody_joint_set, + &mut self.ccd_solver, + None, + &(), + &(), + ); } } #[derive(Debug, Clone)] pub enum ClientHandlerMessage { Tick, - ChatMessage { from: String, message: String }, - PlayersUpdate { players: Vec }, - PlanetData { planets: Vec }, - ModulesUpdate { modules: Vec }, + ChatMessage { + from: String, + message: String, + }, + PlayersUpdate { + players: Vec, + }, + PlanetData { + planets: Vec, + }, + ModulesUpdate { + modules: Vec, + }, } diff --git a/server/src/orbit/constants.rs b/server/src/orbit/constants.rs index 5924fac108b89a87847641591969902196ca1cf8..aa93646ad5c70f7217ad4f24e6564ae3cf5f80ea 100644 --- a/server/src/orbit/constants.rs +++ b/server/src/orbit/constants.rs @@ -22,6 +22,6 @@ pub const MOON_RADIUS: f64 = MOON_RADIUS_RL * GAME_SCALE_DISTANCE * MOON_RADIUS_ pub const MOON_MASS_RL: f64 = 73476730900000000000000.0; pub const MOON_MASS: f64 = MOON_MASS_RL * GAME_SCALE_MASS * MOON_MASS_BIAS; pub const MOON_PERIAPSIS: f64 = 363228900.0 * GAME_SCALE_DISTANCE * MOON_PERIAPSIS_BIAS; -pub const MOON_APOAPSIS: f64 = 405400000.0 * GAME_SCALE_DISTANCE * MOON_APOAPSIS_BIAS; +pub const MOON_APOAPSIS: f64 = 405400000.0 * GAME_SCALE_DISTANCE * MOON_APOAPSIS_BIAS; pub const MOON_ORBIT_TIME_RL: f64 = 2332800.0; -pub const MOON_ORBIT_TIME: f64 = MOON_ORBIT_TIME_RL * GAME_SCALE_TIME * MOON_ORBIT_TIME_BIAS; \ No newline at end of file +pub const MOON_ORBIT_TIME: f64 = MOON_ORBIT_TIME_RL * GAME_SCALE_TIME * MOON_ORBIT_TIME_BIAS; diff --git a/server/src/orbit/kepler.rs b/server/src/orbit/kepler.rs index a698038dbe0358fcd5a7c48b2e289373efcccf2a..0ef980c9fa9b0cb1600b1cf3dde22f6ff13d41ac 100644 --- a/server/src/orbit/kepler.rs +++ b/server/src/orbit/kepler.rs @@ -4,4 +4,4 @@ /// e is the eccentricity of the orbit (0 = perfect circle, and up to 1 is increasingly elliptical) pub fn kepler_equation(eccentric_anomaly: f64, mean_anomaly: f64, eccentricity: f64) -> f64 { mean_anomaly - eccentric_anomaly + eccentricity * eccentric_anomaly.sin() -} \ No newline at end of file +} diff --git a/server/src/orbit/mod.rs b/server/src/orbit/mod.rs index d7d02c09c1b9de33d9aa4f54e27d70a3e997d2b3..0028b8509d824b6d511f47362a815b94b1658687 100644 --- a/server/src/orbit/mod.rs +++ b/server/src/orbit/mod.rs @@ -1,6 +1,6 @@ pub mod constants; +pub mod kepler; +pub mod newtonian; #[allow(clippy::module_inception)] pub mod orbit; -pub mod newtonian; -pub mod kepler; -pub mod vis_viva; \ No newline at end of file +pub mod vis_viva; diff --git a/server/src/orbit/newtonian.rs b/server/src/orbit/newtonian.rs index 9578dedb0d345151e1d3586f0dc3208c62a53b8c..6b5786d124ba06e4b86dd3fe7b67e97f964394e2 100644 --- a/server/src/orbit/newtonian.rs +++ b/server/src/orbit/newtonian.rs @@ -3,7 +3,11 @@ use crate::orbit::kepler::kepler_equation; pub const NEWTONIAN_STEP_SIZE: f64 = 0.0001; pub const NEWTONIAN_ACCEPTABLE_ERROR: f64 = 0.00000001; -pub fn solve_kepler_with_newtonian(mean_anomaly: f64, eccentricity: f64, max_iterations: u64) -> f64 { +pub fn solve_kepler_with_newtonian( + mean_anomaly: f64, + eccentricity: f64, + max_iterations: u64, +) -> f64 { let mut guess = mean_anomaly; for _ in 0..max_iterations { @@ -15,10 +19,11 @@ pub fn solve_kepler_with_newtonian(mean_anomaly: f64, eccentricity: f64, max_ite } // otherwise, update guess - let slope = (kepler_equation(guess + NEWTONIAN_STEP_SIZE, mean_anomaly, eccentricity) - y) / NEWTONIAN_STEP_SIZE; + let slope = (kepler_equation(guess + NEWTONIAN_STEP_SIZE, mean_anomaly, eccentricity) - y) + / NEWTONIAN_STEP_SIZE; let step = y / slope; guess -= step; } guess -} \ No newline at end of file +} diff --git a/server/src/orbit/orbit.rs b/server/src/orbit/orbit.rs index 226f3c5fee1c53fdc94f73211fe5bc682f6b2d44..fca217d7e9f8561f7048303416c9e8c6f850e40e 100644 --- a/server/src/orbit/orbit.rs +++ b/server/src/orbit/orbit.rs @@ -1,15 +1,28 @@ // Mostly stolen from SebLague's plane game // thanks -use nalgebra::{vector, Vector2}; use crate::orbit::newtonian::solve_kepler_with_newtonian; +use nalgebra::{vector, Vector2}; #[allow(clippy::too_many_arguments)] -pub fn calculate_vector_of_orbit(periapsis: f64, apoapsis: f64, t: f64, current_x: f64, current_y: f64, orbiting_x: f64, orbiting_y: f64, mass: f64, step: f64) -> Vector2 { +pub fn calculate_vector_of_orbit( + periapsis: f64, + apoapsis: f64, + t: f64, + current_x: f64, + current_y: f64, + orbiting_x: f64, + orbiting_y: f64, + mass: f64, + step: f64, +) -> Vector2 { let semi_major_length = (apoapsis + periapsis) / 2.0; let _linear_eccentricity = semi_major_length - periapsis; // distance between center and focus - let target = calculate_world_position_of_orbit(calculate_point_on_orbit(periapsis, apoapsis, t), vector![orbiting_x, orbiting_y]); + let target = calculate_world_position_of_orbit( + calculate_point_on_orbit(periapsis, apoapsis, t), + vector![orbiting_x, orbiting_y], + ); let target_x = target[0]; let target_y = target[1]; @@ -32,7 +45,8 @@ pub fn calculate_point_on_orbit(periapsis: f64, apoapsis: f64, t: f64) -> Vector let semi_major_length = (apoapsis + periapsis) / 2.0; let linear_eccentricity = semi_major_length - periapsis; // distance between center and focus let eccentricity = linear_eccentricity / semi_major_length; // 0: circle. 1: parabola. in between: ellipse - let semi_minor_length = (semi_major_length * semi_major_length - linear_eccentricity * linear_eccentricity).sqrt(); + let semi_minor_length = + (semi_major_length * semi_major_length - linear_eccentricity * linear_eccentricity).sqrt(); let mean_anomaly = t * std::f64::consts::PI * 2.0; let eccentric_anomaly = solve_kepler_with_newtonian(mean_anomaly, eccentricity, 100); @@ -44,8 +58,11 @@ pub fn calculate_point_on_orbit(periapsis: f64, apoapsis: f64, t: f64) -> Vector vector![point_x, point_y] } -pub fn calculate_world_position_of_orbit(point: Vector2, orbiting_on: Vector2) -> Vector2 { +pub fn calculate_world_position_of_orbit( + point: Vector2, + orbiting_on: Vector2, +) -> Vector2 { // i have no idea if this is actually right or not // we'll find out vector![point[0] + orbiting_on[0], point[1] + orbiting_on[1]] -} \ No newline at end of file +} diff --git a/server/src/orbit/vis_viva.rs b/server/src/orbit/vis_viva.rs index 22c9a17e966d1b43e20a5432efcd0600600a32c6..df052ff68cdc36e4c3736a1ac44abdffba272ff2 100644 --- a/server/src/orbit/vis_viva.rs +++ b/server/src/orbit/vis_viva.rs @@ -1,3 +1,8 @@ -pub fn vis_viva(distance_between_centers: f64, semi_major: f64, g: f64, mass_of_bigger: f64) -> f64 { +pub fn vis_viva( + distance_between_centers: f64, + semi_major: f64, + g: f64, + mass_of_bigger: f64, +) -> f64 { (g * mass_of_bigger * (2.0 / distance_between_centers - 1.0 / semi_major)).sqrt() -} \ No newline at end of file +} diff --git a/server/src/planet.rs b/server/src/planet.rs index 36927569ebaaba18cdf4eb61681f53ac53c58d77..485b427eedc88d5eae8bf45859e35a5edf9decb3 100644 --- a/server/src/planet.rs +++ b/server/src/planet.rs @@ -1,12 +1,16 @@ -use std::collections::HashMap; -use nalgebra::{Vector2, vector}; -use rapier2d_f64::prelude::{RigidBodyHandle, RigidBodySet, ColliderBuilder, RigidBodyBuilder, ColliderSet}; +use nalgebra::{vector, Vector2}; +use rapier2d_f64::prelude::{ + ColliderBuilder, ColliderSet, RigidBodyBuilder, RigidBodyHandle, RigidBodySet, +}; use starkingdoms_protocol::planet::PlanetType; +use std::collections::HashMap; -use crate::entity::{Entities, get_entity_id, Entity, EntityId}; -use crate::{SCALE, manager::ClientHandlerMessage}; -use crate::orbit::constants::{EARTH_MASS, EARTH_RADIUS, MOON_APOAPSIS, MOON_MASS, MOON_PERIAPSIS, MOON_RADIUS}; +use crate::entity::{get_entity_id, Entities, Entity, EntityId}; +use crate::orbit::constants::{ + EARTH_MASS, EARTH_RADIUS, MOON_APOAPSIS, MOON_MASS, MOON_PERIAPSIS, MOON_RADIUS, +}; use crate::orbit::orbit::{calculate_point_on_orbit, calculate_world_position_of_orbit}; +use crate::{manager::ClientHandlerMessage, SCALE}; //const GRAVITY: f64 = 0.001; pub const GRAVITY: f64 = 12.6674; @@ -17,14 +21,17 @@ pub struct Planet { pub body_handle: RigidBodyHandle, pub position: (f64, f64), pub radius: f64, - pub mass: f64 + pub mass: f64, } impl Planet { pub fn gravity(&self, position: (f64, f64), mass: f64) -> (f64, f64) { - let distance = ((position.0 - self.position.0).powi(2) + (position.1 - self.position.1).powi(2)).sqrt(); + let distance = ((position.0 - self.position.0).powi(2) + + (position.1 - self.position.1).powi(2)) + .sqrt(); let force = GRAVITY * ((self.mass * mass) / (distance * distance)); - let mut direction = Vector2::new(self.position.0 - position.0, self.position.1 - position.1); + let mut direction = + Vector2::new(self.position.0 - position.0, self.position.1 - position.1); direction.set_magnitude(force); (direction.x, direction.y) } @@ -44,12 +51,16 @@ impl Planets { self.planets.get_mut(planet_id) } - pub async fn make_planet(_planet_id: &str, - planet_type: PlanetType, mass: f64, radius: f64, - position: (f64, f64), rigid_body_set: &mut RigidBodySet, collider_set: &mut ColliderSet - ) -> (EntityId, Entity) { - let collider = ColliderBuilder::ball(radius / SCALE) - .build(); + pub async fn make_planet( + _planet_id: &str, + planet_type: PlanetType, + mass: f64, + radius: f64, + position: (f64, f64), + rigid_body_set: &mut RigidBodySet, + collider_set: &mut ColliderSet, + ) -> (EntityId, Entity) { + let collider = ColliderBuilder::ball(radius / SCALE).build(); let body = RigidBodyBuilder::kinematic_position_based() .translation(vector![position.0 / SCALE, position.1 / SCALE]) .dominance_group(127) @@ -59,17 +70,23 @@ impl Planets { collider_set.insert_with_parent(collider, body_handle, rigid_body_set); let entity_id = get_entity_id(); - (entity_id, Entity::Planet(Planet { - planet_type, - body_handle, - position, - radius, - mass, - })) + ( + entity_id, + Entity::Planet(Planet { + planet_type, + body_handle, + position, + radius, + mass, + }), + ) } - pub async fn create_planets(rigid_body_set: &mut RigidBodySet, collider_set: &mut ColliderSet, - entities: &mut Entities) -> Vec { + pub async fn create_planets( + rigid_body_set: &mut RigidBodySet, + collider_set: &mut ColliderSet, + entities: &mut Entities, + ) -> Vec { let mut planet_ids: Vec = Vec::new(); let (earth_id, entity) = Planets::make_planet( "earth", @@ -79,13 +96,17 @@ impl Planets { (100.0, 100.0), rigid_body_set, collider_set, - ).await; + ) + .await; entities.insert(earth_id, entity); planet_ids.push(earth_id); let moon_start_point; if let Entity::Planet(earth) = entities.get(&earth_id).unwrap() { - moon_start_point = calculate_world_position_of_orbit(calculate_point_on_orbit(MOON_PERIAPSIS, MOON_APOAPSIS, 0.0), vector![earth.position.0, earth.position.1]); + moon_start_point = calculate_world_position_of_orbit( + calculate_point_on_orbit(MOON_PERIAPSIS, MOON_APOAPSIS, 0.0), + vector![earth.position.0, earth.position.1], + ); } else { moon_start_point = vector![0., 0.]; } @@ -97,8 +118,9 @@ impl Planets { MOON_RADIUS, (moon_start_point[0], moon_start_point[1]), rigid_body_set, - collider_set - ).await; + collider_set, + ) + .await; entities.insert(moon_id, moon); planet_ids.push(moon_id); planet_ids @@ -118,9 +140,7 @@ impl Planets { }); } - ClientHandlerMessage::PlanetData { - planets - } + ClientHandlerMessage::PlanetData { planets } } pub fn gravity(&self, position: (f64, f64), mass: f64) -> (f64, f64) { diff --git a/server/src/timer.rs b/server/src/timer.rs index da273b29cb7fdd054b5eb31202ad95dd61198a53..c6948d637c126190fd5648bd93543255b48961cb 100644 --- a/server/src/timer.rs +++ b/server/src/timer.rs @@ -1,22 +1,35 @@ -use std::{time::Duration, sync::Arc, f64::consts::PI}; -use log::{warn, info}; -use nalgebra::{vector, point}; -use rand::Rng; -use rapier2d_f64::prelude::{PhysicsPipeline, ColliderBuilder, RigidBodyBuilder, MassProperties, RigidBodyHandle}; -use async_std::sync::RwLock; -use async_std::task::sleep; -use starkingdoms_protocol::{player::Player, planet::PlanetType, module::ModuleType}; -use crate::{manager::{ClientHandlerMessage, ClientManager, PhysicsData, Module}, SCALE, planet::{Planets, Planet}, entity::{get_entity_id, Entity}}; use crate::entity::EntityHandler; -use crate::orbit::constants::{GAME_ORBITS_ENABLED, MOON_APOAPSIS, MOON_ORBIT_TIME, MOON_PERIAPSIS}; +use crate::orbit::constants::{ + GAME_ORBITS_ENABLED, MOON_APOAPSIS, MOON_ORBIT_TIME, MOON_PERIAPSIS, +}; use crate::orbit::orbit::{calculate_point_on_orbit, calculate_world_position_of_orbit}; +use crate::{ + entity::{get_entity_id, Entity}, + manager::{ClientHandlerMessage, ClientManager, Module, PhysicsData}, + planet::{Planet, Planets}, + SCALE, +}; +use async_std::sync::RwLock; +use async_std::task::sleep; +use log::{info, warn}; +use nalgebra::{point, vector}; +use rand::Rng; +use rapier2d_f64::prelude::{ + ColliderBuilder, MassProperties, PhysicsPipeline, RigidBodyBuilder, RigidBodyHandle, +}; +use starkingdoms_protocol::{module::ModuleType, planet::PlanetType, player::Player}; +use std::{f64::consts::PI, sync::Arc, time::Duration}; pub const ROTATIONAL_FORCE: f64 = 100.0; pub const LATERAL_FORCE: f64 = 100.0; pub const MODULE_SPAWN: f64 = 3.0; pub const MODULE_MAX: u32 = 10; -pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc>, entities: Arc>) { +pub async fn timer_main( + mgr: ClientManager, + physics_data_orig: Arc>, + entities: Arc>, +) { let mut pipeline = PhysicsPipeline::new(); let mut time = 0.0; @@ -29,7 +42,12 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc MODULE_SPAWN && entities.read().await.get_module_count() < MODULE_MAX { + if module_timer > MODULE_SPAWN && entities.read().await.get_module_count() < MODULE_MAX + { module_timer = 0.; let mut rigid_body_set = physics_data.rigid_body_set.clone(); @@ -81,10 +109,17 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc() * PI * 2. }; let module_body = RigidBodyBuilder::dynamic() - .translation(vector![angle.cos() * 2050. / SCALE, angle.sin() * 2050.0/SCALE]) + .translation(vector![ + angle.cos() * 2050. / SCALE, + angle.sin() * 2050.0 / SCALE + ]) .build(); let module_handler = rigid_body_set.insert(module_body); - collider_set.insert_with_parent(module_collider, module_handler, &mut rigid_body_set); + collider_set.insert_with_parent( + module_collider, + module_handler, + &mut rigid_body_set, + ); physics_data.rigid_body_set = rigid_body_set; physics_data.collider_set = collider_set; @@ -94,7 +129,11 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc 80. { let mut rigid_body_set = physics_data.rigid_body_set.clone(); @@ -114,8 +156,14 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc { - match client_thread.tx.send(ClientHandlerMessage::PlayersUpdate {players: protocol_players.clone()}).await { + match client_thread + .tx + .send(ClientHandlerMessage::PlayersUpdate { + players: protocol_players.clone(), + }) + .await + { Ok(_) => (), Err(e) => { warn!("unable to send position packet: {}", e); @@ -247,20 +295,32 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc = attached_modules.iter().map(|m| { - m.handle - }).collect(); - modules.append(&mut attached_modules.iter().map(|m| { - let module = m.to_module(); - info!("{:?}", module); - module - }).collect()); + let attached_handles: Vec = + attached_modules.iter().map(|m| m.handle).collect(); + modules.append( + &mut attached_modules + .iter() + .map(|m| { + let module = m.to_module(); + info!("{:?}", module); + module + }) + .collect(), + ); modules.iter().for_each(|module| { if attached_handles.contains(&module.handle) { - info!("{:?}", physics_data.rigid_body_set.get(module.handle).unwrap().translation()); + info!( + "{:?}", + physics_data + .rigid_body_set + .get(module.handle) + .unwrap() + .translation() + ); } }); - let protocol_modules: Vec = modules.iter() + let protocol_modules: Vec = modules + .iter() .map(|module| { let body = physics_data.rigid_body_set.get(module.handle).unwrap(); return starkingdoms_protocol::module::Module { @@ -270,8 +330,15 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc (), Err(e) => { warn!("unable to send module position packet: {}", e);