use std::{time::Duration, sync::Arc, f64::consts::PI}; use log::{debug, warn}; use nalgebra::{vector, point}; use rand::Rng; use rapier2d_f64::prelude::{PhysicsPipeline, ColliderBuilder, RigidBodyBuilder, MassProperties}; 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::orbit::{calculate_point_on_orbit, calculate_world_position_of_orbit}; pub const ROTATIONAL_FORCE: f64 = 100.0; pub const LATERAL_FORCE: f64 = 100.0; pub const MODULE_SPAWN: f64 = 3000.0; pub const MODULE_MAX: u32 = 10; pub async fn timer_main(mgr: ClientManager, physics_data_orig: Arc>, entities: Arc>) { let mut pipeline = PhysicsPipeline::new(); let mut time = 0.0; let mut module_timer = 0.0; let _planet_ids; { let mut data_handle = physics_data_orig.write().await; 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; data_handle.rigid_body_set = rigid_body_set; data_handle.collider_set = collider_set; } loop { sleep(Duration::from_millis(5)).await; time += 5.0 / 1000.0; // 5ms, time is in seconds module_timer += 5.0 / 1000.0; let mut physics_data = physics_data_orig.write().await; // update orbits (but dont, actually, yet) // DO NOT SIMPLIFY EXPRESSION // IT MAY ALWAYS BE TRUE // THATS FINE if GAME_ORBITS_ENABLED { let planets = entities.write().await; // update earth (nothing changes, yet) let earth = planets.get_planet(PlanetType::Earth).unwrap(); 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 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); } physics_data.tick(&mut pipeline); let mut protocol_players = vec![]; { if module_timer > 1.0 && entities.read().await.get_module_count() < MODULE_MAX { debug!("module spawn"); module_timer = 0.; let mut rigid_body_set = physics_data.rigid_body_set.clone(); let mut collider_set = physics_data.collider_set.clone(); let module_collider = ColliderBuilder::cuboid(18.75 / SCALE, 23.4375 / SCALE) .translation(vector![0.0, 1.5625 / SCALE]) .mass_properties(MassProperties::new(point![0.0, 0.0], 120.0, 122500.0)) .build(); let angle: f64 = { let mut rng = rand::thread_rng(); rng.gen::() * PI * 2. }; let module_body = RigidBodyBuilder::dynamic() .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); physics_data.rigid_body_set = rigid_body_set; physics_data.collider_set = collider_set; let module = Module { handle: module_handler, module_type: ModuleType::Cargo, }; entities.write().await.entities.insert(get_entity_id(), Entity::Module(module)); } for module in entities.read().await.get_modules().iter() { 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 planets = entities.read().await; let grav_force = planets.gravity((module_body.translation().x, module_body.translation().y), module_body.mass()); module_body.apply_impulse(vector![grav_force.0, grav_force.1], true); } } { for (player_id, player) in entities.read().await.get_players().iter() { let player_handle = player.handle; let player_body = physics_data.rigid_body_set.get_mut(player_handle).unwrap(); 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 mut left_top_thruster: f64 = 0.0; let mut right_top_thruster: f64 = 0.0; let mut left_bottom_thruster: f64 = 0.0; let mut right_bottom_thruster: f64 = 0.0; if player.input.right { left_top_thruster -= 1.0; right_bottom_thruster += 1.0; } if player.input.left { right_top_thruster -= 1.0; left_bottom_thruster += 1.0; } if player.input.up { left_bottom_thruster -= 1.0; right_bottom_thruster -= 1.0; } if player.input.down { left_top_thruster += 1.0; right_top_thruster += 1.0; } left_top_thruster = LATERAL_FORCE * left_top_thruster.clamp(-1.0, 1.0); right_top_thruster = LATERAL_FORCE * right_top_thruster.clamp(-1.0, 1.0); left_bottom_thruster = LATERAL_FORCE * left_bottom_thruster.clamp(-1.0, 1.0); right_bottom_thruster = LATERAL_FORCE * right_bottom_thruster.clamp(-1.0, 1.0); let rotation = player_body.rotation().clone().angle(); let left_top_thruster = vector![ -left_top_thruster * rotation.sin(), left_top_thruster * rotation.cos() ]; let right_top_thruster = vector![ -right_top_thruster * rotation.sin(), right_top_thruster * rotation.cos() ]; let left_bottom_thruster = vector![ -left_bottom_thruster * rotation.sin(), left_bottom_thruster * rotation.cos() ]; let right_bottom_thruster = vector![ -right_bottom_thruster * rotation.sin(), right_bottom_thruster * rotation.cos() ]; let scale = SCALE; let top_left_point = point![ -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() ] + player_body.translation(); let bottom_left_point = point![ -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() ] + 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); let translation = player_body.translation(); let username; { let usernames = mgr.usernames.read().await; username = usernames.get(player_id).unwrap().clone(); } // 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, username, special_fields: Default::default(), }); } } let mut to_remove = vec![]; let mut mgr_w = mgr.handlers.write().await; let mgr_r = mgr_w.clone(); 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 { Ok(_) => (), Err(e) => { warn!("unable to send position packet: {}", e); } }; let modules = entities.read().await.get_modules(); let protocol_modules: Vec = 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, special_fields: Default::default(), }; }).collect(); match client_thread.tx.send(ClientHandlerMessage::ModulesUpdate { modules: protocol_modules.clone() }).await { Ok(_) => (), Err(e) => { warn!("unable to send module position packet: {}", e); } }; let world = entities.read().await; let planet_data = world.to_protocol(); match client_thread.tx.send(planet_data).await { Ok(_) => (), Err(e) => { warn!("unable to send earth packet: {}", e); } }; } Err(e) => { warn!("unable to update a client thread: {}", e); to_remove.push(addr); } } } for pending_removal in to_remove { mgr_w.remove(pending_removal); } } }