use crate::entity::EntityHandler;
use crate::module::{Module, AttachedModule};
use crate::orbit::constants::{GAME_ORBITS_ENABLED, MARS_APHELION, MARS_ORBIT_TIME, MARS_PERIHELION, 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, PhysicsData},
planet::Planets,
SCALE,
};
use async_std::sync::RwLock;
use async_std::task::sleep;
use log::warn;
use nalgebra::{point, vector};
use protobuf::SpecialFields;
use rand::Rng;
use rapier2d_f64::prelude::{ColliderBuilder, MassProperties, PhysicsPipeline, RigidBodyBuilder};
use starkingdoms_protocol::{module::ModuleType, planet::PlanetType, player::Player};
use std::error::Error;
use std::{f64::consts::PI, sync::Arc, time::Duration};
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<RwLock<PhysicsData>>,
entities: Arc<RwLock<EntityHandler>>,
) -> Result<(), Box<dyn Error>> {
let mut pipeline = PhysicsPipeline::new();
let mut time = 0.0;
let mut module_timer = 0.0;
{
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();
let _ = Planets::create_planets(
&mut rigid_body_set,
&mut collider_set,
&mut entities.write().await.entities,
);
data_handle.rigid_body_set = rigid_body_set;
data_handle.collider_set = collider_set;
}
#[allow(clippy::significant_drop_tightening)]
// underdeveloped lint, TODO remove when its better
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 mut planets = entities.write().await;
let mut map = planets.entities.clone();
// update earth (nothing changes, yet)
let earth_id = planets.get_planet_id(PlanetType::Earth).ok_or("earth does not exist")?;
if let Entity::Planet(earth) = map
.get(&earth_id)
.ok_or("earth does not exist")? {
let new_earth_position = vector![earth.position.0, earth.position.1];
// update moon
let moon_id = planets.get_planet_id(PlanetType::Moon).ok_or("moon does not exist")?;
if let Entity::Planet(moon) = map
.get_mut(&moon_id)
.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,
);
let new_moon_position = new_moon_position / SCALE;
let moon_body = physics_data
.rigid_body_set
.get_mut(moon.body_handle)
.ok_or("moon does not exist")?;
moon_body.set_next_kinematic_translation(new_moon_position);
moon.position = (
moon_body.translation()[0],
moon_body.translation()[1],
);
// update mars
let mars_id = planets.get_planet_id(PlanetType::Mars).ok_or("mars does not exist")?;
if let Entity::Planet(mars) = map
.get_mut(&mars_id)
.ok_or("mars does not exist")? {
let new_mars_position = calculate_world_position_of_orbit(
calculate_point_on_orbit(MARS_PERIHELION, MARS_APHELION, time / MARS_ORBIT_TIME),
new_moon_position,
);
let new_mars_position = new_mars_position/SCALE;
let mars_body = physics_data
.rigid_body_set
.get_mut(mars.body_handle)
.ok_or("mars does not exist")?;
mars_body.set_next_kinematic_translation(new_mars_position);
mars.position = (
mars_body.translation()[0],
mars_body.translation()[1],
);
}
}
}
planets.entities = map;
}
physics_data.tick(&mut pipeline);
let mut protocol_players = vec![];
{
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();
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], 0.0001, 0.005))
.build();
let angle: f64 = {
let mut rng = rand::thread_rng();
rng.gen::<f64>() * 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,
lifetime: 0.0,
flags: 0,
};
entities
.write()
.await
.entities
.insert(get_entity_id(), Entity::Module(module));
}
let mut entities = entities.write().await;
for module in &mut entities.get_modules() {
let module_handle = module.handle;
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(
(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)
.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. {
let mut rigid_body_set = physics_data.rigid_body_set.clone();
let mut island_manager = physics_data.island_manager.clone();
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,
);
physics_data.rigid_body_set = rigid_body_set;
physics_data.collider_set = collider_set;
physics_data.island_manager = island_manager;
physics_data.impulse_joint_set = impulse_joint_set;
physics_data.multibody_joint_set = multibody_joint_set;
entities.entities.remove(&id);
}
}
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)
.ok_or("module does not exist")?;
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);
for child in module.children.clone() {
if let Some(child) = child {
let joint = physics_data.impulse_joint_set
.get(child.connection).ok_or("module joint does not exist")?;
if joint.impulses.magnitude() > 0.00006 {
let module: Option<AttachedModule>;
if let Some(Entity::AttachedModule(p_module)) =
entities.entities.get_mut(&child.child)
{
module = Some(p_module.clone());
} else {
warn!("attempted to detach nonexistent module");
continue;
}
let player_id = module.as_ref().ok_or("cannot detach module that doesn't exist")?.player_id;
AttachedModule::detach(
&mut physics_data,
&mut entities,
player_id,
&module.ok_or("cannot detach module that doesn't exist")?,
);
}
}
}
}
}
{
let mut entities = entities.write().await;
for (player_id, player) in &entities.get_players() {
let player_handle = player.handle;
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 grav_force = entities.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 scale = SCALE;
let left_top_thruster = vector![
-left_top_thruster / scale * rotation.sin(),
left_top_thruster / scale * rotation.cos()
];
let right_top_thruster = vector![
-right_top_thruster / scale * rotation.sin(),
right_top_thruster / scale * rotation.cos()
];
let left_bottom_thruster = vector![
-left_bottom_thruster / scale * rotation.sin(),
left_bottom_thruster / scale * rotation.cos()
];
let right_bottom_thruster = vector![
-right_bottom_thruster / scale * rotation.sin(),
right_bottom_thruster / scale * rotation.cos()
];
let top_left_point = point![
(-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).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).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).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);
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)
.ok_or("username does not exist")?
.clone();
}
protocol_players.push(Player {
rotation,
x: (translation.x * SCALE),
y: (translation.y * SCALE),
username,
special_fields: SpecialFields::default(),
});
for child in player.children.clone() {
if let Some(child) = child {
let joint = physics_data.impulse_joint_set
.get(child.connection).ok_or("module joint does not exist")?;
// displays impulse on joint * 10000 so its visible, use to tune breaking
// force
//debug!("impulse: {}", joint.impulses.magnitude() * 10000.);
if joint.impulses.magnitude() > 0.00006 {
let module: Option<AttachedModule>;
if let Some(Entity::AttachedModule(p_module)) =
entities.entities.get_mut(&child.child)
{
module = Some(p_module.clone());
} else {
warn!("attempted to detach nonexistent module");
continue;
}
let player_id = module.as_ref().ok_or("cannot detach module that doesn't exist")?.player_id;
AttachedModule::detach(
&mut physics_data,
&mut entities,
player_id,
&module.ok_or("cannot detach module that doesn't exist")?,
);
}
}
}
}
}
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 {
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_id();
let entities = entities.read().await;
let this_player = entities.get_player_id(*addr);
let mut attached_modules: Vec<starkingdoms_protocol::module::AttachedModule> =
Vec::new();
let unattached_modules = entities.get_all_attached();
let mut unattached_modules: Vec<starkingdoms_protocol::module::Module> =
unattached_modules
.iter()
.filter(|m| match this_player {
Some(id) => {
if m.player_id == id {
#[allow(clippy::expect_used)]
{
attached_modules.push(
m.to_protocol(
&entities,
&physics_data.rigid_body_set,
)
.expect("module does not exist"),
);
}
false
} else {
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: SpecialFields::default(),
}
})
.collect();
let mut protocol_modules: Vec<starkingdoms_protocol::module::Module> = modules
.iter()
.map(|(id, 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: *id,
flags: module.flags,
special_fields: SpecialFields::default(),
}
})
.collect();
protocol_modules.append(&mut unattached_modules);
match client_thread
.tx
.send(ClientHandlerMessage::ModulesUpdate {
modules: protocol_modules.clone(),
})
.await
{
Ok(_) => (),
Err(e) => {
warn!("unable to send module position packet: {}", e);
}
};
match client_thread
.tx
.send(ClientHandlerMessage::ModuleTreeUpdate {
modules: attached_modules,
})
.await
{
Ok(_) => (),
Err(e) => {
warn!("unable to send module tree update packet: {}", e);
}
}
let planet_data = entities.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);
}
}
}