From 29a5457af664e7f51c1f89ad04f472bd242b3f98 Mon Sep 17 00:00:00 2001 From: core Date: Thu, 29 Jun 2023 15:03:11 -0400 Subject: [PATCH] code cleanup pt1 --- Cargo.lock | 5 +- api/src/config.rs | 2 +- api/src/main.rs | 2 +- api/src/routes/mod.rs | 2 +- api/src/routes/select_realm.rs | 2 +- api/src/routes/server_list.rs | 8 +- client/index.html | 4 - protocol/src/lib.rs | 33 +++-- server/Cargo.toml | 4 +- server/src/api.rs | 6 +- server/src/entity.rs | 31 ++-- server/src/handler.rs | 219 ++++++++++++++++++--------- server/src/main.rs | 102 ++++++++----- server/src/manager.rs | 27 ++-- server/src/module.rs | 220 ++++++++++++---------------- server/src/orbit/constants.rs | 16 +- server/src/orbit/kepler.rs | 2 +- server/src/orbit/newtonian.rs | 2 +- server/src/orbit/orbit.rs | 17 ++- server/src/planet.rs | 12 +- server/src/timer.rs | 124 +++++++++------- spacetime_rs/src/commands/docker.rs | 9 +- spacetime_rs/src/configure/asset.rs | 6 +- web/play.html | 2 +- web/static/css/index.css | 3 - web/static/css/play.css | 5 - 26 files changed, 473 insertions(+), 392 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 298ff5dbab9dca03bc16c64754ce9f4711376c8e..687196ecef91823d548f53d3e9c1a416388d805e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -511,9 +511,9 @@ dependencies = [ [[package]] name = "async-tungstenite" -version = "0.21.0" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2c69d237cf761215175f390021344f5530101cca8164d69878b8a1779e80c" +checksum = "ce01ac37fdc85f10a43c43bc582cbd566720357011578a935761075f898baf58" dependencies = [ "futures-io", "futures-util", @@ -3436,6 +3436,7 @@ dependencies = [ "log", "nalgebra", "parking_lot 0.12.1", + "protobuf 3.2.0", "rand", "rapier2d-f64", "reqwest", diff --git a/api/src/config.rs b/api/src/config.rs index dcda4cd81ddffb5e488b9f527a6fb865b96998d9..792358a0d6e1dace41039fc3456d6d9bbbac8396 100644 --- a/api/src/config.rs +++ b/api/src/config.rs @@ -32,7 +32,7 @@ pub struct StarkingdomsApiConfig { pub base: String, pub game: String, pub realms: HashMap, - pub servers: Vec + pub servers: Vec, } #[derive(Serialize, Deserialize, Debug)] diff --git a/api/src/main.rs b/api/src/main.rs index c777468f0bba810512e48195bb96ad2eff1a051f..664c0743161b95d0036a8474012edad5b241dc62 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -1,5 +1,6 @@ use crate::config::CONFIG; use crate::error::{APIError, APIErrorsResponse}; +use actix_cors::Cors; use actix_request_identifier::RequestIdentifier; use actix_web::http::header::HeaderValue; use actix_web::web::{Data, JsonConfig}; @@ -9,7 +10,6 @@ use sea_orm::{ConnectOptions, Database, DatabaseConnection}; use starkingdoms_api_migration::{Migrator, MigratorTrait}; use std::error::Error; use std::time::Duration; -use actix_cors::Cors; use tera::Tera; pub mod config; diff --git a/api/src/routes/mod.rs b/api/src/routes/mod.rs index 9d0608a478b1711a25252a3eddfd4e0c979a05e0..2b2c6854b81248512f3f22d25504227b6cec0ee6 100644 --- a/api/src/routes/mod.rs +++ b/api/src/routes/mod.rs @@ -2,4 +2,4 @@ pub mod beamin; pub mod beamout; pub mod callback; pub mod select_realm; -pub mod server_list; \ No newline at end of file +pub mod server_list; diff --git a/api/src/routes/select_realm.rs b/api/src/routes/select_realm.rs index 15b10e7227f182ab367300b6aea993729d6933ce..9492f46f24dabcedc51a3668f11561d8ccca4ded 100644 --- a/api/src/routes/select_realm.rs +++ b/api/src/routes/select_realm.rs @@ -16,7 +16,7 @@ pub struct RealmsListTemplateContext { #[get("/select-realm")] pub async fn select_realm(state: Data) -> HttpResponse { - let context = match Context::from_serialize(&RealmsListTemplateContext { + let context = match Context::from_serialize(RealmsListTemplateContext { back_to: format!("{}/callback", CONFIG.base), realms: CONFIG.realms.clone(), }) { diff --git a/api/src/routes/server_list.rs b/api/src/routes/server_list.rs index f3504420c15310401b94d970d0fc5e869e6f2676..60f4755c38fc2258c9d2a8b6cb396ebbad4c571c 100644 --- a/api/src/routes/server_list.rs +++ b/api/src/routes/server_list.rs @@ -1,11 +1,11 @@ -use actix_web::{get}; +use crate::config::CONFIG; +use actix_web::get; use actix_web::web::Json; use serde::Serialize; -use crate::config::CONFIG; #[derive(Serialize)] pub struct ServerListResponse { - pub servers: Vec + pub servers: Vec, } #[get("server-list")] @@ -13,4 +13,4 @@ pub async fn server_list() -> Json { Json(ServerListResponse { servers: CONFIG.servers.clone(), }) -} \ No newline at end of file +} diff --git a/client/index.html b/client/index.html index 6bcdb055aea1f3a63194a0b75b2a6b79cb103bd7..80c2b6f37b7a097116d82bf2da2a7a4127ed1729 100644 --- a/client/index.html +++ b/client/index.html @@ -115,9 +115,5 @@ width: 100%; } - .w-90 { - width: 90%; - } - diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 5c4912b6af7b838abcd7f5eb8b32def2a5391d24..2f99751b70610b063726c8af51261040c1b09494 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -1,11 +1,11 @@ use crate::message_c2s::{ MessageC2SAuthenticateAndBeamOut, MessageC2SChat, MessageC2SGoodbye, MessageC2SHello, MessageC2SInput, MessageC2SModuleDetach, MessageC2SModuleGrabBegin, MessageC2SModuleGrabEnd, - MessageC2SMouseInput, MessageC2SPing + MessageC2SMouseInput, MessageC2SPing, }; use crate::message_s2c::{ - MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CModulesUpdate, - MessageS2CModuleAdd, MessageS2CModuleRemove, MessageS2CModuleTreeUpdate, + MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CModuleAdd, + MessageS2CModuleRemove, MessageS2CModuleTreeUpdate, MessageS2CModulesUpdate, MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong, }; use crate::planet::PlanetType; @@ -50,7 +50,7 @@ impl TryFrom<&[u8]> for MessageC2S { type Error = Box; fn try_from(value: &[u8]) -> Result { - let pkt = starkingdoms_protocol::PacketWrapper::parse_from_bytes(value)?; + let pkt = PacketWrapper::parse_from_bytes(value)?; let deser_pkt = match pkt.packet_id { _id if _id == message_c2s::message_c2shello::Packet_info::type_.value() as i64 => { @@ -84,17 +84,23 @@ impl TryFrom<&[u8]> for MessageC2S { _id if _id == message_c2s::message_c2smodule_grab_begin::Packet_info::type_.value() as i64 => { - MessageC2S::ModuleGrabBegin(MessageC2SModuleGrabBegin::parse_from_bytes(&pkt.packet_data)?) + MessageC2S::ModuleGrabBegin(MessageC2SModuleGrabBegin::parse_from_bytes( + &pkt.packet_data, + )?) } _id if _id == message_c2s::message_c2smodule_grab_end::Packet_info::type_.value() as i64 => { - MessageC2S::ModuleGrabEnd(MessageC2SModuleGrabEnd::parse_from_bytes(&pkt.packet_data)?) + MessageC2S::ModuleGrabEnd(MessageC2SModuleGrabEnd::parse_from_bytes( + &pkt.packet_data, + )?) } _id if _id == message_c2s::message_c2smodule_detach::Packet_info::type_.value() as i64 => { - MessageC2S::ModuleDetach(MessageC2SModuleDetach::parse_from_bytes(&pkt.packet_data)?) + MessageC2S::ModuleDetach(MessageC2SModuleDetach::parse_from_bytes( + &pkt.packet_data, + )?) } _id => { return Err(format!("Unrecognized C2S packet {}", _id).into()); @@ -201,18 +207,15 @@ impl TryFrom<&[u8]> for MessageS2C { )?) } _id if _id - == message_s2c::message_s2cmodule_tree_update::Packet_info::type_.value() as i64 => + == message_s2c::message_s2cmodule_tree_update::Packet_info::type_.value() + as i64 => { MessageS2C::ModuleTreeUpdate(MessageS2CModuleTreeUpdate::parse_from_bytes( &pkt.packet_data, )?) } - _id if _id - == message_s2c::message_s2cmodule_add::Packet_info::type_.value() as i64 => - { - MessageS2C::ModuleAdd(MessageS2CModuleAdd::parse_from_bytes( - &pkt.packet_data, - )?) + _id if _id == message_s2c::message_s2cmodule_add::Packet_info::type_.value() as i64 => { + MessageS2C::ModuleAdd(MessageS2CModuleAdd::parse_from_bytes(&pkt.packet_data)?) } _id if _id == message_s2c::message_s2cmodule_remove::Packet_info::type_.value() as i64 => @@ -287,7 +290,7 @@ impl TryInto> for MessageS2C { } } -impl planet::PlanetType { +impl PlanetType { pub fn as_texture_id(&self) -> String { match self { PlanetType::Earth => "earth".to_string(), diff --git a/server/Cargo.toml b/server/Cargo.toml index c5c08415a81b65f911b3397fba0fac53afc69b76..c3c87872eda96e5d77e4b76bff38e87af9639501 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -17,7 +17,7 @@ serde_json = "1" futures = { version = "0.3", default-features = false } tungstenite = { version = "0.19.0", default-features = false } -async-tungstenite = "0.21.0" +async-tungstenite = "0.22.1" log = "0.4" simple_logger = "4.1" @@ -28,6 +28,8 @@ nalgebra = "0.32.2" rand = "0.8.5" reqwest = "0.11.16" +protobuf = "3.2.0" + parking_lot = { version = "0.12.1", features = ["deadlock_detection"] } [build-dependencies] diff --git a/server/src/api.rs b/server/src/api.rs index e1734b42e7112a41c325b02a41e4f4aea02d9b9a..2b3fa24d1f7b3fd608372bf665130a674e44681a 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -31,7 +31,7 @@ pub async fn load_player_data_from_api( }; let res = client - .post(format!("{}/beamin", std::env::var("STK_API_URL").unwrap())) + .post(format!("{}/beamin", std::env::var("STK_API_URL")?)) .header("Content-Type", "application/json") .body(serde_json::to_string(&req_body)?) .send() @@ -75,11 +75,11 @@ pub async fn save_player_data_to_api( 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.clone(), }; let res = client - .post(format!("{}/beamout", std::env::var("STK_API_URL").unwrap())) + .post(format!("{}/beamout", std::env::var("STK_API_URL")?)) .header("Content-Type", "application/json") .body(serde_json::to_string(&req_body)?) .send() diff --git a/server/src/entity.rs b/server/src/entity.rs index 2cc580d414b5f4ab53ef02dbf0ff7b6e32b915c3..bbf37777e0a5ca64c7c4b0c17dfa0cc693550093 100644 --- a/server/src/entity.rs +++ b/server/src/entity.rs @@ -1,12 +1,14 @@ use std::{collections::HashMap, net::SocketAddr, sync::atomic::AtomicU32}; use nalgebra::Vector2; +use protobuf::SpecialFields; use starkingdoms_protocol::planet::PlanetType; use crate::{ manager::{ClientHandlerMessage, Player}, + module::{AttachedModule, Module}, planet::Planet, - SCALE, module::{Module, AttachedModule}, + SCALE, }; pub type EntityId = u32; @@ -15,9 +17,7 @@ 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") - }; + assert!(id <= 4_147_483_600, "No remaining entity ids"); id } @@ -27,8 +27,8 @@ pub struct EntityHandler { } impl EntityHandler { - pub fn new() -> EntityHandler { - EntityHandler { + pub fn new() -> Self { + Self { entities: Entities::new(), } } @@ -71,7 +71,7 @@ impl EntityHandler { } } pub fn get_player_id(&self, addr: SocketAddr) -> Option { - for (id, entity) in self.entities.iter() { + for (id, entity) in &self.entities { if let Entity::Player(player) = entity { if player.addr == addr { return Some(*id); @@ -83,10 +83,7 @@ impl EntityHandler { pub fn get_player(&self, addr: SocketAddr) -> Option { let players = self.get_players(); let player = players.iter().find(|p| p.0 == addr); - match player { - Some(p) => Some(p.1.clone()), - None => None, - } + player.map(|p| p.1.clone()) } pub fn get_modules(&self) -> Vec { let mut modules = Vec::new(); @@ -99,9 +96,9 @@ impl EntityHandler { } pub fn get_modules_id(&self) -> Vec<(EntityId, Module)> { let mut modules = Vec::new(); - for (id, entity) in self.entities.clone().into_iter() { + for (id, entity) in self.entities.clone() { if let Entity::Module(module) = entity { - modules.push((id as u32, module.clone())); + modules.push((id, module.clone())); } } modules @@ -122,7 +119,7 @@ impl EntityHandler { None } pub fn get_from_module(&self, p_module: &Module) -> Option { - for (id, entity) in self.entities.iter() { + for (id, entity) in &self.entities { if let Entity::Module(module) = entity { if module.handle == p_module.handle { return Some(*id); @@ -146,8 +143,8 @@ impl EntityHandler { } None } - pub fn get_id_from_attached(&self, p_module: AttachedModule) -> Option { - for (id, entity) in self.entities.iter() { + pub fn get_id_from_attached(&self, p_module: &AttachedModule) -> Option { + for (id, entity) in &self.entities { if let Entity::AttachedModule(module) = entity { if module.handle == p_module.handle { return Some(*id); @@ -177,7 +174,7 @@ impl EntityHandler { x: planet.position.0 * SCALE, y: planet.position.1 * SCALE, radius: planet.radius * SCALE, // DO NOT * SCALE. THIS VALUE IS NOT SCALED! YES IT IS - special_fields: Default::default(), + special_fields: SpecialFields::default(), }); } diff --git a/server/src/handler.rs b/server/src/handler.rs index df82955c288fa700530be8f524c16b9d9e1165c2..75cca58bd1a5aa9c325ac8d0bc99616384a82157 100644 --- a/server/src/handler.rs +++ b/server/src/handler.rs @@ -1,8 +1,6 @@ use crate::api::{load_player_data_from_api, save_player_data_to_api}; use crate::entity::{get_entity_id, Entity, EntityHandler}; -use crate::manager::{ - ClientHandlerMessage, ClientManager, Player, PhysicsData, -}; +use crate::manager::{ClientHandlerMessage, ClientManager, PhysicsData, Player}; use crate::module::{AttachedModule, Module}; use crate::{recv, send, SCALE}; use async_std::net::TcpStream; @@ -14,12 +12,13 @@ use log::{error, info, warn}; use nalgebra::{point, vector, Vector2}; use rand::Rng; use rapier2d_f64::prelude::{ - Collider, ColliderBuilder, MassProperties, RigidBodyBuilder, RigidBodyType, Isometry, + Collider, ColliderBuilder, Isometry, MassProperties, RigidBodyBuilder, RigidBodyType, }; use starkingdoms_protocol::goodbye_reason::GoodbyeReason; use starkingdoms_protocol::message_s2c::{ - MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CModulesUpdate, - MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong, MessageS2CModuleAdd, MessageS2CModuleTreeUpdate, MessageS2CModuleRemove, + MessageS2CChat, MessageS2CGoodbye, MessageS2CHello, MessageS2CModuleAdd, + MessageS2CModuleRemove, MessageS2CModuleTreeUpdate, MessageS2CModulesUpdate, + MessageS2CPlanetData, MessageS2CPlayersUpdate, MessageS2CPong, }; use starkingdoms_protocol::state::State; @@ -29,6 +28,7 @@ use std::f64::consts::PI; use std::net::SocketAddr; use std::sync::Arc; use std::time::{Duration, SystemTime}; +use protobuf::SpecialFields; use tungstenite::Message; pub async fn handle_client( @@ -123,6 +123,8 @@ pub async fn handle_client( match pkt { MessageC2S::Hello(pkt) => { info!("client sent hello"); + // there is no way to not use unwrap here :/ + #[allow(clippy::unwrap_used)] if !matches!(pkt.next_state.unwrap(), State::Play) { error!( "client sent unexpected state {:?} (expected: Play)", @@ -130,7 +132,7 @@ pub async fn handle_client( ); let msg = MessageS2C::Goodbye(MessageS2CGoodbye { reason: GoodbyeReason::UnexpectedNextState.into(), - special_fields: Default::default(), + special_fields: SpecialFields::default(), }) .try_into()?; send!(client_tx, msg).await?; @@ -186,7 +188,7 @@ pub async fn handle_client( let msg = MessageS2C::Hello(MessageS2CHello { version: pkt.version, given_username: pkt.requested_username.clone(), - special_fields: Default::default(), + special_fields: SpecialFields::default(), next_state: pkt.next_state, }) .try_into()?; @@ -252,7 +254,7 @@ pub async fn handle_client( let player_data = match load_player_data_from_api( &pkt.token, &pkt.user, - &std::env::var("STK_API_KEY").unwrap(), + &std::env::var("STK_API_KEY")?, ) .await { @@ -317,7 +319,6 @@ pub async fn handle_client( }, 2, );*/ - } } MessageC2S::Goodbye(pkt) => { @@ -331,7 +332,7 @@ pub async fn handle_client( ); let msg = MessageS2C::Goodbye(MessageS2CGoodbye { reason: GoodbyeReason::UnexpectedPacket.into(), - special_fields: Default::default(), + special_fields: SpecialFields::default(), }) .try_into()?; send!(client_tx, msg).await?; @@ -347,7 +348,7 @@ pub async fn handle_client( ); let msg = MessageS2C::Goodbye(MessageS2CGoodbye { reason: GoodbyeReason::UnexpectedPacket.into(), - special_fields: Default::default(), + special_fields: SpecialFields::default(), }) .try_into()?; send!(client_tx, msg).await?; @@ -388,11 +389,12 @@ pub async fn handle_client( let mut handle = entities.write().await; let id = handle .get_player_id(remote_addr) - .expect("could not get player id"); + .ok_or("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") + .ok_or("player disconnected but continued to send packets")? { me.input.up = p.up_pressed; me.input.down = p.down_pressed; @@ -410,7 +412,7 @@ pub async fn handle_client( .read() .await .get_player(remote_addr) - .expect("Player sending messages after disconnect"); + .ok_or("Player sending messages after disconnect")?; if Some(p.token) != player.auth_token || Some(p.user_id) != player.auth_user { @@ -420,9 +422,9 @@ pub async fn handle_client( 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(), + &player.auth_token.ok_or("Tried to beamout without an auth token")?, + &player.auth_user.ok_or("Tried to beamout without setting a user")?, + &std::env::var("STK_API_KEY")?, ) .await { @@ -451,19 +453,24 @@ pub async fn handle_client( //debug!("[{}] detach: {:?}", remote_addr, p); //debug!("[{}] {:?}", remote_addr, entities.entities); - if let Some(Entity::AttachedModule(p_module)) = entities.entities.get_mut(&p.module_id) { + if let Some(Entity::AttachedModule(p_module)) = + entities.entities.get_mut(&p.module_id) + { module = Some(p_module.clone()); } else { warn!("[{}] attempted to detach nonexistent module", remote_addr); continue; } - let player_id = entities.get_player_id(remote_addr).unwrap(); - let module_id = AttachedModule::detach(&mut data_handle, &mut entities, - player_id, - module.unwrap()); - let module = entities.get_module_from_id(module_id).unwrap(); - let body = data_handle.rigid_body_set.get(module.handle).unwrap(); + let player_id = entities.get_player_id(remote_addr).ok_or("player does not exist")?; + let module_id = AttachedModule::detach( + &mut data_handle, + &mut entities, + player_id, + module.ok_or("cannot detach module that doesn't exist")?, + ).ok_or("detach failed")?; + let module = entities.get_module_from_id(module_id).ok_or("player does not exist")?; + let body = data_handle.rigid_body_set.get(module.handle).ok_or("module rigidbody does not exist")?; let prot_module = starkingdoms_protocol::module::Module { module_type: module.module_type.into(), rotation: body.rotation().angle(), @@ -471,17 +478,23 @@ pub async fn handle_client( y: body.translation().y * SCALE, id: module_id, flags: module.flags, - special_fields: Default::default(), + special_fields: SpecialFields::default(), }; let msg = MessageS2C::ModuleRemove(MessageS2CModuleRemove { module: Some(prot_module).into(), - special_fields: Default::default(), + special_fields: SpecialFields::default(), }) .try_into()?; send!(client_tx, msg).await?; } MessageC2S::ModuleGrabBegin(p) => { - if let Entity::Module(_module) = entities.write().await.entities.get_mut(&p.module_id).unwrap() { + if let Entity::Module(_module) = entities + .write() + .await + .entities + .get_mut(&p.module_id) + .ok_or("module does not exist")? + { //debug!("[{}] grab begin: {:?}, flags: {}", remote_addr, p, module.flags); } } @@ -490,75 +503,139 @@ pub async fn handle_client( let mut module: Option = None; let mut did_attach = false; let mut attached_id = None; - if let Entity::Module(p_module) = entities.entities.get_mut(&p.module_id).unwrap() { + if let Entity::Module(p_module) = + entities.entities.get_mut(&p.module_id).ok_or("module does not exist")? + { module = Some(p_module.clone()); //debug!("[{}] grab end: {:?}", remote_addr, p); } let mut data_handle = data.write().await; - let player_id = entities.get_player_id(remote_addr).unwrap(); - let player = entities.get_player(remote_addr).unwrap(); - let body = data_handle.rigid_body_set.get(player.handle).unwrap(); - let (x, y) = (body.translation().x - p.worldpos_x/SCALE, body.translation().y - p.worldpos_y/SCALE); + let player_id = entities.get_player_id(remote_addr).ok_or("player entity does not exist")?; + let player = entities.get_player(remote_addr).ok_or("player does not exist")?; + let body = data_handle.rigid_body_set.get(player.handle).ok_or("player rigidbody does not exist")?; + let (x, y) = ( + body.translation().x - p.worldpos_x / SCALE, + body.translation().y - p.worldpos_y / SCALE, + ); let angle = -body.rotation().angle(); - let (x, y) = (x*angle.cos() - y*angle.sin(), x*angle.sin() + y*angle.cos()); + let (x, y) = ( + x.mul_add(angle.cos(), -y * angle.sin()), + x.mul_add(angle.sin(), y * angle.cos()), + ); if 1.5 < y && y < 3. && -2. < x && x < 2. { - attached_id = Some(AttachedModule::attach(&mut data_handle, &mut entities, - player_id, - player_id, module.clone().unwrap(), 2)); + attached_id = Some(AttachedModule::attach( + &mut data_handle, + &mut entities, + player_id, + player_id, + module.clone().ok_or("module is None")?, + 2, + )); did_attach = true; } else if -3. < y && y < -1.5 && -2. < x && x < 2. { - attached_id = Some(AttachedModule::attach(&mut data_handle, &mut entities, - player_id, - player_id, module.clone().unwrap(), 0)); + attached_id = Some(AttachedModule::attach( + &mut data_handle, + &mut entities, + player_id, + player_id, + module.clone().ok_or("module is None")?, + 0, + )); did_attach = true; } else if -3. < x && x < -1.5 && -2. < y && y < 2. { - attached_id = Some(AttachedModule::attach(&mut data_handle, &mut entities, - player_id, - player_id, module.clone().unwrap(), 3)); + attached_id = Some(AttachedModule::attach( + &mut data_handle, + &mut entities, + player_id, + player_id, + module.clone().ok_or("module is None")?, + 3, + )); did_attach = true; } else if 1.5 < x && x < 3. && -2. < y && y < 2. { - attached_id = Some(AttachedModule::attach(&mut data_handle, &mut entities, - player_id, - player_id, module.clone().unwrap(), 1)); + attached_id = Some(AttachedModule::attach( + &mut data_handle, + &mut entities, + player_id, + player_id, + module.clone().ok_or("module is None")?, + 1, + )); did_attach = true; } let modules = player.search_modules(&entities); for attached in modules { - let body = data_handle.rigid_body_set.get(attached.handle).unwrap(); - let (x, y) = (body.translation().x - p.worldpos_x/SCALE, body.translation().y - p.worldpos_y/SCALE); + let body = data_handle.rigid_body_set.get(attached.handle).ok_or("attached module rigidbody does not exist")?; + let (x, y) = ( + body.translation().x - p.worldpos_x / SCALE, + body.translation().y - p.worldpos_y / SCALE, + ); let angle = -body.rotation().angle(); - let (x, y) = (x*angle.cos() - y*angle.sin(), x*angle.sin() + y*angle.cos()); - let parent_id = entities.get_id_from_attached(attached).unwrap(); + let (x, y) = ( + x.mul_add(angle.cos(), -y * angle.sin()), + x.mul_add(angle.sin(), y * angle.cos()), + ); + let parent_id = entities.get_id_from_attached(&attached).ok_or("attached module does not exist")?; + + // ghostly: this is cursed as hell + // please find a better way in the future lmao if 1.5 < y && y < 3. && -2. < x && x < 2. { - attached_id = Some(AttachedModule::attach(&mut data_handle, &mut entities, - parent_id, - player_id, module.clone().unwrap(), 2)); + attached_id = Some(AttachedModule::attach( + &mut data_handle, + &mut entities, + parent_id, + player_id, + module.clone().ok_or("module does not exist")?, + 2, + )); did_attach = true; } else if -3. < x && x < -1.5 && -2. < y && y < 2. { - attached_id = Some(AttachedModule::attach(&mut data_handle, &mut entities, - parent_id, - player_id, module.clone().unwrap(), 3)); + attached_id = Some(AttachedModule::attach( + &mut data_handle, + &mut entities, + parent_id, + player_id, + module.clone().ok_or("module does not exist")?, + 3, + )); did_attach = true; } else if 1.5 < x && x < 3. && -2. < y && y < 2. { - attached_id = Some(AttachedModule::attach(&mut data_handle, &mut entities, - parent_id, - player_id, module.clone().unwrap(), 1)); + attached_id = Some(AttachedModule::attach( + &mut data_handle, + &mut entities, + parent_id, + player_id, + module.clone().ok_or("module does not exist")?, + 1, + )); did_attach = true; } } - if did_attach == false { - let body = data_handle.rigid_body_set.get_mut(module.unwrap().handle).unwrap(); - body.set_position(Isometry::new(Vector2::new(p.worldpos_x/SCALE, p.worldpos_y/SCALE),body.rotation().angle()), true); + if !did_attach { + let body = data_handle + .rigid_body_set + .get_mut(module.ok_or("module does not exist")?.handle) + .ok_or("module rigidbody does not exist")?; + body.set_position( + Isometry::new( + Vector2::new(p.worldpos_x / SCALE, p.worldpos_y / SCALE), + body.rotation().angle(), + ), + true, + ); + } else if let Some(Ok(id)) = attached_id { + let prot_module = entities + .get_attached_from_id(id) + .ok_or("attached module does not exist")? + .to_protocol(&entities, &data_handle.rigid_body_set); + let msg = MessageS2C::ModuleAdd(MessageS2CModuleAdd { + module: Some(prot_module.ok_or("attached module does not exist")?).into(), + special_fields: SpecialFields::default(), + }) + .try_into()?; + send!(client_tx, msg).await?; } else { - if let Some(Ok(id)) = attached_id { - let prot_module = entities.get_attached_from_id(id).unwrap().to_protocol(&entities, &mut data_handle.rigid_body_set); - let msg = MessageS2C::ModuleAdd(MessageS2CModuleAdd { - module: Some(prot_module).into(), - special_fields: Default::default(), - }) - .try_into()?; - send!(client_tx, msg).await?; - } + warn!("attached ID does not exist"); } } }, diff --git a/server/src/main.rs b/server/src/main.rs index 02212b8fe25104f229196d410c56804d7508a392..cf04f032cea67c708dd9c1f3a5d343a72b806e65 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,3 +1,19 @@ +// StarKingdoms.IO, an open source browser game +// Copyright (C) 2023 ghostly_zsh (and contributors, depending on the license you choose) +// +// + +#![warn(clippy::pedantic)] +#![warn(clippy::nursery)] +#![deny(clippy::unwrap_used)] +#![deny(clippy::expect_used)] +#![allow(clippy::must_use_candidate)] +#![allow(clippy::too_many_lines)] +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::missing_errors_doc)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::missing_panics_doc)] + use crate::entity::Entity; use crate::handler::handle_client; use crate::manager::{ClientHandler, ClientManager}; @@ -12,17 +28,18 @@ use lazy_static::lazy_static; use log::{error, info, warn, Level}; use manager::PhysicsData; use nalgebra::vector; +use parking_lot::deadlock::check_deadlock; use rapier2d_f64::prelude::{ BroadPhase, CCDSolver, ColliderSet, ImpulseJointSet, IntegrationParameters, IslandManager, MultibodyJointSet, NarrowPhase, RigidBodySet, }; use serde::{Deserialize, Serialize}; use starkingdoms_protocol::PROTOCOL_VERSION; +use std::collections::HashMap; use std::error::Error; use std::net::SocketAddr; use std::thread; use std::time::Duration; -use parking_lot::deadlock::check_deadlock; pub mod handler; pub mod manager; @@ -31,9 +48,9 @@ pub mod timer; pub mod macros; pub mod api; pub mod entity; +pub mod module; pub mod orbit; pub mod planet; -pub mod module; const SCALE: f64 = 10.0; @@ -58,7 +75,7 @@ async fn _handle_request( mgr: ClientManager, entities: Arc>, physics_data: Arc>, -) -> Result<(), Box> { +) -> Result<(), Box> { let mut peek_buf = [0u8; 9]; loop { @@ -78,10 +95,9 @@ async fn _handle_request( channel: env!("STK_CHANNEL").to_string(), build: env!("STK_BUILD").to_string(), }, - players: CMGR.usernames.read().await.len() as u32, + players: u32::try_from(CMGR.usernames.read().await.len())?, description: env!("STK_SLP_DESCRIPTION").to_string(), - }) - .unwrap(); + })?; let resp_str = format!( "HTTP/1.0 200 OK\nAccess-Control-Allow-Origin: *\nContent-Length: {}\n\n{}", @@ -130,9 +146,11 @@ async fn _handle_request( { Ok(_) => (), Err(e) if e.is::() => { - let e = e - .downcast::() - .unwrap(); + #[allow(clippy::expect_used)] + let e = { + e.downcast::().expect("unable to convert between types safely") + }; + if matches!(*e, async_tungstenite::tungstenite::Error::ConnectionClosed) { info!("[{}] connection closed normally", remote_addr); } else { @@ -156,14 +174,12 @@ async fn _handle_request( let mut collider_set = data.collider_set.clone(); let mut impulse_joint_set = data.impulse_joint_set.clone(); let mut multibody_joint_set = data.multibody_joint_set.clone(); - let player_id = match entity_write.get_player_id(remote_addr) { - Some(s) => s, - None => { + let Some(player_id) = entity_write.get_player_id(remote_addr) else { warn!("[{}] player missing from entities.players", remote_addr); return Err("Player missing from entities.players".into()); - } }; - if let Entity::Player(player) = entity_write.entities.get(&player_id).unwrap() { + + if let Some(Entity::Player(player)) = entity_write.entities.get(&player_id) { rigid_body_set.remove( player.handle, &mut island_manager, @@ -181,9 +197,13 @@ async fn _handle_request( &mut multibody_joint_set, true, ); - let module_id = entity_write.get_id_from_attached(module).unwrap(); + let module_id = entity_write + .get_id_from_attached(&module) + .ok_or("Tried to remove nonexistent module")?; entity_write.entities.remove(&module_id); } + } else { + warn!("tried to remove player that doesnt exist: #{}", player_id); } data.rigid_body_set = rigid_body_set; data.collider_set = collider_set; @@ -198,8 +218,8 @@ async fn _handle_request( lazy_static! { static ref CMGR: ClientManager = ClientManager { - handlers: Arc::new(RwLock::new(Default::default())), - usernames: Arc::new(RwLock::new(Default::default())), + handlers: Arc::new(RwLock::new(HashMap::default())), + usernames: Arc::new(RwLock::new(HashMap::default())), }; static ref DATA: Arc> = Arc::new(RwLock::new(PhysicsData { gravity: vector![0.0, 0.0], @@ -224,9 +244,13 @@ lazy_static! { pub const PANIC_ON_DEADLOCK: bool = true; +//noinspection ALL #[async_std::main] async fn main() { - simple_logger::init_with_level(Level::Debug).expect("Unable to start logging service"); + #[allow(clippy::expect_used)] + { + simple_logger::init_with_level(Level::Debug).expect("Unable to start logging service"); + } info!( "StarKingdoms server (v: {}, build {}) - initializing", @@ -247,31 +271,29 @@ async fn main() { info!("Starting deadlock detector..."); - thread::spawn(move || { - loop { - thread::sleep(Duration::from_secs(10)); - let deadlocks = check_deadlock(); - if deadlocks.is_empty() { - continue; - } + thread::spawn(move || loop { + thread::sleep(Duration::from_secs(10)); + let deadlocks = check_deadlock(); + if deadlocks.is_empty() { + continue; + } - error!("---- DEADLOCK DETECTED ----"); - error!("{} deadlocks were detected.", deadlocks.len()); - for (i, threads) in deadlocks.iter().enumerate() { - error!("-= Deadlock #{}", i); - for t in threads { - error!("-= Thread ID = {:#?}", t.thread_id()); - error!("-= Backtrace:\n{:#?}", t.backtrace()); - } - } - if PANIC_ON_DEADLOCK { - error!("StarKingdoms is configured to panic when deadlocks are detected."); - error!("Bye!"); - panic!("Deadlock detected on one or more threads"); - } else { - error!("StarKingdoms is not configured to panic when deadlocks are detected."); + error!("---- DEADLOCK DETECTED ----"); + error!("{} deadlocks were detected.", deadlocks.len()); + for (i, threads) in deadlocks.iter().enumerate() { + error!("-= Deadlock #{}", i); + for t in threads { + error!("-= Thread ID = {:#?}", t.thread_id()); + error!("-= Backtrace:\n{:#?}", t.backtrace()); } } + if PANIC_ON_DEADLOCK { + error!("StarKingdoms is configured to panic when deadlocks are detected."); + error!("Bye!"); + panic!("Deadlock detected on one or more threads"); + } else { + error!("StarKingdoms is not configured to panic when deadlocks are detected."); + } }); let addr = SocketAddr::from(([0, 0, 0, 0], 3000)); diff --git a/server/src/manager.rs b/server/src/manager.rs index 80b68e7ad594e0b051195410ff49025f83c46d12..6da54da223525f072e911cfe9f3c521cdc4c1a2c 100644 --- a/server/src/manager.rs +++ b/server/src/manager.rs @@ -1,13 +1,10 @@ use async_std::channel::Sender; use async_std::sync::RwLock; - use rapier2d_f64::na::Vector2; use rapier2d_f64::prelude::{ - BroadPhase, CCDSolver, ColliderSet, - ImpulseJointSet, IntegrationParameters, IslandManager, - MultibodyJointSet, NarrowPhase, PhysicsPipeline, RigidBodyHandle, - RigidBodySet, + BroadPhase, CCDSolver, ColliderSet, ImpulseJointSet, IntegrationParameters, IslandManager, + MultibodyJointSet, NarrowPhase, PhysicsPipeline, RigidBodyHandle, RigidBodySet, }; use starkingdoms_protocol::api::APISavedPlayerData; @@ -18,7 +15,7 @@ use std::sync::Arc; use crate::entity::{Entity, EntityHandler, EntityId}; -use crate::module::{Attachment, AttachedModule}; +use crate::module::{AttachedModule, Attachment}; #[derive(Clone)] pub struct ClientManager { @@ -45,25 +42,29 @@ impl Player { pub fn search_modules(&self, entities: &EntityHandler) -> Vec { let mut modules = Vec::new(); for attachment in self.children.iter().flatten() { - if let Entity::AttachedModule(child_module) = - entities.entities.get(&attachment.child).unwrap() + if let Some(Entity::AttachedModule(child_module)) = + entities.entities.get(&attachment.child) { modules.append(&mut child_module.search_modules(entities)); } } modules } - pub fn find_parent(&self, module: AttachedModule, entities: &EntityHandler) -> Option<(u8, EntityId)> { + pub fn find_parent( + &self, + module: &AttachedModule, + entities: &EntityHandler, + ) -> Option<(u8, EntityId)> { for (slot, attachment) in self.children.iter().enumerate() { if let Some(attachment) = attachment { if let Entity::AttachedModule(child_module) = - entities.entities.get(&attachment.child).unwrap() + entities.entities.get(&attachment.child)? { - if *child_module == module { - return Some((slot as u8, entities.get_player_id(self.addr).unwrap())); + if *child_module == *module { + return Some((u8::try_from(slot).ok()?, entities.get_player_id(self.addr)?)); } let parent = child_module.find_parent(module.clone(), entities); - if let Some(_) = parent { + if parent.is_some() { return parent; } } diff --git a/server/src/module.rs b/server/src/module.rs index e6a859ecb4aa56a44ccbd1887c8ad6a884e92d3a..4c2f8c0c8755800780aa3c2cef164b2bb6f32117 100644 --- a/server/src/module.rs +++ b/server/src/module.rs @@ -1,11 +1,18 @@ +use std::error::Error; use std::f64::consts::PI; - -use nalgebra::{Vector2, point, vector, Isometry2, Unit}; -use rapier2d_f64::prelude::{RigidBodyHandle, Real, MassProperties, ColliderBuilder, RigidBodyBuilder, FixedJointBuilder, ImpulseJointHandle, RigidBodySet}; +use nalgebra::{point, vector, Isometry2, Unit, Vector2}; +use rapier2d_f64::prelude::{ + ColliderBuilder, FixedJointBuilder, ImpulseJointHandle, MassProperties, Real, RigidBodyBuilder, + RigidBodyHandle, RigidBodySet, +}; use starkingdoms_protocol::module::ModuleType; -use crate::{entity::{EntityId, EntityHandler, Entity, get_entity_id}, manager::PhysicsData, SCALE}; +use crate::{ + entity::{get_entity_id, Entity, EntityHandler, EntityId}, + manager::PhysicsData, + SCALE, +}; #[derive(Debug, Clone)] pub struct Module { @@ -37,79 +44,53 @@ impl AttachedModule { player_id: EntityId, module: Module, attachment_slot: usize, - ) -> Result { + ) -> Result> { let mut entity_map = entities.entities.clone(); let parent_entity = entity_map .get_mut(&parent) - .expect("parent id does not exist"); + .ok_or("parent id does not exist")?; let parent_handle = match parent_entity { Entity::Player(player) => { - if let Some(_child) = &player.children[attachment_slot] { - return Err(()); - } else { - player.handle + if player.children[attachment_slot].is_some() { + return Err("already attached".into()); } - }, + player.handle + } Entity::AttachedModule(module) => { - if let Some(_child) = &module.children[attachment_slot] { - return Err(()); - } else { - module.handle + if module.children[attachment_slot].is_some() { + return Err("already attached".into()); } - }, + module.handle + } _ => { panic!("unexpected parent"); } }; - let parent_body = data.rigid_body_set.get(parent_handle).expect("Parent body does not exist"); + let parent_body = data + .rigid_body_set + .get(parent_handle).ok_or("parent body does not exist")?; let parent_pos = vector![parent_body.translation().x, parent_body.translation().y]; let parent_angle = parent_body.rotation().angle(); - let parent_linvel = parent_body.linvel().clone(); + let parent_linvel = *parent_body.linvel(); let parent_angvel = parent_body.angvel(); let (anchor, rotation) = match attachment_slot { - 0 => { - (point![ - 0. / SCALE, - 53. / SCALE - ], PI) - } - 1 => { - (point![ - -53. / SCALE, - 0. / SCALE - ], -PI/2.) - } - 2 => { - (point![ - 0. / SCALE, - -53. / SCALE - ], 0.) - } - 3 => { - (point![ - 53. / SCALE, - 0. / SCALE - ], PI/2.) - } - _ => { - (point![ - 0. / SCALE, - 53. / SCALE - ], 0.) - } + 0 => (point![0. / SCALE, 53. / SCALE], PI), + 1 => (point![-53. / SCALE, 0. / SCALE], -PI / 2.), + 2 => (point![0. / SCALE, -53. / SCALE], 0.), + 3 => (point![53. / SCALE, 0. / SCALE], PI / 2.), + _ => (point![0. / SCALE, 53. / SCALE], 0.), }; if let Some(id) = entities.get_from_module(&module) { - let relative_pos = - vector![anchor.x * (parent_body.rotation().angle()).cos() + - anchor.y * -(parent_body.rotation().angle()).sin(), - anchor.x * (parent_body.rotation().angle()).sin() + - anchor.y * (parent_body.rotation().angle()).cos()]; + let relative_pos = vector![ + anchor.x.mul_add((parent_body.rotation().angle()).cos(), anchor.y * -(parent_body.rotation().angle()).sin()), + anchor.x.mul_add((parent_body.rotation().angle()).sin(), anchor.y * (parent_body.rotation().angle()).cos()) + ]; let module_pos = parent_pos + relative_pos; - let module_body = data.rigid_body_set.get_mut(module.handle).unwrap(); + let module_body = data.rigid_body_set.get_mut(module.handle).ok_or("module body does not exist")?; module_body.set_translation(module_pos, true); module_body.set_rotation(Unit::from_angle(parent_angle + rotation), true); module_body.set_linvel(parent_linvel, true); @@ -123,7 +104,7 @@ impl AttachedModule { let attach_joint_handle = data.impulse_joint_set .insert(parent_handle, module.handle, attach_joint, true); - let attached_module = AttachedModule { + let attached_module = Self { handle: module.handle, module_type: module.module_type, player_id, @@ -152,23 +133,22 @@ impl AttachedModule { entities.entities = entity_map; return Ok(attached_id); } - Err(()) + Err("entity does not exist".into()) } pub fn detach( data: &mut PhysicsData, entities: &mut EntityHandler, player_id: EntityId, - module: AttachedModule, - ) -> EntityId { + module: Self, + ) -> Option { let mut entity_map = entities.entities.clone(); // player not in parent search // also no parents included in parent search - let player = entities.get_player_from_id(player_id).unwrap(); - let (slot, parent_id) = player.find_parent(module.clone(), entities).unwrap(); + let player = entities.get_player_from_id(player_id)?; + let (slot, parent_id) = player.find_parent(&module, entities)?; let parent_entity = entity_map - .get_mut(&parent_id) - .expect("parent id does not exist"); + .get_mut(&parent_id)?; match parent_entity { Entity::Player(ref mut player) => { @@ -195,28 +175,28 @@ impl AttachedModule { lifetime: 0., flags: 0, }; - entity_map.remove(&entities.get_id_from_attached(module.clone()).unwrap()); + entity_map.remove(&entities.get_id_from_attached(&module)?); let id = get_entity_id(); entity_map.insert(id, Entity::Module(new_module)); for element in tree { for child in element.clone().children { if let Some(child) = child { data.impulse_joint_set.remove(child.connection, true); - let child_body = entities.get_attached_from_id(child.child).unwrap(); + let child_body = entities.get_attached_from_id(child.child)?; let new_module = Module { handle: child_body.handle, module_type: child_body.module_type, lifetime: 0., flags: 0, }; - entity_map.remove(&entities.get_id_from_attached(child_body.clone()).unwrap()); + entity_map.remove(&entities.get_id_from_attached(&child_body)?); let attached_id = get_entity_id(); entity_map.insert(attached_id, Entity::Module(new_module)); } } } entities.entities = entity_map; - id + Some(id) } pub fn attach_new( data: &mut PhysicsData, @@ -225,12 +205,11 @@ impl AttachedModule { player_id: EntityId, module: ModuleTemplate, attachment_slot: usize, - ) -> EntityId { + ) -> Option { let mut entity_map = entities.entities.clone(); let parent_entity = entity_map - .get_mut(&parent) - .expect("parent id does not exist"); + .get_mut(&parent)?; let parent_handle = match parent_entity { Entity::Player(player) => player.handle, Entity::AttachedModule(module) => module.handle, @@ -239,47 +218,23 @@ impl AttachedModule { } }; - let parent_body = data.rigid_body_set.get(parent_handle).expect("Parent body does not exist"); + let parent_body = data + .rigid_body_set + .get(parent_handle)?; let parent_pos = vector![parent_body.translation().x, parent_body.translation().y]; let (anchor, rotation) = match attachment_slot { - 0 => { - (point![ - 0. / SCALE, - 53. / SCALE - ], PI) - } - 1 => { - (point![ - -53. / SCALE, - 0. / SCALE - ], -PI/2.) - } - 2 => { - (point![ - 0. / SCALE, - -53. / SCALE - ], 0.) - } - 3 => { - (point![ - 53. / SCALE, - 0. / SCALE - ], PI/2.) - } - _ => { - (point![ - 0. / SCALE, - 53. / SCALE - ], 0.) - } + 0 => (point![0. / SCALE, 53. / SCALE], PI), + 1 => (point![-53. / SCALE, 0. / SCALE], -PI / 2.), + 2 => (point![0. / SCALE, -53. / SCALE], 0.), + 3 => (point![53. / SCALE, 0. / SCALE], PI / 2.), + _ => (point![0. / SCALE, 53. / SCALE], 0.), }; - let relative_pos = - vector![anchor.x * (parent_body.rotation().angle()).cos() + - anchor.y * -(parent_body.rotation().angle()).sin(), - anchor.x * (parent_body.rotation().angle()).sin() + - anchor.y * (parent_body.rotation().angle()).cos()]; + let relative_pos = vector![ + anchor.x.mul_add((parent_body.rotation().angle()).cos(), anchor.y * -(parent_body.rotation().angle()).sin()), + anchor.x.mul_add((parent_body.rotation().angle()).sin(), anchor.y * (parent_body.rotation().angle()).cos()) + ]; let module_pos = parent_pos + relative_pos; // create attachment module @@ -305,7 +260,7 @@ impl AttachedModule { let attach_joint_handle = data.impulse_joint_set .insert(parent_handle, attached_handle, attach_joint, true); - let attached_module = AttachedModule { + let attached_module = Self { handle: attached_handle, module_type: module.module_type, player_id, @@ -331,7 +286,7 @@ impl AttachedModule { }; entity_map.insert(attached_id, Entity::AttachedModule(attached_module)); entities.entities = entity_map; - attached_id + Some(attached_id) } // TODO: remove this function pub fn to_module(&self) -> Module { @@ -343,17 +298,24 @@ impl AttachedModule { } } // TODO: this one too - pub fn to_module_id(&self, entities: &EntityHandler) -> (EntityId, Module) { - (entities.get_id_from_attached(self.clone()).unwrap(), Module { - handle: self.handle, - module_type: self.module_type, - lifetime: 10., - flags: 1, - }) + pub fn to_module_id(&self, entities: &EntityHandler) -> Option<(EntityId, Module)> { + Some(( + entities.get_id_from_attached(self)?, + Module { + handle: self.handle, + module_type: self.module_type, + lifetime: 10., + flags: 1, + }, + )) } - pub fn to_protocol(&self, entities: &EntityHandler, data: &RigidBodySet) -> starkingdoms_protocol::module::AttachedModule { - let body = data.get(self.handle).unwrap(); + pub fn to_protocol( + &self, + entities: &EntityHandler, + data: &RigidBodySet, + ) -> Option { + let body = data.get(self.handle)?; let children = self.children.to_vec(); let mut prot_children = Vec::new(); for i in 1..children.len() { @@ -365,22 +327,22 @@ impl AttachedModule { }); } } - starkingdoms_protocol::module::AttachedModule { + Some(starkingdoms_protocol::module::AttachedModule { module_type: self.module_type.into(), rotation: body.rotation().angle(), x: body.translation().x * SCALE, y: body.translation().y * SCALE, - id: entities.get_id_from_attached(self.clone()).unwrap(), + id: entities.get_id_from_attached(self)?, children: prot_children, special_fields: Default::default(), - } + }) } - pub fn search_modules(&self, entities: &EntityHandler) -> Vec { + pub fn search_modules(&self, entities: &EntityHandler) -> Vec { let mut modules = vec![self.clone()]; for attachment in self.children.iter().flatten() { - if let Entity::AttachedModule(child_module) = - entities.entities.get(&attachment.child).unwrap() + if let Some(Entity::AttachedModule(child_module)) = + entities.entities.get(&attachment.child) { modules.append(&mut child_module.search_modules(entities)); } @@ -388,17 +350,21 @@ impl AttachedModule { modules } - pub fn find_parent(&self, module: AttachedModule, entities: &EntityHandler) -> Option<(u8, EntityId)> { + pub fn find_parent( + &self, + module: Self, + entities: &EntityHandler, + ) -> Option<(u8, EntityId)> { for (slot, attachment) in self.children.iter().enumerate() { if let Some(attachment) = attachment { if let Entity::AttachedModule(child_module) = - entities.entities.get(&attachment.child).unwrap() + entities.entities.get(&attachment.child)? { if *child_module == module { - return Some((slot as u8, entities.get_id_from_attached(self.clone()).unwrap())); + return Some((slot as u8, entities.get_id_from_attached(self)?)); } let parent = child_module.find_parent(module.clone(), entities); - if let Some(_) = parent { + if parent.is_some() { return parent; } } @@ -408,7 +374,7 @@ impl AttachedModule { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Attachment { pub child: EntityId, pub connection: ImpulseJointHandle, diff --git a/server/src/orbit/constants.rs b/server/src/orbit/constants.rs index aa93646ad5c70f7217ad4f24e6564ae3cf5f80ea..f4550e329a55bc3ace022009e0a4897ce346f981 100644 --- a/server/src/orbit/constants.rs +++ b/server/src/orbit/constants.rs @@ -1,6 +1,6 @@ -pub const GAME_SCALE_DISTANCE: f64 = 0.0001567865; // 1000 / EARTH_RADIUS_RL -pub const GAME_SCALE_MASS: f64 = 0.0000000000000000000006697923643670463; // 4000 / EARTH_MASS_RL -pub const GAME_SCALE_TIME: f64 = 0.00051440329218107; // 1200 / MOON_ORBIT_TIME_RL +pub const GAME_SCALE_DISTANCE: f64 = 0.000_156_786_5; // 1000 / EARTH_RADIUS_RL +pub const GAME_SCALE_MASS: f64 = 0.000_000_000_000_000_000_000_669_792_364_367_046_3; // 4000 / EARTH_MASS_RL +pub const GAME_SCALE_TIME: f64 = 0.000_514_403_292_181_07; // 1200 / MOON_ORBIT_TIME_RL pub const GAME_ORBITS_ENABLED: bool = false; pub const EARTH_RADIUS_BIAS: f64 = 1.0; @@ -8,7 +8,7 @@ pub const EARTH_MASS_BIAS: f64 = 1.0; pub const EARTH_RADIUS_RL: f64 = 6_378_100.0; pub const EARTH_RADIUS: f64 = EARTH_RADIUS_RL * GAME_SCALE_DISTANCE * EARTH_RADIUS_BIAS; -pub const EARTH_MASS_RL: f64 = 5972000000000000000000000.0; +pub const EARTH_MASS_RL: f64 = 5_972_000_000_000_000_000_000_000.0; pub const EARTH_MASS: f64 = EARTH_MASS_RL * GAME_SCALE_MASS * EARTH_MASS_BIAS; pub const MOON_RADIUS_BIAS: f64 = 1.0; @@ -19,9 +19,9 @@ pub const MOON_ORBIT_TIME_BIAS: f64 = 1.0; pub const MOON_RADIUS_RL: f64 = 1_737_400.0; pub const MOON_RADIUS: f64 = MOON_RADIUS_RL * GAME_SCALE_DISTANCE * MOON_RADIUS_BIAS; -pub const MOON_MASS_RL: f64 = 73476730900000000000000.0; +pub const MOON_MASS_RL: f64 = 73_476_730_900_000_000_000_000.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_ORBIT_TIME_RL: f64 = 2332800.0; +pub const MOON_PERIAPSIS: f64 = 363_228_900.0 * GAME_SCALE_DISTANCE * MOON_PERIAPSIS_BIAS; +pub const MOON_APOAPSIS: f64 = 405_400_000.0 * GAME_SCALE_DISTANCE * MOON_APOAPSIS_BIAS; +pub const MOON_ORBIT_TIME_RL: f64 = 2_332_800.0; pub const MOON_ORBIT_TIME: f64 = MOON_ORBIT_TIME_RL * GAME_SCALE_TIME * MOON_ORBIT_TIME_BIAS; diff --git a/server/src/orbit/kepler.rs b/server/src/orbit/kepler.rs index 0ef980c9fa9b0cb1600b1cf3dde22f6ff13d41ac..64a23ff07a7d07a4b0d31651eb9a0ce9f90854fa 100644 --- a/server/src/orbit/kepler.rs +++ b/server/src/orbit/kepler.rs @@ -3,5 +3,5 @@ /// E is the Eccentric Anomaly (angle to where the body is on the ellipse) /// 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() + eccentricity.mul_add(eccentric_anomaly.sin(), mean_anomaly - eccentric_anomaly) } diff --git a/server/src/orbit/newtonian.rs b/server/src/orbit/newtonian.rs index 6b5786d124ba06e4b86dd3fe7b67e97f964394e2..0943323f7a940552262655e2c3c6ec37991e5267 100644 --- a/server/src/orbit/newtonian.rs +++ b/server/src/orbit/newtonian.rs @@ -1,7 +1,7 @@ use crate::orbit::kepler::kepler_equation; pub const NEWTONIAN_STEP_SIZE: f64 = 0.0001; -pub const NEWTONIAN_ACCEPTABLE_ERROR: f64 = 0.00000001; +pub const NEWTONIAN_ACCEPTABLE_ERROR: f64 = 0.000_000_01; pub fn solve_kepler_with_newtonian( mean_anomaly: f64, diff --git a/server/src/orbit/orbit.rs b/server/src/orbit/orbit.rs index fca217d7e9f8561f7048303416c9e8c6f850e40e..7623fc60e2a7537d7ab9d876c95dea4fb650362d 100644 --- a/server/src/orbit/orbit.rs +++ b/server/src/orbit/orbit.rs @@ -16,8 +16,9 @@ pub fn calculate_vector_of_orbit( mass: f64, step: f64, ) -> Vector2 { - let semi_major_length = (apoapsis + periapsis) / 2.0; - let _linear_eccentricity = semi_major_length - periapsis; // distance between center and focus + // this doesnt actually do anything + //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), @@ -45,14 +46,20 @@ 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 + .mul_add( + 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); let ellipse_center_x = -linear_eccentricity; - let point_x = eccentric_anomaly.cos() * semi_major_length + ellipse_center_x; + let point_x = eccentric_anomaly + .cos() + .mul_add(semi_major_length, ellipse_center_x); let point_y = eccentric_anomaly.sin() * semi_minor_length; vector![point_x, point_y] diff --git a/server/src/planet.rs b/server/src/planet.rs index ee692c953947dd8939ba7d7c8b71076a08d7041a..19d068ac01073f1831c4f97e0b1b854fdb66c5ae 100644 --- a/server/src/planet.rs +++ b/server/src/planet.rs @@ -1,4 +1,3 @@ - use nalgebra::{vector, Vector2}; use rapier2d_f64::prelude::{ ColliderBuilder, ColliderSet, RigidBodyBuilder, RigidBodyHandle, RigidBodySet, @@ -26,9 +25,7 @@ pub struct Planet { 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).hypot(position.1 - self.position.1); let force = GRAVITY * ((self.mass * mass) / (distance * distance)); let mut direction = Vector2::new(self.position.0 - position.0, self.position.1 - position.1); @@ -88,7 +85,7 @@ impl Planets { entities: &mut Entities, ) -> Vec { let mut planet_ids: Vec = Vec::new(); - let (earth_id, entity) = Planets::make_planet( + let (earth_id, entity) = Self::make_planet( "earth", PlanetType::Earth, EARTH_MASS, @@ -102,7 +99,8 @@ impl Planets { planet_ids.push(earth_id); let moon_start_point; - if let Entity::Planet(earth) = entities.get(&earth_id).unwrap() { + #[allow(clippy::expect_used)] + if let Entity::Planet(earth) = entities.get(&earth_id).expect("earth does not exist") { 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], @@ -111,7 +109,7 @@ impl Planets { moon_start_point = vector![0., 0.]; } - let (moon_id, moon) = Planets::make_planet( + let (moon_id, moon) = Self::make_planet( "moon", PlanetType::Moon, MOON_MASS, diff --git a/server/src/timer.rs b/server/src/timer.rs index abf3aa1fb16dbfe86e587e91ab4eee3f03e525d8..d84e9ce01f2d67642f8e2b4dcf7cb7d0c18ab0f1 100644 --- a/server/src/timer.rs +++ b/server/src/timer.rs @@ -12,24 +12,24 @@ use crate::{ }; use async_std::sync::RwLock; use async_std::task::sleep; -use log::{warn}; +use log::warn; use nalgebra::{point, vector}; use rand::Rng; -use rapier2d_f64::prelude::{ - ColliderBuilder, MassProperties, PhysicsPipeline, RigidBodyBuilder, -}; +use rapier2d_f64::prelude::{ColliderBuilder, MassProperties, PhysicsPipeline, RigidBodyBuilder}; use starkingdoms_protocol::{module::ModuleType, planet::PlanetType, player::Player}; use std::{f64::consts::PI, sync::Arc, time::Duration}; +use std::error::Error; pub const LATERAL_FORCE: f64 = 0.0002; pub const MODULE_SPAWN: f64 = 3.0; pub const MODULE_MAX: u32 = 10; +//noinspection ALL pub async fn timer_main( mgr: ClientManager, physics_data_orig: Arc>, entities: Arc>, -) { +) -> Result<(), Box> { let mut pipeline = PhysicsPipeline::new(); let mut time = 0.0; @@ -68,11 +68,11 @@ pub async fn timer_main( let planets = entities.write().await; // update earth (nothing changes, yet) - let earth = planets.get_planet(PlanetType::Earth).unwrap(); + let earth = planets.get_planet(PlanetType::Earth).ok_or("earth does not exist")?; let new_earth_position = vector![earth.position.0, earth.position.1]; // update moon - let moon: &mut Planet = &mut planets.get_planet(PlanetType::Moon).unwrap(); + let moon: &mut Planet = &mut planets.get_planet(PlanetType::Moon).ok_or("moon does not exist")?; let new_moon_position = calculate_world_position_of_orbit( calculate_point_on_orbit(MOON_PERIAPSIS, MOON_APOAPSIS, time / MOON_ORBIT_TIME), new_earth_position, @@ -80,7 +80,7 @@ pub async fn timer_main( let moon_body = physics_data .rigid_body_set .get_mut(moon.body_handle) - .unwrap(); + .ok_or("moon does not exist")?; moon_body.set_next_kinematic_position(new_moon_position.into()); moon.position = ( moon_body.translation()[0] / SCALE, @@ -137,9 +137,9 @@ pub async fn timer_main( .insert(get_entity_id(), Entity::Module(module)); } let mut entities = entities.write().await; - for module in entities.get_modules().iter_mut() { + for module in &mut entities.get_modules() { let module_handle = module.handle; - let module_body = physics_data.rigid_body_set.get_mut(module_handle).unwrap(); + let module_body = physics_data.rigid_body_set.get_mut(module_handle).ok_or("module does not exist")?; module_body.reset_forces(true); module_body.reset_torques(true); let grav_force = entities.gravity( @@ -147,8 +147,8 @@ pub async fn timer_main( 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() { + let id = entities.get_from_module(module).ok_or("module entity does not exist")?; + if let Entity::Module(p_module) = entities.entities.get_mut(&id).ok_or("module does not exist")? { p_module.lifetime += 5. / 1000.; } if module.lifetime > 80. { @@ -173,9 +173,9 @@ pub async fn timer_main( entities.entities.remove(&id); } } - for module in entities.get_all_attached().iter_mut() { + for module in &mut entities.get_all_attached() { let module_handle = module.handle; - let module_body = physics_data.rigid_body_set.get_mut(module_handle).unwrap(); + let module_body = physics_data.rigid_body_set.get_mut(module_handle).ok_or("module does not exist")?; module_body.reset_forces(true); module_body.reset_torques(true); let grav_force = entities.gravity( @@ -187,9 +187,9 @@ pub async fn timer_main( } { - for (player_id, player) in entities.read().await.get_players().iter() { + for (player_id, player) in &entities.read().await.get_players() { let player_handle = player.handle; - let player_body = physics_data.rigid_body_set.get_mut(player_handle).unwrap(); + let player_body = physics_data.rigid_body_set.get_mut(player_handle).ok_or("player body does not exist")?; player_body.reset_forces(true); player_body.reset_torques(true); let planets = entities.read().await; @@ -245,20 +245,20 @@ pub async fn timer_main( right_bottom_thruster / scale * rotation.cos() ]; let top_left_point = point![ - -25. / scale * rotation.cos() + 25. / scale * rotation.sin(), - -25. / scale * rotation.sin() - 25. / scale * rotation.cos() + (-25. / scale).mul_add(rotation.cos(), 25. / scale * rotation.sin()), + (-25. / scale).mul_add(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).mul_add(rotation.cos(), 25. / scale * rotation.sin()), + (25. / scale).mul_add(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).mul_add(rotation.cos(), -25. / scale * rotation.sin()), + (-25. / scale).mul_add(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).mul_add(rotation.cos(), -25. / scale * rotation.sin()), + (25. / scale).mul_add(rotation.sin(), 25. / scale * rotation.cos()) ] + player_body.translation(); player_body.add_force_at_point(left_top_thruster, top_left_point, true); @@ -271,10 +271,9 @@ pub async fn timer_main( let username; { let usernames = mgr.usernames.read().await; - username = usernames.get(player_id).unwrap().clone(); + username = usernames.get(player_id).ok_or("username does not exist")?.clone(); } - // TODO: Figure out how to adjust codegen to use f64 protocol_players.push(Player { rotation, x: (translation.x * SCALE), @@ -290,7 +289,7 @@ pub async fn timer_main( let mut mgr_w = mgr.handlers.write().await; let mgr_r = mgr_w.clone(); - for (addr, client_thread) in mgr_r.iter() { + for (addr, client_thread) in &mgr_r { match client_thread.tx.send(ClientHandlerMessage::Tick).await { Ok(_) => { match client_thread @@ -308,44 +307,53 @@ pub async fn timer_main( let modules = entities.read().await.get_modules_id(); let entities = entities.read().await; let this_player = entities.get_player_id(*addr); - let mut attached_modules: Vec = Vec::new(); + let mut attached_modules: Vec = + Vec::new(); let unattached_modules = entities.get_all_attached(); - let mut unattached_modules: Vec = unattached_modules - .iter() - .filter(|m| { - match this_player { + let mut unattached_modules: Vec = + unattached_modules + .iter() + .filter(|m| match this_player { Some(id) => { if m.player_id != id { true } else { - attached_modules.push(m.to_protocol(&entities, &physics_data.rigid_body_set)); + #[allow(clippy::expect_used)] { + attached_modules.push( + m.to_protocol(&entities, &physics_data.rigid_body_set).expect("module does not exist"), + ); + } false } } - None => { - true + None => true, + }) + .map(|m| { + let id; + let module; + #[allow(clippy::expect_used)] { (id, module) = m.to_module_id(&entities).expect("unable to get module id"); } + //info!("{:?}", module); + let body; + #[allow(clippy::expect_used)] { body = physics_data.rigid_body_set.get(module.handle).expect("module body does not exist"); } + + starkingdoms_protocol::module::Module { + module_type: module.module_type.into(), + rotation: body.rotation().angle(), + x: body.translation().x * SCALE, + y: body.translation().y * SCALE, + id, + flags: module.flags, + special_fields: Default::default(), } - } - }) - .map(|m| { - let (id, module) = m.to_module_id(&entities); - //info!("{:?}", module); - let body = physics_data.rigid_body_set.get(module.handle).unwrap(); - starkingdoms_protocol::module::Module { - module_type: module.module_type.into(), - rotation: body.rotation().angle(), - x: body.translation().x * SCALE, - y: body.translation().y * SCALE, - id, - flags: module.flags, - special_fields: Default::default(), - } - }) - .collect(); + }) + .collect(); let mut protocol_modules: Vec = modules .iter() .map(|(id, module)| { - let body = physics_data.rigid_body_set.get(module.handle).unwrap(); + let body; + + #[allow(clippy::expect_used)] { body = physics_data.rigid_body_set.get(module.handle).expect("module body does not exist"); } + starkingdoms_protocol::module::Module { module_type: module.module_type.into(), rotation: body.rotation().angle(), @@ -371,9 +379,13 @@ pub async fn timer_main( } }; - match client_thread.tx.send(ClientHandlerMessage::ModuleTreeUpdate { - modules: attached_modules - }).await { + match client_thread + .tx + .send(ClientHandlerMessage::ModuleTreeUpdate { + modules: attached_modules, + }) + .await + { Ok(_) => (), Err(e) => { warn!("unable to send module tree update packet: {}", e); diff --git a/spacetime_rs/src/commands/docker.rs b/spacetime_rs/src/commands/docker.rs index 7f97499c4f70b9e96897d3a72d3b1141f942d243..d7184bd760ac63defe5483ba6426b958a6cb85e8 100644 --- a/spacetime_rs/src/commands/docker.rs +++ b/spacetime_rs/src/commands/docker.rs @@ -2,11 +2,11 @@ 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 sedregex::find_and_replace; use std::error::Error; use std::fs; use std::path::PathBuf; use std::process::Command; -use sedregex::find_and_replace; fn _build(img: &str, channel: &str, root: &PathBuf) -> Result<(), Box> { let mut patched = "".to_string(); @@ -18,7 +18,7 @@ fn _build(img: &str, channel: &str, root: &PathBuf) -> Result<(), Box } else if img == "web" { let (a, b) = match channel { "stable" => ("starkingdoms", "tk"), - _ => (channel, "starkingdoms.io") + _ => (channel, "starkingdoms.io"), }; let index_html_path = root.clone().join("client/").join("index.html"); @@ -30,7 +30,10 @@ fn _build(img: &str, channel: &str, root: &PathBuf) -> Result<(), Box fs::write(&index_html_path, index_html_patched.as_bytes())?; - fs::write(root.clone().join("client/").join("index.html.orig"), index_html_src.clone())?; + fs::write( + root.clone().join("client/").join("index.html.orig"), + index_html_src.clone(), + )?; patched = index_html_src.clone(); diff --git a/spacetime_rs/src/configure/asset.rs b/spacetime_rs/src/configure/asset.rs index a1b49e0eba87bce48cd2e088f3554023f1a614c0..1070043174d04e7be108b9b48e7a31da6b3c6390 100644 --- a/spacetime_rs/src/configure/asset.rs +++ b/spacetime_rs/src/configure/asset.rs @@ -27,7 +27,11 @@ pub fn configure_assets(writer: &mut NinjaWriter, root: &Path) -> Result<( ); let default_asset_size = 512; - let asset_overrides = HashMap::from([("earth.svg", 2048), ("moon.svg", 2048), ("starfield.svg", 2048)]); + let asset_overrides = HashMap::from([ + ("earth.svg", 2048), + ("moon.svg", 2048), + ("starfield.svg", 2048), + ]); // generate an inkscape rule for all required asset sizes let mut written_rules_for = vec![]; diff --git a/web/play.html b/web/play.html index 8626dc314f4dd236ef8147ff67b169404f35d73d..88745505ab806f5a9021a23ef1f42be4f1de14b4 100644 --- a/web/play.html +++ b/web/play.html @@ -2,7 +2,7 @@ StarKingdoms.TK - + diff --git a/web/static/css/index.css b/web/static/css/index.css index 405b74d0b8c9309dea57b7c398d5ddf443741a74..5631279878ec32ee364a53f7a7349a74507fe38e 100644 --- a/web/static/css/index.css +++ b/web/static/css/index.css @@ -11,6 +11,3 @@ width: 100%; } -.w-90 { - width: 90%; -} \ No newline at end of file diff --git a/web/static/css/play.css b/web/static/css/play.css index baa503ddcd6ff92ad62cb06eec06c61dbfc687f2..aa4ffbfda6e9ff66e09446fd792a7714d6ecaf4f 100644 --- a/web/static/css/play.css +++ b/web/static/css/play.css @@ -1,8 +1,3 @@ -.texturebox { - display: inline; - margin: 5px; -} - #canvas { position: absolute; top: 0;