// 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 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 crafting;
pub mod macros;
pub mod mathutil;
pub mod module;
pub mod planet;
pub mod player;
pub mod ws;
struct StkPluginGroup;
#[derive(Resource, Clone)]
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,
)
.in_fixed_schedule(),
)
.add_plugins(StkTungsteniteServerPlugin)
.add_systems(
Startup,
(setup_integration_parameters, planet::spawn_planets),
)
.add_systems(Update, (player::on_message, player::packet::on_close))
.add_systems(
FixedUpdate,
(
module::module_spawn,
player::packet::send_player_energy,
player::packet::on_position_change,
module::save::save_eligibility,
module::convert_modules,
),
)
.add_systems(
FixedUpdate,
(
module::break_modules,
planet::gravity_update,
player::player_input_update,
)
.chain(),
)
.insert_resource(Time::<Fixed>::from_seconds(
server_config.server.world_fixed_timestep,
))
.run();
// game is done running
info!("Goodbye!");
}
// set settings in physics engine
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();
}
}
}