use std::{time::Duration, sync::Arc};
use log::{debug, warn};
use nalgebra::{vector, point};
use rapier2d_f64::prelude::{PhysicsPipeline};
use async_std::sync::RwLock;
use async_std::task::sleep;
use starkingdoms_protocol::{player::Player, planet::PlanetType};
use crate::{manager::{ClientHandlerMessage, ClientManager, PhysicsData}, SCALE, planet::{Planets, Planet}, entity::{Entities, Entity, EntityHandler}};
use crate::orbit::constants::{EARTH_MASS, GAME_ORBITS_ENABLED, MOON_APOAPSIS, MOON_MASS, MOON_ORBIT_TIME, MOON_PERIAPSIS};
use crate::orbit::orbit::{calculate_point_on_orbit, calculate_vector_of_orbit, calculate_world_position_of_orbit};
pub const ROTATIONAL_FORCE: f64 = 100.0;
pub const LATERAL_FORCE: f64 = 100.0;
pub async fn timer_main(mgr: ClientManager, physics_data: Arc<RwLock<PhysicsData>>, entities: Arc<RwLock<EntityHandler>>) {
let mut pipeline = PhysicsPipeline::new();
let mut time = 0.0;
let planet_ids;
{
let mut data_handle = physics_data.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::new(&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
let mut physics_data = physics_data.write().await;
// update orbits (but dont, actually, yet)
// DO NOT SIMPLIFY EXPRESSION
// IT MAY ALWAYS BE TRUE
// THATS FINE
if GAME_ORBITS_ENABLED {
let mut planets = entities.write().await;
// update earth (nothing changes, yet)
let new_earth_position;
let earth = planets.get_planet(PlanetType::Earth).unwrap();
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![];
{
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 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);
}
}
}