// StarKingdoms.IO, a browser game about drifting through space
// Copyright (C) 2023 ghostly_zsh, TerraMaster85, core
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#![allow(clippy::type_complexity)] // bevy :(
#![allow(clippy::too_many_arguments)] // bevy :(
#![allow(clippy::only_used_in_recursion)] // todo: remove this
use crate::ws::{StkTungsteniteServerConfig, StkTungsteniteServerPlugin};
use bevy::{
app::{PluginGroupBuilder, ScheduleRunnerPlugin},
prelude::*,
time::TimePlugin,
};
use bevy_rapier2d::prelude::*;
use module::component::{
Attach, CanAttach, LooseAttach, ModuleTimer, PartBundle, PartFlags, PartType,
};
use packet::*;
use planet::PlanetType;
use serde::{Deserialize, Serialize};
use std::fs;
use crate::config::{PartsConfig, PhysicsSolver, PlanetsConfig, StkConfig};
use std::sync::OnceLock;
use std::time::Duration;
#[macro_use]
pub mod config;
pub mod macros;
pub mod mathutil;
pub mod module;
pub mod packet;
pub mod planet;
pub mod player;
pub mod ws;
struct StkPluginGroup;
#[derive(Resource)]
pub struct AppKeys {
pub app_key: Vec<u8>,
}
// factor to multiply positions by to send to the client
pub static CLIENT_SCALE: f32 = 50.0;
// good ol' classic almost useless but still necessary code
static _SERVER_CONFIG: OnceLock<StkConfig> = OnceLock::new();
#[inline]
pub fn server_config() -> StkConfig {
_SERVER_CONFIG.get().unwrap().clone()
}
static _PARTS_CONFIG: OnceLock<PartsConfig> = OnceLock::new();
#[inline]
pub fn parts_config() -> PartsConfig {
_PARTS_CONFIG.get().unwrap().clone()
}
static _PLANETS_CONFIG: OnceLock<PlanetsConfig> = OnceLock::new();
#[inline]
pub fn planets_config() -> PlanetsConfig {
_PLANETS_CONFIG.get().unwrap().clone()
}
// group main stk plugins together
#[cfg(debug_assertions)]
impl PluginGroup for StkPluginGroup {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>()
.add(TaskPoolPlugin::default())
.add(TypeRegistrationPlugin)
.add(FrameCountPlugin)
.add(TimePlugin)
.add(ScheduleRunnerPlugin::run_loop(Duration::from_millis(
server_config().server.tick_time_ms,
)))
.add(bevy::log::LogPlugin {
level: bevy::log::Level::DEBUG,
filter: "wgpu=error,bevy_render=info,bevy_ecs=trace".to_string(),
update_subscriber: None,
})
}
}
#[cfg(not(debug_assertions))]
impl PluginGroup for StkPluginGroup {
fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>()
.add(TaskPoolPlugin::default())
.add(TypeRegistrationPlugin)
.add(FrameCountPlugin)
.add(TimePlugin)
.add(ScheduleRunnerPlugin::run_loop(Duration::from_millis(1)))
}
}
// auth token
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserToken {
pub id: i64,
pub username: String,
pub permission_level: i32,
pub expires: std::time::SystemTime,
}
fn main() {
// read the server main config
let server_config = fs::read_to_string("/etc/starkingdoms/config.toml").unwrap();
let parts_config = fs::read_to_string("/etc/starkingdoms/parts.toml").unwrap();
let planets_config = fs::read_to_string("/etc/starkingdoms/planets.toml").unwrap();
// put config in variables
let server_config: StkConfig = toml::from_str(&server_config).unwrap();
_SERVER_CONFIG.set(server_config.clone()).unwrap();
let parts_config: PartsConfig = toml::from_str(&parts_config).unwrap();
_PARTS_CONFIG.set(parts_config.clone()).unwrap();
let planets_config: PlanetsConfig = toml::from_str(&planets_config).unwrap();
_PLANETS_CONFIG.set(planets_config.clone()).unwrap();
// make the game, start it in .run()
App::new()
.insert_resource(AppKeys {
app_key: server_config.security.app_key.as_bytes().to_vec(),
})
.insert_resource(StkTungsteniteServerConfig {
addr: server_config.server.bind.ip,
port: server_config.server.bind.port,
})
.insert_resource(server_config.clone())
.insert_resource(parts_config)
.insert_resource(planets_config)
.add_plugins(StkPluginGroup)
.insert_resource(RapierConfiguration {
gravity: Vect { x: 0.0, y: 0.0 },
..Default::default()
})
.init_resource::<ModuleTimer>()
.add_plugins(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(
server_config.world.pixels_per_meter,
))
.add_plugins(StkTungsteniteServerPlugin)
.add_systems(Startup, setup_integration_parameters)
.add_systems(Startup, planet::spawn_planets)
.add_systems(FixedUpdate, module::module_spawn)
.add_systems(Update, player::on_message)
.add_systems(Update, player::packet::on_close)
.add_systems(FixedUpdate, player::packet::send_player_energy)
.add_systems(FixedUpdate, player::packet::on_position_change)
.add_systems(
FixedUpdate,
(module::break_modules, gravity_update, player::player_input_update).chain(),
)
.add_systems(FixedUpdate, module::save::save_eligibility)
.add_systems(FixedUpdate, module::convert_modules)
.insert_resource(Time::<Fixed>::from_seconds(
server_config.server.world_fixed_timestep,
))
.run();
// game is done running
info!("Goodbye!");
}
fn setup_integration_parameters(mut context: ResMut<RapierContext>, server_config: Res<StkConfig>) {
context.integration_parameters = server_config.physics.parameters;
match server_config.physics.solver {
PhysicsSolver::SmallstepPGS => {
context
.integration_parameters
.switch_to_small_steps_pgs_solver();
}
PhysicsSolver::OldPGS => {
context
.integration_parameters
.switch_to_standard_pgs_solver();
}
}
}
fn gravity_update(
mut part_query: Query<
(
&Transform,
&ReadMassProperties,
&mut ExternalForce,
&mut ExternalImpulse,
),
With<PartType>,
>,
planet_query: Query<(&Transform, &ReadMassProperties), With<PlanetType>>,
server_config: Res<StkConfig>,
) {
for (part_transform, part_mp, mut forces, mut impulses) in &mut part_query {
impulses.impulse = Vec2::ZERO;
forces.force = Vec2::ZERO;
forces.torque = 0.;
let part_mp = part_mp.get();
let part_mass = part_mp.mass;
let part_translate = part_transform.translation;
for (planet_transform, planet_mp) in &planet_query {
let planet_mp = planet_mp.get();
let planet_mass = planet_mp.mass;
let planet_translate = planet_transform.translation;
let distance = planet_translate.distance(part_translate);
let force =
server_config.world.gravity * ((part_mass * planet_mass) / (distance * distance));
let direction = (planet_translate - part_translate).normalize() * force;
/*let gravity_force = ExternalForce::at_point(
direction.xy(),
part_transform.translation.xy(),
part_transform.translation.xy(),
);
forces.force += gravity_force.force;
forces.torque += gravity_force.torque;*/
impulses.impulse += direction.xy();
}
}
}