use derive_builder::Builder;
#[cfg(feature = "audio")]
#[cfg(feature = "client")]
use crate::resources::{
sounds::{AudioSettings, NoAudioServerError},
RESOURCES,
};
#[cfg(feature = "client")]
use super::{Window, WindowBuilder};
use super::TickSettings;
use parking_lot::{Condvar, Mutex};
#[cfg(feature = "client")]
use std::{
sync::{atomic::AtomicBool, Arc, OnceLock},
time::Duration,
};
#[derive(Clone, Builder, Default)]
pub struct EngineSettings {
#[builder(setter(into, strip_option), default)]
#[cfg(feature = "client")]
pub window_settings: WindowBuilder,
#[builder(setter(into), default)]
pub tick_settings: TickSettings,
}
pub struct Settings {
#[cfg(feature = "client")]
window: Mutex<std::sync::OnceLock<Arc<Window>>>,
pub tick_system: TickSystem,
#[cfg(feature = "client")]
pub graphics: Graphics,
#[cfg(feature = "audio")]
pub audio: Audio,
}
impl Settings {
pub(crate) fn new() -> Self {
Self {
#[cfg(feature = "client")]
window: Mutex::new(std::sync::OnceLock::new()),
tick_system: TickSystem::new(),
#[cfg(feature = "client")]
graphics: Graphics::new(PresentMode::Fifo),
#[cfg(feature = "audio")]
audio: Audio::new(),
}
}
#[cfg(feature = "client")]
pub(crate) fn set_window(&self, window: Arc<Window>) -> Result<(), Arc<Window>> {
self.window.lock().set(window)?;
Ok(())
}
#[cfg(feature = "client")]
pub fn window(&self) -> Option<Arc<Window>> {
self.window.lock().get().cloned()
}
}
pub struct TickSystem {
pub(super) tick_settings: Mutex<TickSettings>,
pub(super) tick_pause_lock: (Mutex<bool>, Condvar),
}
impl TickSystem {
pub(crate) fn new() -> Self {
Self {
tick_settings: Mutex::new(TickSettings::default()),
tick_pause_lock: (Mutex::new(false), Condvar::new()),
}
}
pub fn get(&self) -> TickSettings {
self.tick_settings.lock().clone()
}
pub fn set(&self, settings: TickSettings) {
*self.tick_pause_lock.0.lock() = settings.paused;
*self.tick_settings.lock() = settings;
self.tick_pause_lock.1.notify_all();
}
}
#[cfg(feature = "audio")]
pub struct Audio {
audio_settings: Mutex<AudioSettings>,
}
#[cfg(feature = "audio")]
impl Audio {
pub(crate) fn new() -> Self {
Self {
audio_settings: Mutex::new(AudioSettings::new()),
}
}
#[cfg(feature = "audio")]
pub fn get(&self) -> AudioSettings {
*self.audio_settings.lock()
}
#[cfg(feature = "audio")]
pub fn set(&self, settings: AudioSettings) -> Result<(), NoAudioServerError> {
*self.audio_settings.lock() = settings;
RESOURCES
.audio_server
.send(crate::resources::sounds::AudioUpdate::SettingsChange(
settings,
))
.ok()
.ok_or(NoAudioServerError)
}
}
#[cfg(feature = "client")]
pub struct Graphics {
pub(crate) present_mode: Mutex<PresentMode>,
framerate_limit: Mutex<Duration>,
pub(crate) available_present_modes: OnceLock<Vec<PresentMode>>,
pub(crate) recreate_swapchain: AtomicBool,
}
#[cfg(feature = "client")]
impl Graphics {
pub(crate) fn new(present_mode: PresentMode) -> Self {
Self {
present_mode: Mutex::new(present_mode),
framerate_limit: Mutex::new(Duration::from_secs(0)),
available_present_modes: OnceLock::new(),
recreate_swapchain: false.into(),
}
}
pub fn present_mode(&self) -> PresentMode {
*self.present_mode.lock()
}
pub fn set_present_mode(&self, mode: PresentMode) -> anyhow::Result<()> {
if self.get_supported_present_modes().contains(&mode) {
*self.present_mode.lock() = mode;
self.recreate_swapchain
.store(true, std::sync::atomic::Ordering::Release);
Ok(())
} else {
Err(anyhow::Error::msg(format!(
"This present mode \"{:?}\" is not available on this device.\nAvailable modes on this device are {:?}",
mode, self.get_supported_present_modes()
)))
}
}
pub fn framerate_limit(&self) -> Duration {
*self.framerate_limit.lock()
}
pub fn set_framerate_limit(&self, limit: Duration) {
*self.framerate_limit.lock() = limit;
}
pub fn set_fps_cap(&self, cap: u64) {
if cap == 0 {
self.set_framerate_limit(Duration::from_secs(cap));
return;
}
self.set_framerate_limit(Duration::from_secs_f64(1.0 / cap as f64));
}
pub fn get_supported_present_modes(&self) -> Vec<PresentMode> {
self.available_present_modes
.get()
.cloned()
.unwrap_or(vec![])
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
#[cfg(feature = "client")]
pub enum PresentMode {
Immediate,
Mailbox,
Fifo,
}
#[cfg(feature = "client")]
impl From<PresentMode> for vulkano::swapchain::PresentMode {
fn from(value: PresentMode) -> vulkano::swapchain::PresentMode {
use vulkano::swapchain::PresentMode as Pm;
match value {
PresentMode::Immediate => Pm::Immediate,
PresentMode::Mailbox => Pm::Mailbox,
PresentMode::Fifo => Pm::Fifo,
}
}
}
#[cfg(feature = "client")]
impl From<vulkano::swapchain::PresentMode> for PresentMode {
fn from(value: vulkano::swapchain::PresentMode) -> PresentMode {
use vulkano::swapchain::PresentMode as Pm;
match value {
Pm::Immediate => PresentMode::Immediate,
Pm::Mailbox => PresentMode::Mailbox,
_ => PresentMode::Fifo,
}
}
}