M Cargo.lock => Cargo.lock +17 -0
@@ 3164,6 3164,14 @@ dependencies = [
]
[[package]]
+name = "spacetime"
+version = "0.1.0"
+dependencies = [
+ "tabwriter",
+ "which",
+]
+
+[[package]]
name = "spade"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 3400,6 3408,15 @@ dependencies = [
]
[[package]]
+name = "tabwriter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36205cfc997faadcc4b0b87aaef3fbedafe20d38d4959a7ca6ff803564051111"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
name = "tempfile"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
M Cargo.toml => Cargo.toml +2 -1
@@ 4,7 4,8 @@ members = [
"protocol",
"api",
"api/starkingdoms_api_entities",
- "api/starkingdoms_api_migration"
+ "api/starkingdoms_api_migration",
+ "spacetime_rs"
]
[profile.dev.package.rapier2d-f64]
M Jenkinsfile => Jenkinsfile +2 -2
@@ 26,8 26,8 @@ pipeline {
}
stage('Deploy') {
steps {
- sh 'sshpass -p ${INFRA_KEY} ./spacetime infra update-bleeding'
- sh 'sshpass -p ${INFRA_KEY} ./spacetime infra restart-bleeding'
+ sh 'sshpass -p ${INFRA_KEY} ./ansible/infra update-bleeding'
+ sh 'sshpass -p ${INFRA_KEY} ./ansible/infra restart-bleeding'
}
}
}
A ansible/infra => ansible/infra +3 -0
@@ 0,0 1,3 @@
+#!/bin/bash
+echo "[*] Connecting to infrastructure manager server. If you are prompted for a password, enter your infrastructure key. You may be prompted several times."
+ssh team@10.16.1.3 /home/team/run_ansible.sh "$1"<
\ No newline at end of file
M api/src/config.rs => api/src/config.rs +22 -12
@@ 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<StarkingdomsApiConfig> = 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<String, StarkingdomsApiConfigRealm>
+ pub realms: HashMap<String, StarkingdomsApiConfigRealm>,
}
#[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))
+}
M api/src/error.rs => api/src/error.rs +45 -57
@@ 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<APIError>
+ pub errors: Vec<APIError>,
}
#[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<String>
+ pub path: Option<String>,
}
-fn is_none<T>(o: &Option<T>) -> bool { o.is_none() }
+fn is_none<T>(o: &Option<T>) -> 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
+}
M api/src/main.rs => api/src/main.rs +21 -17
@@ 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<dyn Error>> {
let data = Data::new(AppState {
conn: db,
- templates: tera
+ templates: tera,
});
HttpServer::new(move || {
@@ 57,19 57,23 @@ async fn main() -> Result<(), Box<dyn Error>> {
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
+}
M api/src/routes/beamin.rs => api/src/routes/beamin.rs +49 -54
@@ 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<BeaminRequest>, state: Data<AppState>) -> 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<Sha256> = Hmac::new_from_slice(CONFIG.jwt_signing_secret.as_bytes()).unwrap();
@@ 46,67 44,64 @@ pub async fn beam_in(data: Json<BeaminRequest>, state: Data<AppState>) -> 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<starkingdoms_api_entities::entity::user_savefile::Model> = 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<starkingdoms_api_entities::entity::user_savefile::Model> =
+ 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
+}
M api/src/routes/beamout.rs => api/src/routes/beamout.rs +35 -40
@@ 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<BeamoutRequest>, state: Data<AppState>) -> 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<Sha256> = Hmac::new_from_slice(CONFIG.jwt_signing_secret.as_bytes()).unwrap();
@@ 46,26 44,22 @@ pub async fn beam_out(data: Json<BeamoutRequest>, state: Data<AppState>) -> 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<BeamoutRequest>, state: Data<AppState>) -> 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<BeamoutRequest>, state: Data<AppState>) -> 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
+}
M api/src/routes/callback.rs => api/src/routes/callback.rs +72 -63
@@ 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<CallbackQueryParams>, state: Data<AppState>)
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<String, String> = match query.token.verify_with_key(&rs256_pub_key) {
@@ 59,35 57,35 @@ pub async fn callback(query: Query<CallbackQueryParams>, state: Data<AppState>)
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<CallbackQueryParams>, state: Data<AppState>)
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<CallbackQueryParams>, state: Data<AppState>)
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<CallbackQueryParams>, state: Data<AppState>)
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<CallbackQueryParams>, state: Data<AppState>)
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
+}
M api/src/routes/mod.rs => api/src/routes/mod.rs +2 -2
@@ 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;
M api/src/routes/select_realm.rs => api/src/routes/select_realm.rs +21 -23
@@ 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<String, StarkingdomsApiConfigRealm>,
- pub back_to: String
+ pub back_to: String,
}
#[get("/select-realm")]
@@ 24,14 24,13 @@ pub async fn select_realm(state: Data<AppState>) -> 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<AppState>) -> 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
+}
M api/starkingdoms_api_entities/src/lib.rs => api/starkingdoms_api_entities/src/lib.rs +1 -1
@@ 1,1 1,1 @@
-pub mod entity;>
\ No newline at end of file
+pub mod entity;
M api/starkingdoms_api_migration/src/m20230417_162824_create_table_users.rs => api/starkingdoms_api_migration/src/m20230417_162824_create_table_users.rs +16 -9
@@ 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,
}
M api/starkingdoms_api_migration/src/m20230417_164240_create_table_user_auth_realms.rs => api/starkingdoms_api_migration/src/m20230417_164240_create_table_user_auth_realms.rs +27 -15
@@ 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,
}
M api/starkingdoms_api_migration/src/m20230420_144333_create_table_user_data.rs => api/starkingdoms_api_migration/src/m20230420_144333_create_table_user_data.rs +28 -16
@@ 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,
}
M assets/dist/spritesheet-125.json => assets/dist/spritesheet-125.json +34 -34
@@ 1,25 1,25 @@
{
"frames": {
- "moon.png": {
- "frame": { "x": 0, "y": 0, "w": 256, "h": 256 },
+ "starfield.png": {
+ "frame": { "x": 0, "y": 0, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
- "spriteSourceSize": { "x": 0, "y": 0, "w": 256, "h": 256 },
- "sourceSize": { "w": 256, "h": 256 },
- "pivot": { "x": 128, "y": 128 },
- "9slicedFrame": { "x": 0, "y": 0, "w": 256, "h": 256 }
+ "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
+ "sourceSize": { "w": 64, "h": 64 },
+ "pivot": { "x": 32, "y": 32 },
+ "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
- "earth.png": {
- "frame": { "x": 0, "y": 256, "w": 256, "h": 256 },
+ "moon.png": {
+ "frame": { "x": 0, "y": 64, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
- "spriteSourceSize": { "x": 0, "y": 0, "w": 256, "h": 256 },
- "sourceSize": { "w": 256, "h": 256 },
- "pivot": { "x": 128, "y": 128 },
- "9slicedFrame": { "x": 0, "y": 0, "w": 256, "h": 256 }
+ "spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
+ "sourceSize": { "w": 64, "h": 64 },
+ "pivot": { "x": 32, "y": 32 },
+ "9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
- "starfield.png": {
- "frame": { "x": 0, "y": 512, "w": 64, "h": 64 },
+ "earth.png": {
+ "frame": { "x": 0, "y": 128, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 27,8 27,8 @@
"pivot": { "x": 32, "y": 32 },
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
- "autoplr_cfg.png": {
- "frame": { "x": 0, "y": 576, "w": 64, "h": 64 },
+ "autoplr_error.png": {
+ "frame": { "x": 0, "y": 192, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 36,8 36,8 @@
"pivot": { "x": 32, "y": 32 },
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
- "autoplr_error.png": {
- "frame": { "x": 0, "y": 640, "w": 64, "h": 64 },
+ "autoplr_cfg.png": {
+ "frame": { "x": 0, "y": 256, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 46,7 46,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"hearty.png": {
- "frame": { "x": 0, "y": 704, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 320, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 55,7 55,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"superthruster_on.png": {
- "frame": { "x": 0, "y": 768, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 384, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 64,7 64,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"ecothruster_on.png": {
- "frame": { "x": 0, "y": 832, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 448, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 73,7 73,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"landingthruster_on.png": {
- "frame": { "x": 0, "y": 896, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 512, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 82,7 82,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"thruster_on.png": {
- "frame": { "x": 0, "y": 960, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 576, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 91,7 91,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"landingleg.png": {
- "frame": { "x": 0, "y": 1024, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 640, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 100,7 100,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"autoplr_on.png": {
- "frame": { "x": 0, "y": 1088, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 704, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 109,7 109,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"hub_on.png": {
- "frame": { "x": 0, "y": 1152, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 768, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 118,7 118,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"powerhub_on.png": {
- "frame": { "x": 0, "y": 1216, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 832, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 127,7 127,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"superthruster_off.png": {
- "frame": { "x": 0, "y": 1280, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 896, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 136,7 136,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"landingthruster_off.png": {
- "frame": { "x": 0, "y": 1344, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 960, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 145,7 145,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"thruster_off.png": {
- "frame": { "x": 0, "y": 1408, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 1024, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 154,7 154,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"cargo_on.png": {
- "frame": { "x": 0, "y": 1472, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 1088, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 163,7 163,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"cargo_off.png": {
- "frame": { "x": 0, "y": 1536, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 1152, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 172,7 172,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"powerhub_off.png": {
- "frame": { "x": 0, "y": 1600, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 1216, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
@@ 181,7 181,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
"hub_off.png": {
- "frame": { "x": 0, "y": 1664, "w": 64, "h": 64 },
+ "frame": { "x": 0, "y": 1280, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 64, "h": 64 },
M assets/dist/spritesheet-125.png => assets/dist/spritesheet-125.png +0 -0
M assets/dist/spritesheet-375.json => assets/dist/spritesheet-375.json +34 -34
@@ 1,25 1,25 @@
{
"frames": {
- "moon.png": {
- "frame": { "x": 0, "y": 0, "w": 768, "h": 768 },
+ "starfield.png": {
+ "frame": { "x": 0, "y": 0, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
- "spriteSourceSize": { "x": 0, "y": 0, "w": 768, "h": 768 },
- "sourceSize": { "w": 768, "h": 768 },
- "pivot": { "x": 384, "y": 384 },
- "9slicedFrame": { "x": 0, "y": 0, "w": 768, "h": 768 }
+ "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
+ "sourceSize": { "w": 192, "h": 192 },
+ "pivot": { "x": 96, "y": 96 },
+ "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
- "earth.png": {
- "frame": { "x": 0, "y": 768, "w": 768, "h": 768 },
+ "moon.png": {
+ "frame": { "x": 0, "y": 192, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
- "spriteSourceSize": { "x": 0, "y": 0, "w": 768, "h": 768 },
- "sourceSize": { "w": 768, "h": 768 },
- "pivot": { "x": 384, "y": 384 },
- "9slicedFrame": { "x": 0, "y": 0, "w": 768, "h": 768 }
+ "spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
+ "sourceSize": { "w": 192, "h": 192 },
+ "pivot": { "x": 96, "y": 96 },
+ "9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
- "starfield.png": {
- "frame": { "x": 0, "y": 1536, "w": 192, "h": 192 },
+ "earth.png": {
+ "frame": { "x": 0, "y": 384, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 27,8 27,8 @@
"pivot": { "x": 96, "y": 96 },
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
- "autoplr_cfg.png": {
- "frame": { "x": 0, "y": 1728, "w": 192, "h": 192 },
+ "autoplr_error.png": {
+ "frame": { "x": 0, "y": 576, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 36,8 36,8 @@
"pivot": { "x": 96, "y": 96 },
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
- "autoplr_error.png": {
- "frame": { "x": 0, "y": 1920, "w": 192, "h": 192 },
+ "autoplr_cfg.png": {
+ "frame": { "x": 0, "y": 768, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 46,7 46,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"hearty.png": {
- "frame": { "x": 0, "y": 2112, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 960, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 55,7 55,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"superthruster_on.png": {
- "frame": { "x": 0, "y": 2304, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 1152, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 64,7 64,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"ecothruster_on.png": {
- "frame": { "x": 0, "y": 2496, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 1344, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 73,7 73,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"landingthruster_on.png": {
- "frame": { "x": 0, "y": 2688, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 1536, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 82,7 82,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"thruster_on.png": {
- "frame": { "x": 0, "y": 2880, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 1728, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 91,7 91,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"landingleg.png": {
- "frame": { "x": 0, "y": 3072, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 1920, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 100,7 100,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"autoplr_on.png": {
- "frame": { "x": 0, "y": 3264, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 2112, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 109,7 109,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"hub_on.png": {
- "frame": { "x": 0, "y": 3456, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 2304, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 118,7 118,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"powerhub_on.png": {
- "frame": { "x": 0, "y": 3648, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 2496, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 127,7 127,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"landingthruster_off.png": {
- "frame": { "x": 0, "y": 3840, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 2688, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 136,7 136,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"superthruster_off.png": {
- "frame": { "x": 192, "y": 1536, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 2880, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 145,7 145,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"thruster_off.png": {
- "frame": { "x": 192, "y": 1728, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 3072, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 154,7 154,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"cargo_on.png": {
- "frame": { "x": 192, "y": 1920, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 3264, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 163,7 163,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"cargo_off.png": {
- "frame": { "x": 192, "y": 2112, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 3456, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 172,7 172,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"powerhub_off.png": {
- "frame": { "x": 192, "y": 2304, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 3648, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
@@ 181,7 181,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
"hub_off.png": {
- "frame": { "x": 192, "y": 2496, "w": 192, "h": 192 },
+ "frame": { "x": 0, "y": 3840, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 192, "h": 192 },
M assets/dist/spritesheet-375.png => assets/dist/spritesheet-375.png +0 -0
M assets/dist/spritesheet-full.json => assets/dist/spritesheet-full.json +34 -34
@@ 1,25 1,25 @@
{
"frames": {
- "moon.png": {
- "frame": { "x": 0, "y": 0, "w": 2048, "h": 2048 },
+ "starfield.png": {
+ "frame": { "x": 0, "y": 0, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
- "spriteSourceSize": { "x": 0, "y": 0, "w": 2048, "h": 2048 },
- "sourceSize": { "w": 2048, "h": 2048 },
- "pivot": { "x": 1024, "y": 1024 },
- "9slicedFrame": { "x": 0, "y": 0, "w": 2048, "h": 2048 }
+ "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
+ "sourceSize": { "w": 512, "h": 512 },
+ "pivot": { "x": 256, "y": 256 },
+ "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
- "earth.png": {
- "frame": { "x": 0, "y": 2048, "w": 2048, "h": 2048 },
+ "moon.png": {
+ "frame": { "x": 0, "y": 512, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
- "spriteSourceSize": { "x": 0, "y": 0, "w": 2048, "h": 2048 },
- "sourceSize": { "w": 2048, "h": 2048 },
- "pivot": { "x": 1024, "y": 1024 },
- "9slicedFrame": { "x": 0, "y": 0, "w": 2048, "h": 2048 }
+ "spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
+ "sourceSize": { "w": 512, "h": 512 },
+ "pivot": { "x": 256, "y": 256 },
+ "9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
- "starfield.png": {
- "frame": { "x": 2048, "y": 0, "w": 512, "h": 512 },
+ "earth.png": {
+ "frame": { "x": 0, "y": 1024, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 27,8 27,8 @@
"pivot": { "x": 256, "y": 256 },
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
- "autoplr_cfg.png": {
- "frame": { "x": 2560, "y": 0, "w": 512, "h": 512 },
+ "autoplr_error.png": {
+ "frame": { "x": 0, "y": 1536, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 36,8 36,8 @@
"pivot": { "x": 256, "y": 256 },
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
- "autoplr_error.png": {
- "frame": { "x": 3072, "y": 0, "w": 512, "h": 512 },
+ "autoplr_cfg.png": {
+ "frame": { "x": 0, "y": 2048, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 46,7 46,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"hearty.png": {
- "frame": { "x": 3584, "y": 0, "w": 512, "h": 512 },
+ "frame": { "x": 0, "y": 2560, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 55,7 55,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"superthruster_on.png": {
- "frame": { "x": 2048, "y": 512, "w": 512, "h": 512 },
+ "frame": { "x": 0, "y": 3072, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 64,7 64,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"ecothruster_on.png": {
- "frame": { "x": 2560, "y": 512, "w": 512, "h": 512 },
+ "frame": { "x": 0, "y": 3584, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 73,7 73,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"landingthruster_on.png": {
- "frame": { "x": 3072, "y": 512, "w": 512, "h": 512 },
+ "frame": { "x": 512, "y": 0, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 82,7 82,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"thruster_on.png": {
- "frame": { "x": 3584, "y": 512, "w": 512, "h": 512 },
+ "frame": { "x": 1024, "y": 0, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 91,7 91,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"landingleg.png": {
- "frame": { "x": 2048, "y": 1024, "w": 512, "h": 512 },
+ "frame": { "x": 1536, "y": 0, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 100,7 100,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"autoplr_on.png": {
- "frame": { "x": 2560, "y": 1024, "w": 512, "h": 512 },
+ "frame": { "x": 2048, "y": 0, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 109,7 109,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"hub_on.png": {
- "frame": { "x": 3072, "y": 1024, "w": 512, "h": 512 },
+ "frame": { "x": 2560, "y": 0, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 118,7 118,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"powerhub_on.png": {
- "frame": { "x": 3584, "y": 1024, "w": 512, "h": 512 },
+ "frame": { "x": 3072, "y": 0, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 127,7 127,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"superthruster_off.png": {
- "frame": { "x": 2048, "y": 1536, "w": 512, "h": 512 },
+ "frame": { "x": 3584, "y": 0, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 136,7 136,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"landingthruster_off.png": {
- "frame": { "x": 2560, "y": 1536, "w": 512, "h": 512 },
+ "frame": { "x": 512, "y": 512, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 145,7 145,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"thruster_off.png": {
- "frame": { "x": 3072, "y": 1536, "w": 512, "h": 512 },
+ "frame": { "x": 512, "y": 1024, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 154,7 154,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"cargo_on.png": {
- "frame": { "x": 3584, "y": 1536, "w": 512, "h": 512 },
+ "frame": { "x": 512, "y": 1536, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 163,7 163,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"cargo_off.png": {
- "frame": { "x": 2048, "y": 2048, "w": 512, "h": 512 },
+ "frame": { "x": 512, "y": 2048, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 172,7 172,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"powerhub_off.png": {
- "frame": { "x": 2048, "y": 2560, "w": 512, "h": 512 },
+ "frame": { "x": 512, "y": 2560, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
@@ 181,7 181,7 @@
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
"hub_off.png": {
- "frame": { "x": 2048, "y": 3072, "w": 512, "h": 512 },
+ "frame": { "x": 512, "y": 3072, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
"spriteSourceSize": { "x": 0, "y": 0, "w": 512, "h": 512 },
M assets/dist/spritesheet-full.png => assets/dist/spritesheet-full.png +0 -0
M assets/final/125/earth.png => assets/final/125/earth.png +0 -0
M assets/final/125/moon.png => assets/final/125/moon.png +0 -0
M assets/final/375/earth.png => assets/final/375/earth.png +0 -0
M assets/final/375/moon.png => assets/final/375/moon.png +0 -0
M assets/final/full/earth.png => assets/final/full/earth.png +0 -0
M assets/final/full/moon.png => assets/final/full/moon.png +0 -0
R assets/src/autoplr_cfg.ink.svg => assets/src/autoplr_cfg.svg +0 -0
R assets/src/autoplr_error.ink.svg => assets/src/autoplr_error.svg +0 -0
R assets/src/autoplr_on.ink.svg => assets/src/autoplr_on.svg +0 -0
R assets/src/cargo_off.ink.svg => assets/src/cargo_off.svg +0 -0
R assets/src/cargo_on.ink.svg => assets/src/cargo_on.svg +0 -0
R assets/src/earth.ink.svg => assets/src/earth.svg +0 -0
R assets/src/ecothruster_on.ink.svg => assets/src/ecothruster_on.svg +0 -0
R assets/src/hearty.ink.svg => assets/src/hearty.svg +0 -0
R assets/src/hub_off.ink.svg => assets/src/hub_off.svg +0 -0
R assets/src/hub_on.ink.svg => assets/src/hub_on.svg +0 -0
R assets/src/landingleg.ink.svg => assets/src/landingleg.svg +0 -0
R assets/src/landingthruster_off.ink.svg => assets/src/landingthruster_off.svg +0 -0
R assets/src/landingthruster_on.ink.svg => assets/src/landingthruster_on.svg +0 -0
R assets/src/moon.ink.svg => assets/src/moon.svg +0 -0
R assets/src/powerhub_off.ink.svg => assets/src/powerhub_off.svg +0 -0
R assets/src/powerhub_on.ink.svg => assets/src/powerhub_on.svg +0 -0
R assets/src/starfield.ink.svg => assets/src/starfield.svg +0 -0
R assets/src/superthruster_off.ink.svg => assets/src/superthruster_off.svg +0 -0
R assets/src/superthruster_on.ink.svg => assets/src/superthruster_on.svg +0 -0
R assets/src/thruster_off.ink.svg => assets/src/thruster_off.svg +0 -0
R assets/src/thruster_on.ink.svg => assets/src/thruster_on.svg +0 -0
M client/package.json => client/package.json +1 -1
@@ 7,7 7,7 @@
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
- "protobuf": "mkdir -p ./src/protocol && protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=./src/protocol -I ../protocol/src/pbuf ../protocol/src/pbuf/starkingdoms-protocol.proto"
+ "protobuf": "mkdir -p ./src/protocol && protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=./src/protocol -I ../protocol/src/pbuf ../protocol/src/pbuf/starkingdoms-protocol.proto && sed -i 's/\\/\\* eslint-disable \\*\\//\\/\\/@ts-nocheck/' src/protocol/*.ts"
},
"devDependencies": {
"ts-proto": "^1.146.0",
M client/src/gateway.ts => client/src/gateway.ts +2 -2
@@ 74,7 74,7 @@ export async function gateway_connect(gateway_url: string, username: string): Pr
let handshake_start_msg;
if (global.can_beam_out) {
handshake_start_msg = MessageC2SHello.encode({
- version: 3,
+ version: 4,
requestedUsername: username,
nextState: State.Play,
user: window.localStorage.getItem("user")!,
@@ 82,7 82,7 @@ export async function gateway_connect(gateway_url: string, username: string): Pr
}).finish();
} else {
handshake_start_msg = MessageC2SHello.encode({
- version: 3,
+ version: 4,
requestedUsername: username,
nextState: State.Play,
// @ts-ignore
M client/src/index.ts => client/src/index.ts +70 -1
@@ 7,9 7,10 @@ import {
MessageC2SAuthenticateAndBeamOut,
MessageC2SAuthenticateAndBeamOut_packetInfo,
MessageC2SInput,
- MessageC2SInput_packetInfo
+ MessageC2SInput_packetInfo, MessageC2SMouseInput, MessageC2SMouseInput_packetInfo
} from "./protocol/message_c2s";
import {encode} from "./serde";
+import {InputType} from "./protocol/input";
logSetup();
const logger = new Logger("client");
@@ 110,6 111,71 @@ async function client_main(server: string, username: string, texture_quality: st
}
global.canvas.style.setProperty("background-image", `url("/assets/final/${texture_quality}/starfield.png")`);
+ let canvas = document.getElementById("canvas")!;
+
+ document.onmousedown = (e) => {
+ let canvasLeft = canvas.offsetLeft + canvas.clientLeft;
+ let canvasTop = canvas.offsetTop + canvas.clientTop;
+
+ let screenspaceX = e.pageX - canvasLeft;
+ let screenspaceY = e.pageY - canvasTop;
+
+ // convert screenspace to worldspace
+ if (global.me !== null) {
+ let worldX = screenspaceX + global.me?.x;
+ let worldY = screenspaceY + global.me?.y;
+
+ let button: InputType;
+ if (e.button == 0) {
+ button = InputType.Left;
+ } else if (e.button == 1) {
+ button = InputType.Middle;
+ } else if (e.button == 2) {
+ button = InputType.Right;
+ }
+
+ let msg = MessageC2SMouseInput.encode({
+ worldposX: worldX,
+ worldposY: worldY,
+ released: false,
+ button: button!
+ }).finish();
+
+ global.client?.socket.send(encode(MessageC2SMouseInput_packetInfo.type, msg))
+ }
+ }
+
+ document.onmouseup = (e) => {
+ let canvasLeft = canvas.offsetLeft + canvas.clientLeft;
+ let canvasTop = canvas.offsetTop + canvas.clientTop;
+
+ let screenspaceX = e.pageX - canvasLeft;
+ let screenspaceY = e.pageY - canvasTop;
+
+ // convert screenspace to worldspace
+ if (global.me !== null) {
+ let worldX = screenspaceX + global.me?.x;
+ let worldY = screenspaceY + global.me?.y;
+
+ let button: InputType;
+ if (e.button == 0) {
+ button = InputType.Left;
+ } else if (e.button == 1) {
+ button = InputType.Middle;
+ } else if (e.button == 2) {
+ button = InputType.Right;
+ }
+
+ let msg = MessageC2SMouseInput.encode({
+ worldposX: worldX,
+ worldposY: worldY,
+ released: true,
+ button: button!
+ }).finish();
+
+ global.client?.socket.send(encode(MessageC2SMouseInput_packetInfo.type, msg))
+ }
+ }
document.onkeydown = (e) => {
if (e.code == "ArrowLeft" || e.code == "KeyA") { // arrow-left
@@ 213,6 279,9 @@ async function client_main(server: string, username: string, texture_quality: st
global.context.save();
+ // x_{screen} = x_{world} - player_{x_{world}}
+ // x_{world} = x_{screen} + player_{x_{world}}
+
global.context.translate(module.x - global.me!.x, module.y - global.me!.y);
global.context.rotate(module.rotation);
M client/src/protocol/goodbye_reason.ts => client/src/protocol/goodbye_reason.ts +18 -12
@@ 1,35 1,39 @@
-/* eslint-disable */
+//@ts-nocheck
export const protobufPackage = "protocol.goodbye_reason";
export enum GoodbyeReason {
- UnsupportedProtocol = 0,
- UnexpectedPacket = 1,
- UnexpectedNextState = 2,
- UsernameTaken = 3,
- PingPongTimeout = 4,
- Done = 5,
+ UNKNOWN = 0,
+ UnsupportedProtocol = 1,
+ UnexpectedPacket = 2,
+ UnexpectedNextState = 3,
+ UsernameTaken = 4,
+ PingPongTimeout = 5,
+ Done = 6,
UNRECOGNIZED = -1,
}
export function goodbyeReasonFromJSON(object: any): GoodbyeReason {
switch (object) {
case 0:
+ case "UNKNOWN":
+ return GoodbyeReason.UNKNOWN;
+ case 1:
case "UnsupportedProtocol":
return GoodbyeReason.UnsupportedProtocol;
- case 1:
+ case 2:
case "UnexpectedPacket":
return GoodbyeReason.UnexpectedPacket;
- case 2:
+ case 3:
case "UnexpectedNextState":
return GoodbyeReason.UnexpectedNextState;
- case 3:
+ case 4:
case "UsernameTaken":
return GoodbyeReason.UsernameTaken;
- case 4:
+ case 5:
case "PingPongTimeout":
return GoodbyeReason.PingPongTimeout;
- case 5:
+ case 6:
case "Done":
return GoodbyeReason.Done;
case -1:
@@ 41,6 45,8 @@ export function goodbyeReasonFromJSON(object: any): GoodbyeReason {
export function goodbyeReasonToJSON(object: GoodbyeReason): string {
switch (object) {
+ case GoodbyeReason.UNKNOWN:
+ return "UNKNOWN";
case GoodbyeReason.UnsupportedProtocol:
return "UnsupportedProtocol";
case GoodbyeReason.UnexpectedPacket:
A client/src/protocol/input.ts => client/src/protocol/input.ts +48 -0
@@ 0,0 1,48 @@
+//@ts-nocheck
+
+export const protobufPackage = "protocol.input";
+
+export enum InputType {
+ UNKNOWN = 0,
+ Left = 1,
+ Middle = 2,
+ Right = 3,
+ UNRECOGNIZED = -1,
+}
+
+export function inputTypeFromJSON(object: any): InputType {
+ switch (object) {
+ case 0:
+ case "UNKNOWN":
+ return InputType.UNKNOWN;
+ case 1:
+ case "Left":
+ return InputType.Left;
+ case 2:
+ case "Middle":
+ return InputType.Middle;
+ case 3:
+ case "Right":
+ return InputType.Right;
+ case -1:
+ case "UNRECOGNIZED":
+ default:
+ return InputType.UNRECOGNIZED;
+ }
+}
+
+export function inputTypeToJSON(object: InputType): string {
+ switch (object) {
+ case InputType.UNKNOWN:
+ return "UNKNOWN";
+ case InputType.Left:
+ return "Left";
+ case InputType.Middle:
+ return "Middle";
+ case InputType.Right:
+ return "Right";
+ case InputType.UNRECOGNIZED:
+ default:
+ return "UNRECOGNIZED";
+ }
+}
M client/src/protocol/message_c2s.ts => client/src/protocol/message_c2s.ts +141 -4
@@ 1,7 1,7 @@
-// @ts-nocheck
-
+//@ts-nocheck
import * as _m0 from "protobufjs/minimal";
import { GoodbyeReason, goodbyeReasonFromJSON, goodbyeReasonToJSON } from "./goodbye_reason";
+import { InputType, inputTypeFromJSON, inputTypeToJSON } from "./input";
import { State, stateFromJSON, stateToJSON } from "./state";
export const protobufPackage = "protocol.message_c2s";
@@ 13,8 13,8 @@ export interface MessageC2SHello {
requestedUsername: string;
/** The state the connection will go into after the handshake. */
nextState: State;
- token?: string | undefined;
- user?: string | undefined;
+ token: string;
+ user: string;
}
export enum MessageC2SHello_packetInfo {
@@ 246,6 246,46 @@ export function messageC2SAuthenticateAndBeamOut_packetInfoToJSON(
}
}
+export interface MessageC2SMouseInput {
+ worldposX: number;
+ worldposY: number;
+ button: InputType;
+ released: boolean;
+}
+
+export enum MessageC2SMouseInput_packetInfo {
+ unknown = 0,
+ type = 13,
+ UNRECOGNIZED = -1,
+}
+
+export function messageC2SMouseInput_packetInfoFromJSON(object: any): MessageC2SMouseInput_packetInfo {
+ switch (object) {
+ case 0:
+ case "unknown":
+ return MessageC2SMouseInput_packetInfo.unknown;
+ case 13:
+ case "type":
+ return MessageC2SMouseInput_packetInfo.type;
+ case -1:
+ case "UNRECOGNIZED":
+ default:
+ return MessageC2SMouseInput_packetInfo.UNRECOGNIZED;
+ }
+}
+
+export function messageC2SMouseInput_packetInfoToJSON(object: MessageC2SMouseInput_packetInfo): string {
+ switch (object) {
+ case MessageC2SMouseInput_packetInfo.unknown:
+ return "unknown";
+ case MessageC2SMouseInput_packetInfo.type:
+ return "type";
+ case MessageC2SMouseInput_packetInfo.UNRECOGNIZED:
+ default:
+ return "UNRECOGNIZED";
+ }
+}
+
function createBaseMessageC2SHello(): MessageC2SHello {
return { version: 0, requestedUsername: "", nextState: 0, token: "", user: "" };
}
@@ 684,6 724,103 @@ export const MessageC2SAuthenticateAndBeamOut = {
},
};
+function createBaseMessageC2SMouseInput(): MessageC2SMouseInput {
+ return { worldposX: 0, worldposY: 0, button: 0, released: false };
+}
+
+export const MessageC2SMouseInput = {
+ encode(message: MessageC2SMouseInput, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
+ if (message.worldposX !== 0) {
+ writer.uint32(9).double(message.worldposX);
+ }
+ if (message.worldposY !== 0) {
+ writer.uint32(17).double(message.worldposY);
+ }
+ if (message.button !== 0) {
+ writer.uint32(24).int32(message.button);
+ }
+ if (message.released === true) {
+ writer.uint32(32).bool(message.released);
+ }
+ return writer;
+ },
+
+ decode(input: _m0.Reader | Uint8Array, length?: number): MessageC2SMouseInput {
+ const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
+ let end = length === undefined ? reader.len : reader.pos + length;
+ const message = createBaseMessageC2SMouseInput();
+ while (reader.pos < end) {
+ const tag = reader.uint32();
+ switch (tag >>> 3) {
+ case 1:
+ if (tag != 9) {
+ break;
+ }
+
+ message.worldposX = reader.double();
+ continue;
+ case 2:
+ if (tag != 17) {
+ break;
+ }
+
+ message.worldposY = reader.double();
+ continue;
+ case 3:
+ if (tag != 24) {
+ break;
+ }
+
+ message.button = reader.int32() as any;
+ continue;
+ case 4:
+ if (tag != 32) {
+ break;
+ }
+
+ message.released = reader.bool();
+ continue;
+ }
+ if ((tag & 7) == 4 || tag == 0) {
+ break;
+ }
+ reader.skipType(tag & 7);
+ }
+ return message;
+ },
+
+ fromJSON(object: any): MessageC2SMouseInput {
+ return {
+ worldposX: isSet(object.worldposX) ? Number(object.worldposX) : 0,
+ worldposY: isSet(object.worldposY) ? Number(object.worldposY) : 0,
+ button: isSet(object.button) ? inputTypeFromJSON(object.button) : 0,
+ released: isSet(object.released) ? Boolean(object.released) : false,
+ };
+ },
+
+ toJSON(message: MessageC2SMouseInput): unknown {
+ const obj: any = {};
+ message.worldposX !== undefined && (obj.worldposX = message.worldposX);
+ message.worldposY !== undefined && (obj.worldposY = message.worldposY);
+ message.button !== undefined && (obj.button = inputTypeToJSON(message.button));
+ message.released !== undefined && (obj.released = message.released);
+ return obj;
+ },
+
+ create<I extends Exact<DeepPartial<MessageC2SMouseInput>, I>>(base?: I): MessageC2SMouseInput {
+ return MessageC2SMouseInput.fromPartial(base ?? {});
+ },
+
+ fromPartial<I extends Exact<DeepPartial<MessageC2SMouseInput>, I>>(object: I): MessageC2SMouseInput {
+ const message = createBaseMessageC2SMouseInput();
+ message.worldposX = object.worldposX ?? 0;
+ message.worldposY = object.worldposY ?? 0;
+ message.button = object.button ?? 0;
+ message.released = object.released ?? false;
+ return message;
+ },
+};
+
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
export type DeepPartial<T> = T extends Builtin ? T
M client/src/protocol/message_s2c.ts => client/src/protocol/message_s2c.ts +1 -1
@@ 1,4 1,4 @@
-/* eslint-disable */
+//@ts-nocheck
import * as _m0 from "protobufjs/minimal";
import { GoodbyeReason, goodbyeReasonFromJSON, goodbyeReasonToJSON } from "./goodbye_reason";
import { Module } from "./module";
M client/src/protocol/module.ts => client/src/protocol/module.ts +21 -15
@@ 1,24 1,28 @@
-/* eslint-disable */
+//@ts-nocheck
import * as _m0 from "protobufjs/minimal";
export const protobufPackage = "protocol.module";
export enum ModuleType {
- Cargo = 0,
- LandingThruster = 1,
- Hub = 2,
+ UNKNOWN = 0,
+ Cargo = 1,
+ LandingThruster = 2,
+ Hub = 3,
UNRECOGNIZED = -1,
}
export function moduleTypeFromJSON(object: any): ModuleType {
switch (object) {
case 0:
+ case "UNKNOWN":
+ return ModuleType.UNKNOWN;
+ case 1:
case "Cargo":
return ModuleType.Cargo;
- case 1:
+ case 2:
case "LandingThruster":
return ModuleType.LandingThruster;
- case 2:
+ case 3:
case "Hub":
return ModuleType.Hub;
case -1:
@@ 30,6 34,8 @@ export function moduleTypeFromJSON(object: any): ModuleType {
export function moduleTypeToJSON(object: ModuleType): string {
switch (object) {
+ case ModuleType.UNKNOWN:
+ return "UNKNOWN";
case ModuleType.Cargo:
return "Cargo";
case ModuleType.LandingThruster:
@@ 59,13 65,13 @@ export const Module = {
writer.uint32(8).int32(message.moduleType);
}
if (message.rotation !== 0) {
- writer.uint32(21).float(message.rotation);
+ writer.uint32(17).double(message.rotation);
}
if (message.x !== 0) {
- writer.uint32(29).float(message.x);
+ writer.uint32(25).double(message.x);
}
if (message.y !== 0) {
- writer.uint32(37).float(message.y);
+ writer.uint32(33).double(message.y);
}
return writer;
},
@@ 85,25 91,25 @@ export const Module = {
message.moduleType = reader.int32() as any;
continue;
case 2:
- if (tag != 21) {
+ if (tag != 17) {
break;
}
- message.rotation = reader.float();
+ message.rotation = reader.double();
continue;
case 3:
- if (tag != 29) {
+ if (tag != 25) {
break;
}
- message.x = reader.float();
+ message.x = reader.double();
continue;
case 4:
- if (tag != 37) {
+ if (tag != 33) {
break;
}
- message.y = reader.float();
+ message.y = reader.double();
continue;
}
if ((tag & 7) == 4 || tag == 0) {
M client/src/protocol/planet.ts => client/src/protocol/planet.ts +19 -13
@@ 1,20 1,24 @@
-/* eslint-disable */
+//@ts-nocheck
import * as _m0 from "protobufjs/minimal";
export const protobufPackage = "protocol.planet";
export enum PlanetType {
- Earth = 0,
- Moon = 1,
+ UNKNOWN = 0,
+ Earth = 1,
+ Moon = 2,
UNRECOGNIZED = -1,
}
export function planetTypeFromJSON(object: any): PlanetType {
switch (object) {
case 0:
+ case "UNKNOWN":
+ return PlanetType.UNKNOWN;
+ case 1:
case "Earth":
return PlanetType.Earth;
- case 1:
+ case 2:
case "Moon":
return PlanetType.Moon;
case -1:
@@ 26,6 30,8 @@ export function planetTypeFromJSON(object: any): PlanetType {
export function planetTypeToJSON(object: PlanetType): string {
switch (object) {
+ case PlanetType.UNKNOWN:
+ return "UNKNOWN";
case PlanetType.Earth:
return "Earth";
case PlanetType.Moon:
@@ 57,13 63,13 @@ export const Planet = {
writer.uint32(8).int32(message.planetType);
}
if (message.x !== 0) {
- writer.uint32(21).float(message.x);
+ writer.uint32(17).double(message.x);
}
if (message.y !== 0) {
- writer.uint32(29).float(message.y);
+ writer.uint32(25).double(message.y);
}
if (message.radius !== 0) {
- writer.uint32(37).float(message.radius);
+ writer.uint32(33).double(message.radius);
}
return writer;
},
@@ 83,25 89,25 @@ export const Planet = {
message.planetType = reader.int32() as any;
continue;
case 2:
- if (tag != 21) {
+ if (tag != 17) {
break;
}
- message.x = reader.float();
+ message.x = reader.double();
continue;
case 3:
- if (tag != 29) {
+ if (tag != 25) {
break;
}
- message.y = reader.float();
+ message.y = reader.double();
continue;
case 4:
- if (tag != 37) {
+ if (tag != 33) {
break;
}
- message.radius = reader.float();
+ message.radius = reader.double();
continue;
}
if ((tag & 7) == 4 || tag == 0) {
M client/src/protocol/player.ts => client/src/protocol/player.ts +10 -10
@@ 1,4 1,4 @@
-/* eslint-disable */
+//@ts-nocheck
import * as _m0 from "protobufjs/minimal";
export const protobufPackage = "protocol.player";
@@ 21,13 21,13 @@ function createBasePlayer(): Player {
export const Player = {
encode(message: Player, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.rotation !== 0) {
- writer.uint32(13).float(message.rotation);
+ writer.uint32(9).double(message.rotation);
}
if (message.x !== 0) {
- writer.uint32(21).float(message.x);
+ writer.uint32(17).double(message.x);
}
if (message.y !== 0) {
- writer.uint32(29).float(message.y);
+ writer.uint32(25).double(message.y);
}
if (message.username !== "") {
writer.uint32(34).string(message.username);
@@ 43,25 43,25 @@ export const Player = {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1:
- if (tag != 13) {
+ if (tag != 9) {
break;
}
- message.rotation = reader.float();
+ message.rotation = reader.double();
continue;
case 2:
- if (tag != 21) {
+ if (tag != 17) {
break;
}
- message.x = reader.float();
+ message.x = reader.double();
continue;
case 3:
- if (tag != 29) {
+ if (tag != 25) {
break;
}
- message.y = reader.float();
+ message.y = reader.double();
continue;
case 4:
if (tag != 34) {
M client/src/protocol/starkingdoms-protocol.ts => client/src/protocol/starkingdoms-protocol.ts +1 -1
@@ 1,4 1,4 @@
-// @ts-nocheck
+//@ts-nocheck
import * as Long from "long";
import * as _m0 from "protobufjs/minimal";
M client/src/protocol/state.ts => client/src/protocol/state.ts +10 -4
@@ 1,19 1,23 @@
-/* eslint-disable */
+//@ts-nocheck
export const protobufPackage = "protocol.state";
export enum State {
- Handshake = 0,
- Play = 1,
+ UNKNOWN = 0,
+ Handshake = 1,
+ Play = 2,
UNRECOGNIZED = -1,
}
export function stateFromJSON(object: any): State {
switch (object) {
case 0:
+ case "UNKNOWN":
+ return State.UNKNOWN;
+ case 1:
case "Handshake":
return State.Handshake;
- case 1:
+ case 2:
case "Play":
return State.Play;
case -1:
@@ 25,6 29,8 @@ export function stateFromJSON(object: any): State {
export function stateToJSON(object: State): string {
switch (object) {
+ case State.UNKNOWN:
+ return "UNKNOWN";
case State.Handshake:
return "Handshake";
case State.Play:
M client/vite.config.ts => client/vite.config.ts +1 -0
@@ 3,6 3,7 @@ import { defineConfig } from "vite";
export default defineConfig({
build: {
lib: {
+ formats: ["es"],
entry: {
play: "play.html",
index: "index.html",
A docker/README.md => docker/README.md +1 -0
@@ 0,0 1,1 @@
+These files contain essential configuration files used by Ansible to manage and deploy StarKingdoms servers. **DO NOT TOUCH THESE FILES.**<
\ No newline at end of file
M protocol/build.rs => protocol/build.rs +1 -0
@@ 10,5 10,6 @@ fn main() {
.input("src/pbuf/state.proto")
.input("src/pbuf/goodbye_reason.proto")
.input("src/pbuf/module.proto")
+ .input("src/pbuf/input.proto")
.run_from_script();
}
M protocol/src/api.rs => protocol/src/api.rs +1 -3
@@ 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 {}
M protocol/src/legacy.rs => protocol/src/legacy.rs +14 -15
@@ 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<ProtocolPlayer>
+ players: Vec<ProtocolPlayer>,
},
-
PlanetData {
- planets: Vec<ProtocolPlanet>
- }
+ planets: Vec<ProtocolPlanet>,
+ },
}
#[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(),
}
}
}
M protocol/src/lib.rs => protocol/src/lib.rs +116 -67
@@ 1,12 1,18 @@
-use std::error::Error;
-use protobuf::{Enum, Message};
-use crate::message_c2s::{MessageC2SAuthenticateAndBeamOut, MessageC2SChat, MessageC2SGoodbye, MessageC2SHello, MessageC2SInput, 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 = 3;
+pub const PROTOCOL_VERSION: u32 = 4;
pub mod api;
@@ 17,7 23,8 @@ pub enum MessageC2S {
Chat(MessageC2SChat),
Ping(MessageC2SPing),
Input(MessageC2SInput),
- AuthenticateAndBeamOut(MessageC2SAuthenticateAndBeamOut)
+ AuthenticateAndBeamOut(MessageC2SAuthenticateAndBeamOut),
+ MouseInput(MessageC2SMouseInput),
}
#[derive(Debug)]
@@ 40,23 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 => { return Err(format!("Unrecognized C2S packet {}", _id).into()); }
+ _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());
+ }
};
Ok(deser_pkt)
@@ 68,24 87,34 @@ impl TryInto<Vec<u8>> for MessageC2S {
fn try_into(self) -> Result<Vec<u8>, 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::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 {
@@ 107,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)
@@ 138,27 179,34 @@ impl TryInto<Vec<u8>> for MessageS2C {
fn try_into(self) -> Result<Vec<u8>, 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 {
@@ 175,7 223,8 @@ impl planet::PlanetType {
pub fn as_texture_id(&self) -> String {
match self {
PlanetType::Earth => "earth".to_string(),
- PlanetType::Moon => "moon".to_string()
+ PlanetType::Moon => "moon".to_string(),
+ PlanetType::UNKNOWN => "missing".to_string(),
}
}
}
M protocol/src/pbuf/goodbye_reason.proto => protocol/src/pbuf/goodbye_reason.proto +7 -6
@@ 2,10 2,11 @@ syntax = "proto3";
package protocol.goodbye_reason;
enum GoodbyeReason {
- UnsupportedProtocol = 0;
- UnexpectedPacket = 1;
- UnexpectedNextState = 2;
- UsernameTaken = 3;
- PingPongTimeout = 4;
- Done = 5;
+ UNKNOWN = 0;
+ UnsupportedProtocol = 1;
+ UnexpectedPacket = 2;
+ UnexpectedNextState = 3;
+ UsernameTaken = 4;
+ PingPongTimeout = 5;
+ Done = 6;
}=
\ No newline at end of file
A protocol/src/pbuf/input.proto => protocol/src/pbuf/input.proto +9 -0
@@ 0,0 1,9 @@
+syntax = "proto3";
+package protocol.input;
+
+enum InputType {
+ UNKNOWN = 0;
+ Left = 1;
+ Middle = 2;
+ Right = 3;
+}<
\ No newline at end of file
M protocol/src/pbuf/message_c2s.proto => protocol/src/pbuf/message_c2s.proto +12 -0
@@ 3,6 3,7 @@ package protocol.message_c2s;
import "state.proto";
import "goodbye_reason.proto";
+import "input.proto";
message MessageC2SHello {
enum packet_info { unknown = 0; type = 0x01; }
@@ 38,6 39,8 @@ message MessageC2SInput {
bool down_pressed = 2;
bool left_pressed = 3;
bool right_pressed = 4;
+
+
}
message MessageC2SAuthenticateAndBeamOut {
@@ 45,4 48,13 @@ message MessageC2SAuthenticateAndBeamOut {
string user_id = 1; // The user ID that the client is authenticating as
string token = 2; // The token from the authentication server that the user is authenticating as
+}
+
+message MessageC2SMouseInput {
+ enum packet_info { unknown = 0; type = 0x0d; }
+
+ double worldpos_x = 1;
+ double worldpos_y = 2;
+ protocol.input.InputType button = 3;
+ bool released = 4;
}=
\ No newline at end of file
M protocol/src/pbuf/module.proto => protocol/src/pbuf/module.proto +7 -6
@@ 3,13 3,14 @@ package protocol.module;
message Module {
ModuleType module_type = 1;
- float rotation = 2;
- float x = 3;
- float y = 4;
+ double rotation = 2;
+ double x = 3;
+ double y = 4;
}
enum ModuleType {
- Cargo = 0;
- LandingThruster = 1;
- Hub = 2;
+ UNKNOWN = 0;
+ Cargo = 1;
+ LandingThruster = 2;
+ Hub = 3;
}
M protocol/src/pbuf/planet.proto => protocol/src/pbuf/planet.proto +6 -5
@@ 3,12 3,13 @@ package protocol.planet;
message Planet {
PlanetType planet_type = 1; // Type of the planet
- float x = 2; // Translation on the X axis, in game units
- float y = 3; // Translation on the Y axis, in game units
- float radius = 4; // The radius of the planet extending out from (x, y)
+ double x = 2; // Translation on the X axis, in game units
+ double y = 3; // Translation on the Y axis, in game units
+ double radius = 4; // The radius of the planet extending out from (x, y)
}
enum PlanetType {
- Earth = 0;
- Moon = 1;
+ UNKNOWN = 0;
+ Earth = 1;
+ Moon = 2;
}=
\ No newline at end of file
M protocol/src/pbuf/player.proto => protocol/src/pbuf/player.proto +3 -3
@@ 2,8 2,8 @@ syntax = "proto3";
package protocol.player;
message Player {
- float rotation = 1; // The rotation, clockwise, in degrees, of the player
- float x = 2; // The translation on the X axis, in game units, of the player
- float y = 3; // The translation on the Y axis, in game units, of the player
+ double rotation = 1; // The rotation, clockwise, in degrees, of the player
+ double x = 2; // The translation on the X axis, in game units, of the player
+ double y = 3; // The translation on the Y axis, in game units, of the player
string username = 4; // The username of the player
}=
\ No newline at end of file
M protocol/src/pbuf/starkingdoms-protocol.proto => protocol/src/pbuf/starkingdoms-protocol.proto +1 -0
@@ 7,6 7,7 @@ import public "goodbye_reason.proto";
import public "message_s2c.proto";
import public "player.proto";
import public "planet.proto";
+import public "input.proto";
message PacketWrapper {
int64 packet_id = 1; // What is the Packet ID of this packet?
M protocol/src/pbuf/state.proto => protocol/src/pbuf/state.proto +3 -2
@@ 2,6 2,7 @@ syntax = "proto3";
package protocol.state;
enum State {
- Handshake = 0;
- Play = 1;
+ UNKNOWN = 0;
+ Handshake = 1;
+ Play = 2;
}=
\ No newline at end of file
M server/build.rs => server/build.rs +19 -6
@@ 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
+}
M server/src/api.rs => server/src/api.rs +44 -17
@@ 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<APISavedPlayerData, Box<dyn Error>> {
+pub async fn load_player_data_from_api(
+ token: &str,
+ user_id: &str,
+ internal_token: &str,
+) -> Result<APISavedPlayerData, Box<dyn Error>> {
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<dyn Error + Send + Sync>> {
+pub async fn save_player_data_to_api(
+ data: &APISavedPlayerData,
+ token: &str,
+ user_id: &str,
+ internal_token: &str,
+) -> Result<(), Box<dyn Error + Send + Sync>> {
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
+}
M server/src/entity.rs => server/src/entity.rs +22 -24
@@ 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<EntityId, Entity>;
@@ 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<Planet> {
@@ 58,12 64,11 @@ impl EntityHandler {
players
}
pub fn get_player_from_id(&self, id: EntityId) -> Option<Player> {
- if let Some(entity) = self.entities.get(&id) {
- if let Entity::Player(player) = entity {
- return Some(player.clone());
- }
+ if let Some(Entity::Player(player)) = self.entities.get(&id) {
+ Some(player.clone())
+ } else {
+ None
}
- None
}
pub fn get_player_id(&self, addr: SocketAddr) -> Option<EntityId> {
for (id, entity) in self.entities.iter() {
@@ 106,10 111,8 @@ impl EntityHandler {
module_count
}
pub fn get_module_from_id(&self, id: EntityId) -> Option<Module> {
- if let Some(entity) = self.entities.get(&id) {
- if let Entity::Module(module) = entity {
- return Some(module.clone());
- }
+ if let Some(Entity::Module(module)) = self.entities.get(&id) {
+ return Some(module.clone());
}
None
}
@@ 133,10 136,8 @@ impl EntityHandler {
modules
}
pub fn get_attached_from_id(&self, id: EntityId) -> Option<AttachedModule> {
- if let Some(entity) = self.entities.get(&id) {
- if let Entity::AttachedModule(module) = entity {
- return Some(module.clone());
- }
+ if let Some(Entity::AttachedModule(module)) = self.entities.get(&id) {
+ return Some(module.clone());
}
None
}
@@ 156,19 157,16 @@ impl EntityHandler {
let mut planets = vec![];
for planet in self.get_planets() {
- // TODO: Adjust codegen to use f64
planets.push(starkingdoms_protocol::planet::Planet {
planet_type: planet.planet_type.into(),
- x: (planet.position.0 * SCALE) as f32,
- y: (planet.position.1 * SCALE) as f32,
- radius: planet.radius as f32, // DO NOT * SCALE
+ x: planet.position.0 * SCALE,
+ y: planet.position.1 * SCALE,
+ radius: planet.radius, // DO NOT * SCALE. THIS VALUE IS NOT SCALED!
special_fields: Default::default(),
});
}
- ClientHandlerMessage::PlanetData {
- planets
- }
+ ClientHandlerMessage::PlanetData { planets }
}
}
M server/src/handler.rs => server/src/handler.rs +241 -124
@@ 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<RwLock<EntityHandler>>, data: Arc<RwLock<PhysicsData>>,
- remote_addr: SocketAddr, rx: Receiver<ClientHandlerMessage>,
- mut client_tx: SplitSink<WebSocketStream<TcpStream>, Message>, mut client_rx: SplitStream<WebSocketStream<TcpStream>>
- ) -> Result<(), Box<dyn Error>> {
+pub async fn handle_client(
+ mgr: ClientManager,
+ entities: Arc<RwLock<EntityHandler>>,
+ data: Arc<RwLock<PhysicsData>>,
+ remote_addr: SocketAddr,
+ rx: Receiver<ClientHandlerMessage>,
+ mut client_tx: SplitSink<WebSocketStream<TcpStream>, Message>,
+ mut client_rx: SplitStream<WebSocketStream<TcpStream>>,
+) -> Result<(), Box<dyn Error>> {
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<RwLock<EntityHandle
from,
message,
special_fields: Default::default(),
- }).try_into()?;
+ })
+ .try_into()?;
send!(client_tx, msg).await?;
}
}
@@ 50,7 63,8 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc<RwLock<EntityHandle
let msg = MessageS2C::PlayersUpdate(MessageS2CPlayersUpdate {
players,
special_fields: Default::default(),
- }).try_into()?;
+ })
+ .try_into()?;
send!(client_tx, msg).await?;
}
}
@@ 59,7 73,8 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc<RwLock<EntityHandle
let msg = MessageS2C::PlanetData(MessageS2CPlanetData {
planets,
special_fields: Default::default(),
- }).try_into()?;
+ })
+ .try_into()?;
send!(client_tx, msg).await?;
}
}
@@ 68,7 83,8 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc<RwLock<EntityHandle
let msg = MessageS2C::ModulesUpdate(MessageS2CModulesUpdate {
modules,
special_fields: Default::default(),
- }).try_into()?;
+ })
+ .try_into()?;
send!(client_tx, msg).await?;
}
}
@@ 83,46 99,66 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc<RwLock<EntityHandle
let msg = MessageS2C::Goodbye(MessageS2CGoodbye {
reason: GoodbyeReason::PingPongTimeout.into(),
special_fields: Default::default(),
- }).try_into()?;
+ })
+ .try_into()?;
send!(client_tx, msg).await?;
break;
}
if let Some(pkt) = recv!(client_rx)? {
match state {
+ State::UNKNOWN => unreachable!(),
State::Handshake => {
match pkt {
MessageC2S::Hello(pkt) => {
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<u8> = MessageS2C::Goodbye(MessageS2CGoodbye {
reason: GoodbyeReason::UsernameTaken.into(),
special_fields: Default::default(),
- }).try_into()?;
+ })
+ .try_into()?;
send!(client_tx, msg).await?;
break;
}
@@ 130,7 166,10 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc<RwLock<EntityHandle
// username is fine
{
- mgr.usernames.write().await.insert(remote_addr, pkt.requested_username.clone());
+ mgr.usernames
+ .write()
+ .await
+ .insert(remote_addr, pkt.requested_username.clone());
}
let msg = MessageS2C::Hello(MessageS2CHello {
@@ 138,7 177,8 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc<RwLock<EntityHandle
given_username: pkt.requested_username.clone(),
special_fields: Default::default(),
next_state: pkt.next_state,
- }).try_into()?;
+ })
+ .try_into()?;
send!(client_tx, msg).await?;
@@ 157,16 197,27 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc<RwLock<EntityHandle
rng.gen::<f64>() * 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,
@@ 182,125 233,191 @@ pub async fn handle_client(mgr: ClientManager, entities: Arc<RwLock<EntityHandle
if !pkt.user.is_empty() && !pkt.token.is_empty() {
player.auth_token = Some(pkt.token.clone());
player.auth_user = Some(pkt.user.clone());
- info!("[{}] * Beamin: beaming in {} as {} with token {}", remote_addr, username, pkt.user, pkt.token);
+ info!(
+ "[{}] * Beamin: beaming in {} as {} with token {}",
+ remote_addr, username, pkt.user, pkt.token
+ );
- let player_data = match load_player_data_from_api(&pkt.token, &pkt.user, &std::env::var("STK_API_KEY").unwrap()).await {
+ let player_data = match load_player_data_from_api(
+ &pkt.token,
+ &pkt.user,
+ &std::env::var("STK_API_KEY").unwrap(),
+ )
+ .await
+ {
Ok(d) => 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);
+ }
+ },
}
}
}
M server/src/macros.rs => server/src/macros.rs +39 -43
@@ 13,49 13,13 @@ pub fn _generic_pkt_into(p: Vec<u8>) -> 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")
+ }
+ }};
+}
M server/src/main.rs => server/src/main.rs +92 -39
@@ 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<RwLock<EntityHandler>>, physics_data: Arc<RwLock<PhysicsData>>) {
+async fn handle_request(
+ conn: TcpStream,
+ remote_addr: SocketAddr,
+ mgr: ClientManager,
+ entities: Arc<RwLock<EntityHandler>>,
+ physics_data: Arc<RwLock<PhysicsData>>,
+) {
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<RwLock<EntityHandler>>, physics_data: Arc<RwLock<PhysicsData>>) -> Result<(), Box<dyn Error>> {
+async fn _handle_request(
+ mut conn: TcpStream,
+ remote_addr: SocketAddr,
+ mgr: ClientManager,
+ entities: Arc<RwLock<EntityHandler>>,
+ physics_data: Arc<RwLock<PhysicsData>>,
+) -> Result<(), Box<dyn Error>> {
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::<async_tungstenite::tungstenite::error::Error>() => {
- let e = e.downcast::<async_tungstenite::tungstenite::error::Error>().unwrap();
+ let e = e
+ .downcast::<async_tungstenite::tungstenite::error::Error>()
+ .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<RwLock<PhysicsData>> = Arc::new(RwLock::new(PhysicsData {
+ static ref DATA: Arc<RwLock<PhysicsData>> = 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<RwLock<EntityHandler>> = 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,
}
M server/src/manager.rs => server/src/manager.rs +124 -82
@@ 1,17 1,21 @@
-use std::collections::HashMap;
-use std::f64::consts::PI;
-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, PrismaticJointBuilder};
-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 {
@@ 34,16 38,14 @@ 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<AttachedModule> {
let mut modules = Vec::new();
- for child in &self.children {
- if let Some(attachment) = child {
- if let Entity::AttachedModule(child_module) = entities.entities.get(&attachment.child).unwrap() {
- modules.append(&mut child_module.search_modules(entities));
- }
+ for attachment in self.children.iter().flatten() {
+ if let Entity::AttachedModule(child_module) =
+ entities.entities.get(&attachment.child).unwrap()
+ {
+ modules.append(&mut child_module.search_modules(entities));
}
}
modules
@@ 73,26 75,35 @@ pub struct AttachedModule {
pub children: [Option<Attachment>; 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 mut 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");
}
};
- let parent_body = data.rigid_body_set
- .get(parent_handle).unwrap();
+ //let parent_body = data.rigid_body_set
+ // .get(parent_handle).unwrap();
// create attachment module
let module_collider = ColliderBuilder::cuboid(25.0 / SCALE, 25.0 / SCALE)
.mass_properties(loose_body.mass_properties().local_mprops)
@@ 102,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,
@@ 121,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 mut 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");
}
@@ 168,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 = PrismaticJointBuilder::new(Vector2::x_axis())
.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,
@@ 192,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");
}
@@ 214,14 245,14 @@ impl AttachedModule {
lifetime: 10.,
}
}
+
pub fn search_modules(&self, entities: &EntityHandler) -> Vec<AttachedModule> {
let mut modules = Vec::new();
- for child in &self.children {
- if let Some(attachment) = child {
- let 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));
- }
+ for attachment in self.children.iter().flatten() {
+ if let Entity::AttachedModule(child_module) =
+ entities.entities.get(&attachment.child).unwrap()
+ {
+ modules.append(&mut child_module.search_modules(entities));
}
}
modules
@@ 239,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<ClientHandlerMessage>
+ pub tx: Sender<ClientHandlerMessage>,
}
#[derive(Clone, Default)]
pub struct PhysicsData {
pub gravity: Vector2<f64>,
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,
@@ 261,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<starkingdoms_protocol::player::Player> },
- PlanetData { planets: Vec<starkingdoms_protocol::planet::Planet> },
- ModulesUpdate { modules: Vec<starkingdoms_protocol::module::Module> },
+ ChatMessage {
+ from: String,
+ message: String,
+ },
+ PlayersUpdate {
+ players: Vec<starkingdoms_protocol::player::Player>,
+ },
+ PlanetData {
+ planets: Vec<starkingdoms_protocol::planet::Planet>,
+ },
+ ModulesUpdate {
+ modules: Vec<starkingdoms_protocol::module::Module>,
+ },
}
M server/src/orbit/constants.rs => server/src/orbit/constants.rs +2 -2
@@ 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;
M server/src/orbit/kepler.rs => server/src/orbit/kepler.rs +1 -1
@@ 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
+}
M server/src/orbit/mod.rs => server/src/orbit/mod.rs +3 -3
@@ 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;
M server/src/orbit/newtonian.rs => server/src/orbit/newtonian.rs +8 -3
@@ 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
+}
M server/src/orbit/orbit.rs => server/src/orbit/orbit.rs +23 -6
@@ 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<f64> {
+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<f64> {
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<f64>, orbiting_on: Vector2<f64>) -> Vector2<f64> {
+pub fn calculate_world_position_of_orbit(
+ point: Vector2<f64>,
+ orbiting_on: Vector2<f64>,
+) -> Vector2<f64> {
// 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
+}
M server/src/orbit/vis_viva.rs => server/src/orbit/vis_viva.rs +7 -2
@@ 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
+}
M server/src/planet.rs => server/src/planet.rs +54 -34
@@ 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<EntityId> {
+ pub async fn create_planets(
+ rigid_body_set: &mut RigidBodySet,
+ collider_set: &mut ColliderSet,
+ entities: &mut Entities,
+ ) -> Vec<EntityId> {
let mut planet_ids: Vec<EntityId> = 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
@@ 111,16 133,14 @@ impl Planets {
// TODO: Adjust codegen to use f64
planets.push(starkingdoms_protocol::planet::Planet {
planet_type: planet.planet_type.into(),
- x: (planet.position.0 * SCALE) as f32,
- y: (planet.position.1 * SCALE) as f32,
- radius: planet.radius as f32, // DO NOT * SCALE
+ x: planet.position.0 * SCALE,
+ y: planet.position.1 * SCALE,
+ radius: planet.radius, // DO NOT * SCALE - THIS VALUE IS NOT SCALED!
special_fields: Default::default(),
});
}
- ClientHandlerMessage::PlanetData {
- planets
- }
+ ClientHandlerMessage::PlanetData { planets }
}
pub fn gravity(&self, position: (f64, f64), mass: f64) -> (f64, f64) {
M server/src/timer.rs => server/src/timer.rs +134 -67
@@ 1,22 1,35 @@
-use std::{time::Duration, sync::Arc, f64::consts::PI};
-use log::{debug, 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<RwLock<PhysicsData>>, entities: Arc<RwLock<EntityHandler>>) {
+pub async fn timer_main(
+ mgr: ClientManager,
+ physics_data_orig: Arc<RwLock<PhysicsData>>,
+ entities: Arc<RwLock<EntityHandler>>,
+) {
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<RwLock<Physic
let mut rigid_body_set = data_handle.rigid_body_set.clone();
let mut collider_set = data_handle.collider_set.clone();
- _planet_ids = Planets::create_planets(&mut rigid_body_set, &mut collider_set, &mut entities.write().await.entities).await;
+ _planet_ids = Planets::create_planets(
+ &mut rigid_body_set,
+ &mut collider_set,
+ &mut entities.write().await.entities,
+ )
+ .await;
data_handle.rigid_body_set = rigid_body_set;
data_handle.collider_set = collider_set;
@@ 55,10 73,19 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc<RwLock<Physic
// update moon
let moon: &mut Planet = &mut planets.get_planet(PlanetType::Moon).unwrap();
- let new_moon_position = calculate_world_position_of_orbit(calculate_point_on_orbit(MOON_PERIAPSIS, MOON_APOAPSIS, time / MOON_ORBIT_TIME), new_earth_position);
- let moon_body = physics_data.rigid_body_set.get_mut(moon.body_handle).unwrap();
+ let new_moon_position = calculate_world_position_of_orbit(
+ calculate_point_on_orbit(MOON_PERIAPSIS, MOON_APOAPSIS, time / MOON_ORBIT_TIME),
+ new_earth_position,
+ );
+ let moon_body = physics_data
+ .rigid_body_set
+ .get_mut(moon.body_handle)
+ .unwrap();
moon_body.set_next_kinematic_position(new_moon_position.into());
- moon.position = (moon_body.translation()[0] / SCALE, moon_body.translation()[1] / SCALE);
+ moon.position = (
+ moon_body.translation()[0] / SCALE,
+ moon_body.translation()[1] / SCALE,
+ );
}
physics_data.tick(&mut pipeline);
@@ 66,7 93,8 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc<RwLock<Physic
let mut protocol_players = vec![];
{
- if module_timer > 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<RwLock<Physic
rng.gen::<f64>() * 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,19 129,26 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc<RwLock<Physic
module_type: ModuleType::Cargo,
lifetime: 0.0,
};
- entities.write().await.entities.insert(get_entity_id(), Entity::Module(module));
+ entities
+ .write()
+ .await
+ .entities
+ .insert(get_entity_id(), Entity::Module(module));
}
let mut entities = entities.write().await;
- for mut module in entities.get_modules().iter_mut() {
+ for module in entities.get_modules().iter_mut() {
let module_handle = module.handle;
let module_body = physics_data.rigid_body_set.get_mut(module_handle).unwrap();
module_body.reset_forces(true);
module_body.reset_torques(true);
- let grav_force = entities.gravity((module_body.translation().x, module_body.translation().y), module_body.mass());
- //module_body.apply_impulse(vector![grav_force.0, grav_force.1], true);
+ let grav_force = entities.gravity(
+ (module_body.translation().x, module_body.translation().y),
+ module_body.mass(),
+ );
+ module_body.apply_impulse(vector![grav_force.0, grav_force.1], true);
let id = entities.get_from_module(module).unwrap();
if let Entity::Module(p_module) = entities.entities.get_mut(&id).unwrap() {
- p_module.lifetime += 5./1000.;
+ p_module.lifetime += 5. / 1000.;
}
if module.lifetime > 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<RwLock<Physic
let mut collider_set = physics_data.collider_set.clone();
let mut impulse_joint_set = physics_data.impulse_joint_set.clone();
let mut multibody_joint_set = physics_data.multibody_joint_set.clone();
- rigid_body_set.remove(module.handle, &mut island_manager, &mut collider_set,
- &mut impulse_joint_set, &mut multibody_joint_set, true);
+ rigid_body_set.remove(
+ module.handle,
+ &mut island_manager,
+ &mut collider_set,
+ &mut impulse_joint_set,
+ &mut multibody_joint_set,
+ true,
+ );
physics_data.rigid_body_set = rigid_body_set;
physics_data.collider_set = collider_set;
physics_data.island_manager = island_manager;
@@ 133,8 181,11 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc<RwLock<Physic
player_body.reset_forces(true);
player_body.reset_torques(true);
let planets = entities.read().await;
- let grav_force = planets.gravity((player_body.translation().x, player_body.translation().y), player_body.mass());
- //player_body.apply_impulse(vector![grav_force.0, grav_force.1], true);
+ let grav_force = planets.gravity(
+ (player_body.translation().x, player_body.translation().y),
+ player_body.mass(),
+ );
+ player_body.apply_impulse(vector![grav_force.0, grav_force.1], true);
let mut left_top_thruster: f64 = 0.0;
let mut right_top_thruster: f64 = 0.0;
@@ 182,35 233,26 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc<RwLock<Physic
];
let scale = SCALE;
let top_left_point = point![
- -25. / scale * rotation.cos() +25. / scale * rotation.sin(),
- -25. / scale * rotation.sin() -25. / scale * rotation.cos()
+ -25. / scale * rotation.cos() + 25. / scale * rotation.sin(),
+ -25. / scale * rotation.sin() - 25. / scale * rotation.cos()
] + player_body.translation();
let top_right_point = point![
- 25. / scale * rotation.cos() +25. / scale * rotation.sin(),
- 25. / scale * rotation.sin() -25. / scale * rotation.cos()
+ 25. / scale * rotation.cos() + 25. / scale * rotation.sin(),
+ 25. / scale * rotation.sin() - 25. / scale * rotation.cos()
] + player_body.translation();
let bottom_left_point = point![
- -25. / scale * rotation.cos() -25. / scale * rotation.sin(),
- -25. / scale * rotation.sin() +25. / scale * rotation.cos()
+ -25. / scale * rotation.cos() - 25. / scale * rotation.sin(),
+ -25. / scale * rotation.sin() + 25. / scale * rotation.cos()
] + player_body.translation();
let bottom_right_point = point![
- 25. / scale * rotation.cos() -25. / scale * rotation.sin(),
- 25. / scale * rotation.sin() +25. / scale * rotation.cos()
+ 25. / scale * rotation.cos() - 25. / scale * rotation.sin(),
+ 25. / scale * rotation.sin() + 25. / scale * rotation.cos()
] + player_body.translation();
- player_body.add_force_at_point(
- left_top_thruster,
- top_left_point, true);
- player_body.add_force_at_point(
- right_top_thruster,
- top_right_point, true);
- player_body.add_force_at_point(
- left_bottom_thruster,
- bottom_left_point, true);
- player_body.add_force_at_point(
- right_bottom_thruster,
- bottom_right_point, true);
-
+ player_body.add_force_at_point(left_top_thruster, top_left_point, true);
+ player_body.add_force_at_point(right_top_thruster, top_right_point, true);
+ player_body.add_force_at_point(left_bottom_thruster, bottom_left_point, true);
+ player_body.add_force_at_point(right_bottom_thruster, bottom_right_point, true);
let translation = player_body.translation();
@@ 222,9 264,9 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc<RwLock<Physic
// TODO: Figure out how to adjust codegen to use f64
protocol_players.push(Player {
- rotation: rotation as f32,
- x: (translation.x * SCALE) as f32,
- y: (translation.y * SCALE) as f32,
+ rotation,
+ x: (translation.x * SCALE),
+ y: (translation.y * SCALE),
username,
special_fields: Default::default(),
});
@@ 239,7 281,13 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc<RwLock<Physic
for (addr, client_thread) in mgr_r.iter() {
match client_thread.tx.send(ClientHandlerMessage::Tick).await {
Ok(_) => {
- 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,31 295,50 @@ pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc<RwLock<Physic
};
let mut modules = entities.read().await.get_modules();
let attached_modules = entities.read().await.get_all_attached();
- let attached_handles: Vec<RigidBodyHandle> = attached_modules.iter().map(|m| {
- m.handle
- }).collect();
- modules.append(&mut attached_modules.iter().map(|m| {
- let module = m.to_module();
- info!("{:?}", module);
- return module;
- }).collect());
+ let attached_handles: Vec<RigidBodyHandle> =
+ 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<starkingdoms_protocol::module::Module> = modules.iter()
+ let protocol_modules: Vec<starkingdoms_protocol::module::Module> = modules
+ .iter()
.map(|module| {
let body = physics_data.rigid_body_set.get(module.handle).unwrap();
return starkingdoms_protocol::module::Module {
module_type: module.module_type.into(),
- rotation: body.rotation().angle() as f32,
- x: (body.translation().x * SCALE) as f32,
- y: (body.translation().y * SCALE) as f32,
+ rotation: body.rotation().angle(),
+ x: body.translation().x * SCALE,
+ y: body.translation().y * SCALE,
special_fields: Default::default(),
};
- }).collect();
- match client_thread.tx.send(ClientHandlerMessage::ModulesUpdate { modules: protocol_modules.clone() }).await {
+ })
+ .collect();
+ match client_thread
+ .tx
+ .send(ClientHandlerMessage::ModulesUpdate {
+ modules: protocol_modules.clone(),
+ })
+ .await
+ {
Ok(_) => (),
Err(e) => {
warn!("unable to send module position packet: {}", e);
M spacetime => spacetime +2 -271
@@ 4,276 4,7 @@ set -e
SCRIPT_PATH=$(readlink -f "${BASH_SOURCE:-$0}")
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
-SERVER_MODS=${STK_SERVER_MODULES:=""}
-exec_spacetime() {
- # args: target, environment, build root, modules
- echo "[*] Running configure command: 'python3 $SCRIPT_DIR/spacetime_py/spacetime.py $1 $2 $SCRIPT_DIR $SPACETIME_VERBOSE'"
- python3 "$SCRIPT_DIR/spacetime_py/spacetime.py" "$1" "$2" "$SCRIPT_DIR" "$SPACETIME_VERBOSE" "$4"
-}
+echo "[*] Running configure command 'spacetime $* $SCRIPT_DIR'"
-exec_ninja() {
- # args: target
- echo "[*] Running build command: 'ninja -C $SCRIPT_DIR $1'"
- ninja -C "$SCRIPT_DIR" "$1"
-}
-
-sub_help() {
- echo "Spacetime - StarKingdoms build utility"
- echo "Spacetime is a small utility program to generate Ninja build manifests for compiling StarKingdoms."
- echo "Available targets:"
- echo " help - Show this help screen" # done
- echo " run_http - Compile the client and run a development http server for testing it" # done
- echo " run_server (default) - Compile and run the game server" # done
- echo " build_server - Compile the game server" # done
- echo " run_server_prod - Compile and run the game server with optimizations enabled" # done
- echo " build_server_prod - Compile the game server with optimizations enabled" # done
- echo " run_api - Compile and run the API server" # done
- echo " build_api - Compile the API server" # done
- echo " run_api_prod - Compile and run the API server with optimizations enabled" # done
- echo " build_api_prod - Compile the API server with optimizations enabled" # done
- echo " install_tooling - Install the compilation utilities required for compiling StarKingdoms" # done
- echo " build_assets - Compile spritesheets in all three texture sizes for textures-fast" # done
- echo " build_assets_full - Compile spritesheets in full size for textures-fast" # done
- echo " build_assets_375 - Commpile 37.5% spritesheets for textures-fast" # done
- echo " build_assets_125 - Compile 12.5% spritesheets for textures-fast" # done
- echo " clean - Remove all generated files" # done
- echo " build_docker_api - Build the API dockerfile" # done
- echo " build_docker_server - Build the server dockerfile" # done
- echo " build_docker_web - Build the web dockerfile" # done
- echo " build_docker - Build the API, web and server containers" # done
- echo " build_docker_api_stable - Build the API container and push it as api-stable" # done
- echo " build_docker_server_stable - Build the server container and push it as server-stable" # done
- echo " build_docker_web_stable - Build the web dockerfile and push it as web-stable" # done
- echo " build_docker_stable - Build the stable api, web and server containers" # done
- echo " infra [action] - Run an infrastructure command. Requires an infrastructure key" # done
-}
-
-check_install_cargo() {
- echo "[*] Checking for $1"
- if ! command -v "$1" &> /dev/null
- then
- echo "[+] $1 was not found, installing via Cargo..."
- cargo install "$2" $3
- fi
-}
-
-check() {
- echo "[*] Checking for $1"
- if ! command -v "$1" &> /dev/null
- then
- echo "[x] $1 was not found but is required for the build process to continue. Install it with your system package manager, or, if supported, 'st install_tooling'"
- exit 1
- fi
-}
-
-check_all() {
- check inkscape
- check protoc
- check atlasify
-}
-
-sub_clean() {
- rm -rf web/dist
- rm -rf assets/dist
- rm -rf assets/final
- rm -rf target
- rm -rf client/pkg
-}
-
-sub_install_tooling() {
- check inkscape
- check protoc
- check atlasify
- echo "[*] All required tools are installed"
-}
-
-sub_run_http() {
- check_all
- exec_spacetime client prod "$SCRIPT_DIR" "$CLIENT_MODS"
- exec_ninja asset
- cd client && yarn && yarn run dev
-}
-
-sub_build_server() {
- check_all
- exec_spacetime server dev "$SCRIPT_DIR" "$SERVER_MODS"
- exec_ninja server
-}
-sub_run_server() {
- check_all
- exec_spacetime server dev "$SCRIPT_DIR" "$SERVER_MODS"
- exec_ninja server
- exec "$SCRIPT_DIR/target/debug/starkingdoms-server"
-}
-
-sub_build_server_prod() {
- check_all
- exec_spacetime server prod "$SCRIPT_DIR" "$SERVER_MODS"
- exec_ninja server
-}
-sub_run_server_prod() {
- check_all
- exec_spacetime server prod "$SCRIPT_DIR" "$SERVER_MODS"
- exec_ninja server
- exec "$SCRIPT_DIR/target/release/starkingdoms-server"
-}
-
-sub_build_api() {
- check_all
- exec_spacetime api dev "$SCRIPT_DIR" "$SERVER_MODS"
- exec_ninja api
-}
-sub_run_api() {
- check_all
- exec_spacetime api dev "$SCRIPT_DIR" "$SERVER_MODS"
- exec_ninja api
- cd api && exec "$SCRIPT_DIR/target/debug/starkingdoms-api"
-}
-
-sub_build_api_prod() {
- check_all
- exec_spacetime api prod "$SCRIPT_DIR" "$SERVER_MODS"
- exec_ninja api
-}
-sub_run_api_prod() {
- check_all
- exec_spacetime api prod "$SCRIPT_DIR" "$SERVER_MODS"
- exec_ninja api
- cd api && exec "$SCRIPT_DIR/target/release/starkingdoms-api"
-}
-
-sub_build_assets() {
- check_all
- exec_spacetime asset dev "$SCRIPT_DIR"
- exec_ninja asset
-}
-
-sub_build_assets_full() {
- check_all
- exec_spacetime asset dev "$SCRIPT_DIR"
- exec_ninja asset-full
-}
-
-sub_build_assets_375() {
- check_all
- exec_spacetime asset dev "$SCRIPT_DIR"
- exec_ninja asset-375
-}
-
-sub_build_assets_125() {
- check_all
- exec_spacetime asset dev "$SCRIPT_DIR"
- exec_ninja asset-125
-}
-
-
-build_docker() {
- docker buildx build -f "$SCRIPT_DIR/$1".Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-$(git rev-parse --short HEAD) "$SCRIPT_DIR"
- docker buildx build -f "$SCRIPT_DIR/$1".Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-"$2" "$SCRIPT_DIR"
- docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-$(git rev-parse --short HEAD)
- docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-"$2"
-}
-
-swap_out_server_for() {
- echo "[*] Swapping out API server"
- sed -i'orig' "s/let api_server = \"http:\\/\\/localhost:8080\";/let api_server = \"https:\\/\\/api.${1}.${2}\";/" "$SCRIPT_DIR/client/index.html"
- echo "[*] Swapping out game server"
- sed -i "s/let servers = \[\"localhost:3000\"\];/let servers = [\"${1}.${2}\"];/" "$SCRIPT_DIR/client/index.html"
-}
-
-sub_swap_server() {
- swap_out_server_for "$1" "$2"
-}
-sub_reset_server() {
- mv client/index.htmlorig client/index.html
-}
-
-sub_build_docker_api() {
- sub_build_api_prod
- build_docker "api" "bleeding"
-}
-
-sub_build_docker_server() {
- sub_build_server_prod
- build_docker "server" "bleeding"
-}
-
-sub_build_docker_web() {
- swap_out_server_for "bleeding" "starkingdoms.io"
- build_docker "web" "bleeding"
- mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
-}
-
-sub_build_docker_web_stable() {
- swap_out_server_for "starkingdoms" "io"
- build_docker "web" "stable"
- mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
-}
-
-sub_build_docker_api_stable() {
- sub_build_api_prod
- build_docker "api" "stable"
-}
-
-sub_build_docker_server_stable() {
- sub_build_server_prod
- build_docker "server" "stable"
-}
-
-
-sub_build_docker_api_beta() {
- sub_build_api_prod
- build_docker "api" "beta"
-}
-
-sub_build_docker_server_beta() {
- sub_build_server_prod
- build_docker "server" "beta"
-}
-
-sub_build_docker_web_beta() {
- swap_out_server_for "beta" "starkingdoms.io"
- build_docker "web" "beta"
- mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
-}
-
-sub_build_docker() {
- sub_build_docker_api
- sub_build_docker_server
- sub_build_docker_web
-}
-
-sub_build_docker_beta() {
- sub_build_docker_api_beta
- sub_build_docker_server_beta
- sub_build_docker_web_beta
-}
-
-sub_build_docker_stable() {
- sub_build_docker_api_stable
- sub_build_docker_server_stable
- sub_build_docker_web_stable
-}
-
-sub_infra() {
- echo "[*] Connecting to infrastructure manager server. If you are prompted for a password, enter your infrastructure key. You may be prompted several times."
- ssh team@10.16.1.3 /home/team/run_ansible.sh "$1"
-}
-
-subcommand=$1
-case $subcommand in
- "" | "-h" | "--help" | "help")
- sub_help
- ;;
- *)
- echo "[*] Running build command $subcommand"
- shift
- sub_${subcommand} $@
- if [ $? = 127 ]; then
- echo "Error: '$subcommand' is not a known subcommand." >&2
- echo " Run 'st --help' for a list of known subcommands." >&2
- exit 1
- fi
- ;;
-esac>
\ No newline at end of file
+cd "$SCRIPT_DIR" && cargo run --release --bin spacetime -- "$@" "$SCRIPT_DIR"<
\ No newline at end of file
A spacetime_old => spacetime_old +284 -0
@@ 0,0 1,284 @@
+#!/bin/bash
+
+set -e
+
+SCRIPT_PATH=$(readlink -f "${BASH_SOURCE:-$0}")
+SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
+SERVER_MODS=${STK_SERVER_MODULES:=""}
+
+exec_spacetime() {
+ # args: target, environment, build root, modules
+ echo "[*] Running configure command: 'python3 $SCRIPT_DIR/spacetime_py/spacetime.py $1 $2 $SCRIPT_DIR $SPACETIME_VERBOSE'"
+ python3 "$SCRIPT_DIR/spacetime_py/spacetime.py" "$1" "$2" "$SCRIPT_DIR" "$SPACETIME_VERBOSE" "$4"
+}
+
+exec_ninja() {
+ # args: target
+ echo "[*] Running build command: 'ninja -C $SCRIPT_DIR $1'"
+ ninja -C "$SCRIPT_DIR" "$1"
+}
+
+sub_help() {
+ echo "Spacetime - StarKingdoms build utility"
+ echo "Spacetime is a small utility program to generate Ninja build manifests for compiling StarKingdoms."
+ echo "Available targets:"
+ echo " help - Show this help screen" # done
+ echo " run_http - Compile the client and run a development http server for testing it" # done
+ echo " run_server (default) - Compile and run the game server" # done
+ echo " build_server - Compile the game server" # done
+ echo " run_server_prod - Compile and run the game server with optimizations enabled" # done
+ echo " build_server_prod - Compile the game server with optimizations enabled" # done
+ echo " run_api - Compile and run the API server" # done
+ echo " build_api - Compile the API server" # done
+ echo " run_api_prod - Compile and run the API server with optimizations enabled" # done
+ echo " build_api_prod - Compile the API server with optimizations enabled" # done
+ echo " install_tooling - Install the compilation utilities required for compiling StarKingdoms" # done
+ echo " build_assets - Compile spritesheets in all three texture sizes for textures-fast" # done
+ echo " build_assets_full - Compile spritesheets in full size for textures-fast" # done
+ echo " build_assets_375 - Commpile 37.5% spritesheets for textures-fast" # done
+ echo " build_assets_125 - Compile 12.5% spritesheets for textures-fast" # done
+ echo " clean - Remove all generated files" # done
+ echo " build_docker_api - Build the API dockerfile" # done
+ echo " build_docker_server - Build the server dockerfile" # done
+ echo " build_docker_web - Build the web dockerfile" # done
+ echo " build_docker - Build the API, web and server containers" # done
+ echo " build_docker_api_stable - Build the API container and push it as api-stable" # done
+ echo " build_docker_server_stable - Build the server container and push it as server-stable" # done
+ echo " build_docker_web_stable - Build the web dockerfile and push it as web-stable" # done
+ echo " build_docker_stable - Build the stable api, web and server containers" # done
+ echo " infra [action] - Run an infrastructure command. Requires an infrastructure key" # done
+ echo " client_protobuf - Rebuild the client protocol bindings" # done
+}
+
+check_install_cargo() {
+ echo "[*] Checking for $1"
+ if ! command -v "$1" &> /dev/null
+ then
+ echo "[+] $1 was not found, installing via Cargo..."
+ cargo install "$2" $3
+ fi
+}
+
+check() {
+ echo "[*] Checking for $1"
+ if ! command -v "$1" &> /dev/null
+ then
+ echo "[x] $1 was not found but is required for the build process to continue. Install it with your system package manager, or, if supported, 'st install_tooling'"
+ exit 1
+ fi
+}
+
+check_all() {
+ check inkscape
+ check protoc
+ check atlasify
+}
+
+sub_clean() {
+ rm -rf web/dist
+ rm -rf assets/dist
+ rm -rf assets/final
+ rm -rf target
+ rm -rf client/pkg
+}
+
+sub_install_tooling() {
+ check inkscape
+ check protoc
+ check atlasify
+ echo "[*] All required tools are installed"
+}
+
+sub_run_http() {
+ check_all
+ exec_spacetime client prod "$SCRIPT_DIR" "$CLIENT_MODS"
+ exec_ninja asset
+ cd client && yarn && yarn run dev
+}
+
+sub_client_protobuf() {
+ cd client && yarn && yarn protobuf
+}
+
+sub_build_server() {
+ check_all
+ exec_spacetime server dev "$SCRIPT_DIR" "$SERVER_MODS"
+ exec_ninja server
+}
+sub_run_server() {
+ check_all
+ exec_spacetime server dev "$SCRIPT_DIR" "$SERVER_MODS"
+ exec_ninja server
+ exec "$SCRIPT_DIR/target/debug/starkingdoms-server"
+}
+
+sub_build_server_prod() {
+ check_all
+ exec_spacetime server prod "$SCRIPT_DIR" "$SERVER_MODS"
+ exec_ninja server
+}
+sub_run_server_prod() {
+ check_all
+ exec_spacetime server prod "$SCRIPT_DIR" "$SERVER_MODS"
+ exec_ninja server
+ exec "$SCRIPT_DIR/target/release/starkingdoms-server"
+}
+
+sub_build_api() {
+ check_all
+ exec_spacetime api dev "$SCRIPT_DIR" "$SERVER_MODS"
+ exec_ninja api
+}
+sub_run_api() {
+ check_all
+ exec_spacetime api dev "$SCRIPT_DIR" "$SERVER_MODS"
+ exec_ninja api
+ cd api && exec "$SCRIPT_DIR/target/debug/starkingdoms-api"
+}
+
+sub_build_api_prod() {
+ check_all
+ exec_spacetime api prod "$SCRIPT_DIR" "$SERVER_MODS"
+ exec_ninja api
+}
+sub_run_api_prod() {
+ check_all
+ exec_spacetime api prod "$SCRIPT_DIR" "$SERVER_MODS"
+ exec_ninja api
+ cd api && exec "$SCRIPT_DIR/target/release/starkingdoms-api"
+}
+
+sub_build_assets() {
+ check_all
+ exec_spacetime asset dev "$SCRIPT_DIR"
+ exec_ninja asset
+}
+
+sub_build_assets_full() {
+ check_all
+ exec_spacetime asset dev "$SCRIPT_DIR"
+ exec_ninja asset-full
+}
+
+sub_build_assets_375() {
+ check_all
+ exec_spacetime asset dev "$SCRIPT_DIR"
+ exec_ninja asset-375
+}
+
+sub_build_assets_125() {
+ check_all
+ exec_spacetime asset dev "$SCRIPT_DIR"
+ exec_ninja asset-125
+}
+
+
+build_docker() {
+ docker buildx build -f "$SCRIPT_DIR/$1".Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-$(git rev-parse --short HEAD) "$SCRIPT_DIR"
+ docker buildx build -f "$SCRIPT_DIR/$1".Dockerfile -t registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-"$2" "$SCRIPT_DIR"
+ docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-$(git rev-parse --short HEAD)
+ docker push registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:"$1"-"$2"
+}
+
+swap_out_server_for() {
+ echo "[*] Swapping out API server"
+ sed -i'orig' "s/let api_server = \"http:\\/\\/localhost:8080\";/let api_server = \"https:\\/\\/api.${1}.${2}\";/" "$SCRIPT_DIR/client/index.html"
+ echo "[*] Swapping out game server"
+ sed -i "s/let servers = \[\"localhost:3000\"\];/let servers = [\"${1}.${2}\"];/" "$SCRIPT_DIR/client/index.html"
+}
+
+sub_swap_server() {
+ swap_out_server_for "$1" "$2"
+}
+sub_reset_server() {
+ mv client/index.htmlorig client/index.html
+}
+
+sub_build_docker_api() {
+ sub_build_api_prod
+ build_docker "api" "bleeding"
+}
+
+sub_build_docker_server() {
+ sub_build_server_prod
+ build_docker "server" "bleeding"
+}
+
+sub_build_docker_web() {
+ swap_out_server_for "bleeding" "starkingdoms.io"
+ build_docker "web" "bleeding"
+ mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
+}
+
+sub_build_docker_web_stable() {
+ swap_out_server_for "starkingdoms" "io"
+ build_docker "web" "stable"
+ mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
+}
+
+sub_build_docker_api_stable() {
+ sub_build_api_prod
+ build_docker "api" "stable"
+}
+
+sub_build_docker_server_stable() {
+ sub_build_server_prod
+ build_docker "server" "stable"
+}
+
+
+sub_build_docker_api_beta() {
+ sub_build_api_prod
+ build_docker "api" "beta"
+}
+
+sub_build_docker_server_beta() {
+ sub_build_server_prod
+ build_docker "server" "beta"
+}
+
+sub_build_docker_web_beta() {
+ swap_out_server_for "beta" "starkingdoms.io"
+ build_docker "web" "beta"
+ mv "$SCRIPT_DIR/client/index.htmlorig" "$SCRIPT_DIR/client/index.html"
+}
+
+sub_build_docker() {
+ sub_build_docker_api
+ sub_build_docker_server
+ sub_build_docker_web
+}
+
+sub_build_docker_beta() {
+ sub_build_docker_api_beta
+ sub_build_docker_server_beta
+ sub_build_docker_web_beta
+}
+
+sub_build_docker_stable() {
+ sub_build_docker_api_stable
+ sub_build_docker_server_stable
+ sub_build_docker_web_stable
+}
+
+sub_infra() {
+ echo "[*] Connecting to infrastructure manager server. If you are prompted for a password, enter your infrastructure key. You may be prompted several times."
+ ssh team@10.16.1.3 /home/team/run_ansible.sh "$1"
+}
+
+subcommand=$1
+case $subcommand in
+ "" | "-h" | "--help" | "help")
+ sub_help
+ ;;
+ *)
+ echo "[*] Running build command $subcommand"
+ shift
+ sub_${subcommand} $@
+ if [ $? = 127 ]; then
+ echo "Error: '$subcommand' is not a known subcommand." >&2
+ echo " Run 'st --help' for a list of known subcommands." >&2
+ exit 1
+ fi
+ ;;
+esac<
\ No newline at end of file
A spacetime_rs/Cargo.toml => spacetime_rs/Cargo.toml +10 -0
@@ 0,0 1,10 @@
+[package]
+name = "spacetime"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+tabwriter = "1.2.1"
+which = "4.4.0"<
\ No newline at end of file
A spacetime_rs/src/cmd.rs => spacetime_rs/src/cmd.rs +20 -0
@@ 0,0 1,20 @@
+use which::which;
+
+pub fn enforce_commands() {
+ println!("[spacetime] checking for required tooling");
+ _enforce_command("cargo");
+ _enforce_command("ninja");
+ _enforce_command("yarn");
+ _enforce_command("inkscape");
+ _enforce_command("atlasify");
+ println!("[spacetime] all required tools present");
+}
+
+fn _enforce_command(cmd: &str) {
+ if which(cmd).is_err() {
+ eprintln!(
+ "[!] Unable to find required binary {}. Please install it to continue.",
+ cmd
+ );
+ }
+}
A spacetime_rs/src/commands/api.rs => spacetime_rs/src/commands/api.rs +59 -0
@@ 0,0 1,59 @@
+use crate::configure::create_writer;
+use crate::configure::rust::configure_rust_target;
+use crate::ninja::{exec, exec_ninja};
+use std::error::Error;
+use std::path::PathBuf;
+
+pub fn build_api(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ let mut config_file_writer = create_writer(&root)?;
+
+ configure_rust_target("api", "dev", &mut config_file_writer, &root)?;
+
+ exec_ninja(&root, vec!["api".to_string()])?;
+
+ Ok(())
+}
+
+pub fn build_api_prod(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ let mut config_file_writer = create_writer(&root)?;
+
+ configure_rust_target("api", "prod", &mut config_file_writer, &root)?;
+
+ exec_ninja(&root, vec!["api".to_string()])?;
+
+ Ok(())
+}
+
+pub fn run_api(args: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ let mut config_file_writer = create_writer(&root)?;
+
+ configure_rust_target("api", "dev", &mut config_file_writer, &root)?;
+
+ exec_ninja(&root, vec!["api".to_string()])?;
+
+ exec(
+ root.join("target/debug/starkingdoms-api").to_str().unwrap(),
+ &root,
+ args,
+ )?;
+
+ Ok(())
+}
+
+pub fn run_api_prod(args: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ let mut config_file_writer = create_writer(&root)?;
+
+ configure_rust_target("api", "prod", &mut config_file_writer, &root)?;
+
+ exec_ninja(&root, vec!["api".to_string()])?;
+
+ exec(
+ root.join("target/release/starkingdoms-api")
+ .to_str()
+ .unwrap(),
+ &root,
+ args,
+ )?;
+
+ Ok(())
+}
A spacetime_rs/src/commands/assets.rs => spacetime_rs/src/commands/assets.rs +26 -0
@@ 0,0 1,26 @@
+use crate::configure::asset::configure_assets;
+use crate::configure::create_writer;
+use crate::ninja::exec_ninja;
+use std::error::Error;
+use std::path::PathBuf;
+use std::time::SystemTime;
+
+pub fn build_assets(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ let mut config_file_writer = create_writer(&root)?;
+
+ let start = SystemTime::now();
+
+ configure_assets(&mut config_file_writer, &root)?;
+
+ let end = SystemTime::now();
+ let duration = end.duration_since(start).unwrap();
+
+ println!(
+ "[spacetime] configure completed in {} seconds",
+ duration.as_secs_f32()
+ );
+
+ exec_ninja(&root, vec!["asset".to_string()])?;
+
+ Ok(())
+}
A spacetime_rs/src/commands/clean.rs => spacetime_rs/src/commands/clean.rs +11 -0
@@ 0,0 1,11 @@
+use crate::ninja::exec;
+use std::error::Error;
+use std::fs;
+use std::path::PathBuf;
+
+pub fn clean(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ exec("cargo", &root, vec!["clean".to_string()])?;
+ fs::remove_dir_all(root.join("assets").join("dist"))?;
+ fs::remove_dir_all(root.join("assets").join("final"))?;
+ Ok(())
+}
A spacetime_rs/src/commands/client.rs => spacetime_rs/src/commands/client.rs +32 -0
@@ 0,0 1,32 @@
+use crate::configure::client::configure_client;
+use crate::configure::create_writer;
+use crate::ninja::{exec, exec_ninja};
+use std::error::Error;
+use std::path::PathBuf;
+
+pub fn run_http(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ let mut config_file_writer = create_writer(&root)?;
+
+ configure_client(&mut config_file_writer, &root)?;
+
+ exec_ninja(&root, vec!["asset".to_string()])?;
+
+ exec("yarn", &root.join("client"), vec![])?;
+ exec(
+ "yarn",
+ &root.join("client"),
+ vec!["run".to_string(), "dev".to_string()],
+ )?;
+
+ Ok(())
+}
+
+pub fn client_protobuf(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ exec("yarn", &root.join("client"), vec![])?;
+ exec("yarn", &root.join("client"), vec!["protobuf".to_string()])
+}
+
+pub fn build_client_prod(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ exec("yarn", &root.join("client"), vec![])?;
+ exec("yarn", &root.join("client"), vec!["build".to_string()])
+}
A spacetime_rs/src/commands/docker.rs => spacetime_rs/src/commands/docker.rs +143 -0
@@ 0,0 1,143 @@
+use crate::commands::api::build_api_prod;
+use crate::commands::client::build_client_prod;
+use crate::commands::server::build_server_prod;
+use crate::ninja::exec;
+use std::error::Error;
+use std::path::PathBuf;
+use std::process::Command;
+
+fn _build(img: &str, channel: &str, root: &PathBuf) -> Result<(), Box<dyn Error>> {
+ // compile the various thingies
+ if img == "server" {
+ build_server_prod(vec![], root.clone())?;
+ } else if img == "api" {
+ build_api_prod(vec![], root.clone())?;
+ } else {
+ build_client_prod(vec![], root.clone())?
+ }
+
+ let git_commit_id = String::from_utf8(
+ Command::new("git")
+ .args(["rev-parse", "--short", "HEAD"])
+ .current_dir(root)
+ .output()
+ .unwrap()
+ .stdout,
+ )
+ .unwrap()
+ .replace('\n', "");
+ exec(
+ "docker",
+ root,
+ vec![
+ "buildx",
+ "build",
+ "-f",
+ root.join(format!("{}.Dockerfile", img)).to_str().unwrap(),
+ "-t",
+ &format!(
+ "registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:{}-{}",
+ img, git_commit_id
+ ),
+ root.to_str().unwrap(),
+ ]
+ .iter()
+ .map(|u| u.to_string())
+ .collect(),
+ )?;
+ exec(
+ "docker",
+ root,
+ vec![
+ "buildx",
+ "build",
+ "-f",
+ root.join(format!("{}.Dockerfile", img)).to_str().unwrap(),
+ "-t",
+ &format!(
+ "registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:{}-{}",
+ img, channel
+ ),
+ root.to_str().unwrap(),
+ ]
+ .iter()
+ .map(|u| u.to_string())
+ .collect(),
+ )?;
+
+ exec(
+ "docker",
+ root,
+ vec![
+ "push",
+ &format!(
+ "registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:{}-{}",
+ img, git_commit_id
+ ),
+ ]
+ .iter()
+ .map(|u| u.to_string())
+ .collect(),
+ )?;
+ exec(
+ "docker",
+ root,
+ vec![
+ "push",
+ &format!(
+ "registry.gitlab.com/starkingdoms.tk/starkingdoms.tk:{}-{}",
+ img, channel
+ ),
+ ]
+ .iter()
+ .map(|u| u.to_string())
+ .collect(),
+ )?;
+
+ Ok(())
+}
+
+pub fn build_docker_api(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ _build("api", "bleeding", &root)
+}
+pub fn build_docker_server(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ _build("server", "bleeding", &root)
+}
+pub fn build_docker_web(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ _build("web", "bleeding", &root)
+}
+pub fn build_docker(_a: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ build_docker_api(_a.clone(), root.clone())?;
+ build_docker_server(_a.clone(), root.clone())?;
+ build_docker_web(_a, root)
+}
+
+pub fn build_docker_api_beta(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ _build("api", "beta", &root)
+}
+pub fn build_docker_server_beta(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ _build("server", "beta", &root)
+}
+pub fn build_docker_web_beta(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ _build("web", "beta", &root)
+}
+pub fn build_docker_beta(_a: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ build_docker_api_beta(_a.clone(), root.clone())?;
+ build_docker_server_beta(_a.clone(), root.clone())?;
+ build_docker_web_beta(_a, root)
+}
+
+pub fn build_docker_api_stable(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ _build("api", "stable", &root)
+}
+pub fn build_docker_server_stable(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ _build("server", "stable", &root)
+}
+pub fn build_docker_web_stable(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ _build("web", "stable", &root)
+}
+pub fn build_docker_stable(_a: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ build_docker_api_stable(_a.clone(), root.clone())?;
+ build_docker_server_stable(_a.clone(), root.clone())?;
+ build_docker_web_stable(_a, root)
+}
A spacetime_rs/src/commands/mod.rs => spacetime_rs/src/commands/mod.rs +6 -0
@@ 0,0 1,6 @@
+pub mod api;
+pub mod assets;
+pub mod clean;
+pub mod client;
+pub mod docker;
+pub mod server;
A spacetime_rs/src/commands/server.rs => spacetime_rs/src/commands/server.rs +61 -0
@@ 0,0 1,61 @@
+use crate::configure::create_writer;
+use crate::configure::rust::configure_rust_target;
+use crate::ninja::{exec, exec_ninja};
+use std::error::Error;
+use std::path::PathBuf;
+
+pub fn build_server(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ let mut config_file_writer = create_writer(&root)?;
+
+ configure_rust_target("server", "dev", &mut config_file_writer, &root)?;
+
+ exec_ninja(&root, vec!["server".to_string()])?;
+
+ Ok(())
+}
+
+pub fn build_server_prod(_: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ let mut config_file_writer = create_writer(&root)?;
+
+ configure_rust_target("server", "prod", &mut config_file_writer, &root)?;
+
+ exec_ninja(&root, vec!["server".to_string()])?;
+
+ Ok(())
+}
+
+pub fn run_server(args: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ let mut config_file_writer = create_writer(&root)?;
+
+ configure_rust_target("server", "dev", &mut config_file_writer, &root)?;
+
+ exec_ninja(&root, vec!["server".to_string()])?;
+
+ exec(
+ root.join("target/debug/starkingdoms-server")
+ .to_str()
+ .unwrap(),
+ &root,
+ args,
+ )?;
+
+ Ok(())
+}
+
+pub fn run_server_prod(args: Vec<String>, root: PathBuf) -> Result<(), Box<dyn Error>> {
+ let mut config_file_writer = create_writer(&root)?;
+
+ configure_rust_target("server", "prod", &mut config_file_writer, &root)?;
+
+ exec_ninja(&root, vec!["server".to_string()])?;
+
+ exec(
+ root.join("target/release/starkingdoms-server")
+ .to_str()
+ .unwrap(),
+ &root,
+ args,
+ )?;
+
+ Ok(())
+}
A spacetime_rs/src/config.rs => spacetime_rs/src/config.rs +4 -0
@@ 0,0 1,4 @@
+pub const ASSET_DIR: &str = "assets/";
+pub const ASSETS_DIST_SUBDIR: &str = "dist/";
+pub const ASSETS_SRC_SUBDIR: &str = "src/";
+pub const ASSETS_FINAL_SUBDIR: &str = "final/";
A spacetime_rs/src/configure/asset.rs => spacetime_rs/src/configure/asset.rs +419 -0
@@ 0,0 1,419 @@
+use crate::config::{ASSETS_DIST_SUBDIR, ASSETS_FINAL_SUBDIR, ASSETS_SRC_SUBDIR, ASSET_DIR};
+use crate::ninja::NinjaWriter;
+use std::collections::HashMap;
+use std::error::Error;
+use std::fs;
+use std::fs::File;
+use std::path::{Path, PathBuf};
+
+pub fn configure_assets(writer: &mut NinjaWriter<File>, root: &Path) -> Result<(), Box<dyn Error>> {
+ // scan for assets
+ let asset_src_dir = root.join(ASSET_DIR).join(ASSETS_SRC_SUBDIR);
+
+ let mut found_assets = vec![];
+
+ let files_in_src_dir = fs::read_dir(asset_src_dir)?;
+ for maybe_asset in files_in_src_dir {
+ let maybe_asset = maybe_asset?;
+ if maybe_asset.file_name().to_str().unwrap().ends_with(".svg") {
+ found_assets.push(maybe_asset.path());
+ }
+ }
+
+ println!(
+ "[spacetime] asset scan: found {} assets",
+ found_assets.len()
+ );
+
+ let default_asset_size = 512;
+ let asset_overrides = HashMap::from([("earth.ink.svg", 2048), ("moon.ink.svg", 2048)]);
+
+ // generate an inkscape rule for all required asset sizes
+ let mut written_rules_for = vec![];
+
+ gen_inkscape_rule(default_asset_size, writer, &mut written_rules_for)?;
+
+ for size in asset_overrides.values() {
+ gen_inkscape_rule(*size, writer, &mut written_rules_for)?;
+ }
+
+ println!(
+ "[spacetime] generated {} image conversion rules",
+ written_rules_for.len() * 3
+ );
+
+ let mut files_375 = vec![];
+ let mut files_125 = vec![];
+ let mut files_full = vec![];
+
+ for asset in &found_assets {
+ gen_convert_rule(
+ asset,
+ root,
+ writer,
+ &mut files_375,
+ &mut files_full,
+ &mut files_125,
+ asset_size(
+ asset.to_str().unwrap(),
+ &asset_overrides,
+ default_asset_size,
+ ),
+ )?;
+ }
+
+ println!(
+ "[spacetime] generated {} image conversion steps",
+ files_full.len() + files_125.len() + files_375.len()
+ );
+
+ gen_packer_rule(root, writer, &files_375, &files_full, &files_125)?;
+
+ println!("[spacetime] generated asset build commands");
+
+ Ok(())
+}
+
+fn gen_packer_rule(
+ root: &Path,
+ writer: &mut NinjaWriter<File>,
+ files_375: &[PathBuf],
+ files_full: &[PathBuf],
+ files_125: &[PathBuf],
+) -> Result<(), Box<dyn Error>> {
+ writer.rule(
+ "pack",
+ &format!(
+ "cd {} && atlasify -m 4096,4096 -o $out $in && touch $out",
+ root.join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .to_string_lossy()
+ ),
+ None,
+ None,
+ None,
+ Some("console"),
+ None,
+ None,
+ None,
+ None,
+ )?;
+
+ writer.build(
+ vec![root
+ .join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-full")
+ .to_str()
+ .unwrap()
+ .to_string()],
+ "pack".to_string(),
+ files_full
+ .iter()
+ .map(|u| u.to_str().unwrap().to_string())
+ .collect(),
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![
+ root.join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-full.json")
+ .to_str()
+ .unwrap()
+ .to_string(),
+ root.join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-full.png")
+ .to_str()
+ .unwrap()
+ .to_string(),
+ ],
+ None,
+ None,
+ )?;
+ writer.build(
+ vec!["asset-full".to_string()],
+ "phony".to_string(),
+ vec![root
+ .join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-full")
+ .to_str()
+ .unwrap()
+ .to_string()],
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![],
+ None,
+ None,
+ )?;
+
+ writer.build(
+ vec![root
+ .join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-125")
+ .to_str()
+ .unwrap()
+ .to_string()],
+ "pack".to_string(),
+ files_125
+ .iter()
+ .map(|u| u.to_str().unwrap().to_string())
+ .collect(),
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![
+ root.join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-125.json")
+ .to_str()
+ .unwrap()
+ .to_string(),
+ root.join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-125.png")
+ .to_str()
+ .unwrap()
+ .to_string(),
+ ],
+ None,
+ None,
+ )?;
+ writer.build(
+ vec!["asset-125".to_string()],
+ "phony".to_string(),
+ vec![root
+ .join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-125")
+ .to_str()
+ .unwrap()
+ .to_string()],
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![],
+ None,
+ None,
+ )?;
+
+ writer.build(
+ vec![root
+ .join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-375")
+ .to_str()
+ .unwrap()
+ .to_string()],
+ "pack".to_string(),
+ files_375
+ .iter()
+ .map(|u| u.to_str().unwrap().to_string())
+ .collect(),
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![
+ root.join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-375.json")
+ .to_str()
+ .unwrap()
+ .to_string(),
+ root.join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-375.png")
+ .to_str()
+ .unwrap()
+ .to_string(),
+ ],
+ None,
+ None,
+ )?;
+ writer.build(
+ vec!["asset-375".to_string()],
+ "phony".to_string(),
+ vec![root
+ .join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-375")
+ .to_str()
+ .unwrap()
+ .to_string()],
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![],
+ None,
+ None,
+ )?;
+
+ writer.build(
+ vec!["asset".to_string()],
+ "phony".to_string(),
+ vec![
+ root.join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-375")
+ .to_str()
+ .unwrap()
+ .to_string(),
+ root.join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-full")
+ .to_str()
+ .unwrap()
+ .to_string(),
+ root.join(ASSET_DIR)
+ .join(ASSETS_DIST_SUBDIR)
+ .join("spritesheet-125")
+ .to_str()
+ .unwrap()
+ .to_string(),
+ ],
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![],
+ None,
+ None,
+ )?;
+
+ Ok(())
+}
+
+fn gen_convert_rule(
+ asset: &Path,
+ root: &Path,
+ writer: &mut NinjaWriter<File>,
+ files_375: &mut Vec<PathBuf>,
+ files_full: &mut Vec<PathBuf>,
+ files_125: &mut Vec<PathBuf>,
+ size: i32,
+) -> Result<(), Box<dyn Error>> {
+ let out_full = root
+ .join(ASSET_DIR)
+ .join(ASSETS_FINAL_SUBDIR)
+ .join("full/")
+ .join(asset.file_stem().unwrap().to_str().unwrap().to_string() + ".png");
+ files_full.push(out_full.clone());
+ let rule_full = format!("inkscape_{}_px_full", size);
+ writer.build(
+ vec![out_full.to_str().unwrap().to_string()],
+ rule_full,
+ vec![asset.to_str().unwrap().to_string()],
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![],
+ None,
+ None,
+ )?;
+
+ let out_375 = root
+ .join(ASSET_DIR)
+ .join(ASSETS_FINAL_SUBDIR)
+ .join("375/")
+ .join(asset.file_stem().unwrap().to_str().unwrap().to_string() + ".png");
+ files_375.push(out_375.clone());
+ let rule_375 = format!("inkscape_{}_px_375", size);
+ writer.build(
+ vec![out_375.to_str().unwrap().to_string()],
+ rule_375,
+ vec![asset.to_str().unwrap().to_string()],
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![],
+ None,
+ None,
+ )?;
+
+ let out_125 = root
+ .join(ASSET_DIR)
+ .join(ASSETS_FINAL_SUBDIR)
+ .join("125/")
+ .join(asset.file_stem().unwrap().to_str().unwrap().to_string() + ".png");
+ files_125.push(out_125.clone());
+ let rule_125 = format!("inkscape_{}_px_125", size);
+ writer.build(
+ vec![out_125.to_str().unwrap().to_string()],
+ rule_125,
+ vec![asset.to_str().unwrap().to_string()],
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![],
+ None,
+ None,
+ )?;
+
+ Ok(())
+}
+
+fn asset_size(asset: &str, overrides: &HashMap<&str, i32>, default: i32) -> i32 {
+ *overrides.get(asset).unwrap_or(&default)
+}
+
+fn gen_inkscape_rule(
+ size: i32,
+ writer: &mut NinjaWriter<File>,
+ written: &mut Vec<i32>,
+) -> Result<(), Box<dyn Error>> {
+ if written.contains(&size) {
+ return Ok(());
+ }
+
+ writer.rule(
+ &format!("inkscape_{}_px_full", size),
+ &format!("inkscape -w {} -h {} $in -o $out", size, size),
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ )?;
+ writer.rule(
+ &format!("inkscape_{}_px_375", size),
+ &format!(
+ "inkscape -w {} -h {} $in -o $out",
+ (size as f64 * 0.375) as i32,
+ (size as f64 * 0.375) as i32
+ ),
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ )?;
+ writer.rule(
+ &format!("inkscape_{}_px_125", size),
+ &format!(
+ "inkscape -w {} -h {} $in -o $out",
+ (size as f64 * 0.125) as i32,
+ (size as f64 * 0.125) as i32
+ ),
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ None,
+ )?;
+
+ written.push(size);
+
+ Ok(())
+}
A spacetime_rs/src/configure/client.rs => spacetime_rs/src/configure/client.rs +11 -0
@@ 0,0 1,11 @@
+use crate::configure::asset::configure_assets;
+use crate::ninja::NinjaWriter;
+use std::error::Error;
+use std::fs::File;
+use std::path::Path;
+
+pub fn configure_client(writer: &mut NinjaWriter<File>, root: &Path) -> Result<(), Box<dyn Error>> {
+ configure_assets(writer, root)?;
+
+ Ok(())
+}
A spacetime_rs/src/configure/mod.rs => spacetime_rs/src/configure/mod.rs +15 -0
@@ 0,0 1,15 @@
+use crate::ninja::NinjaWriter;
+use std::fs::File;
+use std::io;
+use std::path::Path;
+
+pub mod asset;
+pub mod client;
+pub mod rust;
+
+pub fn create_writer(root: &Path) -> Result<NinjaWriter<File>, io::Error> {
+ let mut w = NinjaWriter::new(File::create(root.join("build.ninja"))?);
+ w.comment("Generated by spacetime")?;
+ w.comment("Do not edit manually")?;
+ Ok(w)
+}
A spacetime_rs/src/configure/rust.rs => spacetime_rs/src/configure/rust.rs +103 -0
@@ 0,0 1,103 @@
+/*
+def gen_rules_for_api(root, env, writer, modules):
+ if env == 'dev':
+ out_dir = 'debug'
+ writer.rule('cargo-api', f'cargo build --bin starkingdoms-api --features "{modules}"',
+ depfile=f'{root}/target/debug/starkingdoms-api.d', pool='console')
+ elif env == 'prod':
+ out_dir = 'release'
+ writer.rule('cargo-api', f'cargo build --bin starkingdoms-api --release --features "{modules}"',
+ depfile=f'{root}/target/release/starkingdoms-api.d', pool='console')
+
+ writer.build([f'{root}/target/{out_dir}/starkingdoms-api'], 'cargo-api', ['server/Cargo.toml'])
+ writer.build(['api'], 'phony', [f'{root}/target/{out_dir}/starkingdoms-api'])
+ */
+
+use crate::ninja::NinjaWriter;
+use std::collections::HashMap;
+use std::error::Error;
+use std::fs::File;
+use std::path::Path;
+
+pub fn configure_rust_target(
+ rust_target: &str,
+ rust_env: &str,
+ writer: &mut NinjaWriter<File>,
+ root: &Path,
+) -> Result<(), Box<dyn Error>> {
+ let out_dir;
+ if rust_env == "dev" {
+ out_dir = "debug";
+ writer.rule(
+ &format!("cargo-{}", rust_target),
+ &format!("cargo build --bin starkingdoms-{}", rust_target),
+ None,
+ Some(
+ root.join("target/debug/")
+ .join(format!("starkingdoms-{}.d", rust_target))
+ .to_str()
+ .unwrap(),
+ ),
+ None,
+ Some("console"),
+ None,
+ None,
+ None,
+ None,
+ )?;
+ } else {
+ out_dir = "release";
+ writer.rule(
+ &format!("cargo-{}", rust_target),
+ &format!("cargo build --bin starkingdoms-{} --release", rust_target),
+ None,
+ Some(
+ root.join("target/release/")
+ .join(format!("starkingdoms-{}.d", rust_target))
+ .to_str()
+ .unwrap(),
+ ),
+ None,
+ Some("console"),
+ None,
+ None,
+ None,
+ None,
+ )?;
+ }
+
+ writer.build(
+ vec![root
+ .join(format!("target/{}/", out_dir))
+ .join(format!("starkingdoms-{}", rust_target))
+ .to_str()
+ .unwrap()
+ .to_string()],
+ format!("cargo-{}", rust_target),
+ vec![root.join("server/Cargo.toml").to_str().unwrap().to_string()],
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![],
+ None,
+ None,
+ )?;
+ writer.build(
+ vec![rust_target.to_string()],
+ "phony".to_string(),
+ vec![root
+ .join(format!("target/{}/", out_dir))
+ .join(format!("starkingdoms-{}", rust_target))
+ .to_str()
+ .unwrap()
+ .to_string()],
+ vec![],
+ vec![],
+ HashMap::new(),
+ vec![],
+ None,
+ None,
+ )?;
+
+ Ok(())
+}
A spacetime_rs/src/main.rs => spacetime_rs/src/main.rs +263 -0
@@ 0,0 1,263 @@
+use crate::cmd::enforce_commands;
+use crate::commands::api::{build_api, build_api_prod, run_api, run_api_prod};
+use crate::commands::assets::build_assets;
+use crate::commands::clean::clean;
+use crate::commands::client::{build_client_prod, client_protobuf, run_http};
+use crate::commands::docker::{
+ build_docker, build_docker_api, build_docker_api_beta, build_docker_api_stable,
+ build_docker_beta, build_docker_server, build_docker_server_beta, build_docker_server_stable,
+ build_docker_stable, build_docker_web, build_docker_web_beta, build_docker_web_stable,
+};
+use crate::commands::server::{build_server, build_server_prod, run_server, run_server_prod};
+use std::collections::HashMap;
+use std::error::Error;
+use std::io::Write;
+use std::path::PathBuf;
+use std::time::SystemTime;
+use tabwriter::TabWriter;
+
+pub mod cmd;
+pub mod commands;
+pub mod config;
+pub mod configure;
+pub mod ninja;
+
+fn main() {
+ let mut bcm = BuildCommandManager::new();
+
+ bcm.register(
+ "run_http",
+ Box::new(run_http),
+ "Compile the client and then run a development HTTP server",
+ false,
+ );
+ bcm.register(
+ "build_assets",
+ Box::new(build_assets),
+ "Compile the asset source files into spritesheets",
+ false,
+ );
+ bcm.register(
+ "client_protobuf",
+ Box::new(client_protobuf),
+ "Update the client protocol bindings",
+ false,
+ );
+ bcm.register(
+ "clean",
+ Box::new(clean),
+ "Remove all compilation artifacts",
+ false,
+ );
+
+ bcm.register(
+ "build_api",
+ Box::new(build_api),
+ "Compile the API server",
+ false,
+ );
+ bcm.register("run_api", Box::new(run_api), "Run the API server", false);
+ bcm.register(
+ "build_api_prod",
+ Box::new(build_api_prod),
+ "Compile the API server with optimizations",
+ false,
+ );
+ bcm.register(
+ "run_api_prod",
+ Box::new(run_api_prod),
+ "Run the API server with optimizations",
+ false,
+ );
+
+ bcm.register(
+ "build_server",
+ Box::new(build_server),
+ "Compile the game server",
+ false,
+ );
+ bcm.register(
+ "run_server",
+ Box::new(run_server),
+ "Run the game server",
+ false,
+ );
+ bcm.register(
+ "build_server_prod",
+ Box::new(build_server_prod),
+ "Compile the game server with optimizations",
+ false,
+ );
+ bcm.register(
+ "run_server_prod",
+ Box::new(run_server_prod),
+ "Run the game server with optimizations",
+ false,
+ );
+
+ bcm.register(
+ "build_docker_beta",
+ Box::new(build_docker_beta),
+ "Build all three docker images for the beta channel",
+ false,
+ );
+ bcm.register(
+ "build_docker_api_beta",
+ Box::new(build_docker_api_beta),
+ "Build the API docker image for the beta channel",
+ false,
+ );
+ bcm.register(
+ "build_docker_server_beta",
+ Box::new(build_docker_server_beta),
+ "Build the main docker image for the beta channel",
+ false,
+ );
+ bcm.register(
+ "build_docker_web_beta",
+ Box::new(build_docker_web_beta),
+ "Build the webserver docker image for the beta channel",
+ false,
+ );
+
+ bcm.register(
+ "build_docker_stable",
+ Box::new(build_docker_stable),
+ "Build all three docker images for the stable channel",
+ false,
+ );
+ bcm.register(
+ "build_docker_api_stable",
+ Box::new(build_docker_api_stable),
+ "Build the API docker image for the stable channel",
+ false,
+ );
+ bcm.register(
+ "build_docker_server_stable",
+ Box::new(build_docker_server_stable),
+ "Build the main docker image for the stable channel",
+ false,
+ );
+ bcm.register(
+ "build_docker_web_stable",
+ Box::new(build_docker_web_stable),
+ "Build the webserver docker image for the stable channel",
+ false,
+ );
+
+ bcm.register(
+ "build_docker",
+ Box::new(build_docker),
+ "Build all three docker images for the bleeding channel",
+ false,
+ );
+ bcm.register(
+ "build_docker_api",
+ Box::new(build_docker_api),
+ "Build the API docker image for the bleeding channel",
+ false,
+ );
+ bcm.register(
+ "build_docker_server",
+ Box::new(build_docker_server),
+ "Build the main docker image for the bleeding channel",
+ false,
+ );
+ bcm.register(
+ "build_docker_web",
+ Box::new(build_docker_web),
+ "Build the webserver docker image for the bleeding channel",
+ false,
+ );
+
+ bcm.register(
+ "build_client_prod",
+ Box::new(build_client_prod),
+ "Build the production-ready client bundle",
+ false,
+ );
+
+ let start = SystemTime::now();
+
+ let args: Vec<String> = std::env::args().collect();
+ match bcm.exec(args) {
+ Ok(_) => (),
+ Err(e) => {
+ let end = SystemTime::now();
+ let duration = end.duration_since(start).unwrap();
+
+ println!("[spacetime] Done in {} seconds", duration.as_secs_f32());
+
+ eprintln!("[!] Error executing build command: {}", e);
+ std::process::exit(-1);
+ }
+ }
+
+ let end = SystemTime::now();
+ let duration = end.duration_since(start).unwrap();
+
+ println!("[spacetime] Done in {} seconds", duration.as_secs_f32());
+}
+
+type BuildCommandCallback = Box<dyn Fn(Vec<String>, PathBuf) -> Result<(), Box<dyn Error>>>;
+#[derive(Default)]
+pub struct BuildCommandManager {
+ commands: HashMap<String, (BuildCommandCallback, String, bool)>,
+}
+impl BuildCommandManager {
+ pub fn new() -> Self {
+ Self {
+ commands: HashMap::new(),
+ }
+ }
+
+ pub fn register(
+ &mut self,
+ name: &str,
+ command: BuildCommandCallback,
+ description: &str,
+ hidden: bool,
+ ) {
+ self.commands
+ .insert(name.to_string(), (command, description.to_string(), hidden));
+ }
+
+ pub fn help(&self) {
+ println!("Spacetime - StarKingdoms build utility");
+ println!("Spacetime is a small Rust utility to assist in compiling StarKingdoms.");
+ println!("Available targets:");
+
+ let mut tw = TabWriter::new(vec![]);
+
+ for (target, (_, description, hidden)) in &self.commands {
+ if *hidden {
+ continue;
+ };
+
+ writeln!(tw, "\t{}\t{}", target, description).unwrap();
+ }
+
+ std::io::stdout()
+ .write_all(&tw.into_inner().unwrap())
+ .unwrap();
+ }
+
+ pub fn exec(&self, args: Vec<String>) -> Result<(), Box<dyn Error>> {
+ if args.len() < 2 || args[1] == "help" || args[1] == "h" {
+ self.help();
+ return Ok(());
+ }
+
+ enforce_commands();
+
+ let main_path = PathBuf::from(args[2].to_string());
+
+ if let Some((callback, _, _)) = self.commands.get(&args[1]) {
+ callback(args[2..].to_owned(), main_path)
+ } else {
+ eprintln!("Unrecognized build command {}", args[1]);
+ self.help();
+ Ok(())
+ }
+ }
+}
A spacetime_rs/src/ninja.rs => spacetime_rs/src/ninja.rs +194 -0
@@ 0,0 1,194 @@
+use std::collections::HashMap;
+use std::error::Error;
+use std::io;
+use std::io::Write;
+use std::path::PathBuf;
+use std::process::{Command, Stdio};
+
+fn escape_path(word: &str) -> String {
+ word.replace("$ ", "$$ ")
+ .replace(' ', "$ ")
+ .replace(':', "$:")
+}
+
+pub struct NinjaWriter<T: Write> {
+ output: T,
+}
+impl<T: Write> NinjaWriter<T> {
+ pub fn new(output: T) -> Self {
+ Self { output }
+ }
+
+ pub fn newline(&mut self) -> Result<(), io::Error> {
+ writeln!(self.output)
+ }
+
+ pub fn comment(&mut self, text: &str) -> Result<(), io::Error> {
+ writeln!(self.output, "# {}", text)
+ }
+
+ pub fn variable(&mut self, key: &str, value: &str, indent: usize) -> Result<(), io::Error> {
+ if value.is_empty() {
+ return Ok(());
+ }
+ writeln!(self.output, "{}{} = {}", self._indent(indent), key, value)
+ }
+
+ pub fn pool(&mut self, name: &str, depth: usize) -> Result<(), io::Error> {
+ writeln!(self.output, "pool {}", name)?;
+ self.variable("depth", &depth.to_string(), 1)
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn rule(
+ &mut self,
+ name: &str,
+ command: &str,
+ description: Option<&str>,
+ depfile: Option<&str>,
+ generator: Option<bool>,
+ pool: Option<&str>,
+ restat: Option<bool>,
+ rspfile: Option<&str>,
+ rspfile_content: Option<&str>,
+ deps: Option<&str>,
+ ) -> Result<(), io::Error> {
+ writeln!(self.output, "rule {}", name)?;
+ self.variable("command", command, 1)?;
+ if let Some(desc) = description {
+ self.variable("description", desc, 1)?;
+ }
+ if let Some(depfile) = depfile {
+ self.variable("depfile", depfile, 1)?;
+ }
+ if let Some(gen) = generator {
+ if gen {
+ self.variable("generator", "1", 1)?;
+ }
+ }
+ if let Some(pool) = pool {
+ self.variable("pool", pool, 1)?;
+ }
+ if let Some(restat) = restat {
+ if restat {
+ self.variable("restat", "1", 1)?;
+ }
+ }
+ if let Some(rspfile) = rspfile {
+ self.variable("rspfile", rspfile, 1)?;
+ }
+ if let Some(rspfile_content) = rspfile_content {
+ self.variable("rspfile_content", rspfile_content, 1)?;
+ }
+ if let Some(deps) = deps {
+ self.variable("deps", deps, 1)?;
+ }
+
+ Ok(())
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ pub fn build(
+ &mut self,
+ outputs: Vec<String>,
+ rule: String,
+ inputs: Vec<String>,
+ mut implicit: Vec<String>,
+ mut order_only: Vec<String>,
+ variables: HashMap<String, String>,
+ mut implicit_outputs: Vec<String>,
+ pool: Option<String>,
+ dyndep: Option<String>,
+ ) -> Result<(), io::Error> {
+ let mut out_outputs: Vec<String> = outputs.iter().map(|u| escape_path(u)).collect();
+ let mut all_inputs: Vec<String> = inputs.iter().map(|u| escape_path(u)).collect();
+
+ if !implicit.is_empty() {
+ all_inputs.push("|".to_string());
+ all_inputs.append(&mut implicit.iter_mut().map(|u| escape_path(u)).collect());
+ }
+ if !order_only.is_empty() {
+ all_inputs.push("||".to_string());
+ all_inputs.append(&mut order_only.iter_mut().map(|u| escape_path(u)).collect());
+ }
+ if !implicit_outputs.is_empty() {
+ out_outputs.push("|".to_string());
+ out_outputs.append(
+ &mut implicit_outputs
+ .iter_mut()
+ .map(|u| escape_path(u))
+ .collect(),
+ );
+ }
+
+ all_inputs.insert(0, rule);
+
+ writeln!(
+ self.output,
+ "build {}: {}",
+ out_outputs.join(" "),
+ all_inputs.join(" ")
+ )?;
+
+ if let Some(pool) = pool {
+ self.variable("pool", &pool, 1)?;
+ }
+ if let Some(dyndep) = dyndep {
+ self.variable("dyndep", &dyndep, 1)?;
+ }
+
+ for (key, value) in variables {
+ self.variable(&key, &value, 1)?;
+ }
+
+ Ok(())
+ }
+
+ pub fn include(&mut self, path: &str) -> Result<(), io::Error> {
+ writeln!(self.output, "include {}", path)
+ }
+
+ pub fn subninja(&mut self, path: &str) -> Result<(), io::Error> {
+ writeln!(self.output, "subninja {}", path)
+ }
+
+ pub fn default(&mut self, paths: Vec<String>) -> Result<(), io::Error> {
+ writeln!(self.output, "default {}", paths.join(" "))
+ }
+
+ fn _indent(&self, num: usize) -> String {
+ " ".repeat(num)
+ }
+}
+
+pub fn exec_ninja(root: &PathBuf, args: Vec<String>) -> Result<(), Box<dyn Error>> {
+ println!("[exec] cmd: ninja {:?}", args);
+ let stat = Command::new("ninja")
+ .args(args)
+ .current_dir(root)
+ .stdout(Stdio::inherit())
+ .stderr(Stdio::inherit())
+ .spawn()?
+ .wait()?;
+ if stat.success() {
+ Ok(())
+ } else {
+ Err(format!("ninja exited with error: {}", stat.code().unwrap()).into())
+ }
+}
+
+pub fn exec(cmd: &str, dir: &PathBuf, args: Vec<String>) -> Result<(), Box<dyn Error>> {
+ println!("[exec] cmd: {} {:?}", cmd, args);
+ let stat = Command::new(cmd)
+ .args(args)
+ .current_dir(dir)
+ .stdout(Stdio::inherit())
+ .stderr(Stdio::inherit())
+ .spawn()?
+ .wait()?;
+ if stat.success() {
+ Ok(())
+ } else {
+ Err(format!("{} exited with error: {}", cmd, stat.code().unwrap()).into())
+ }
+}