You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

150 lines
4.9 KiB

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::io::Write;
use std::path::PathBuf;
use std::{collections::HashMap, fs::File, io::ErrorKind, path::Path};
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
pub struct Config {
pub name: String,
#[serde(default = "default_version")]
pub version: String,
#[serde(default)]
pub authors: Vec<String>,
pub script: String,
pub available_cards: HashMap<String, Card>,
pub default_back: Option<PathBuf>,
pub piles: HashMap<String, Pile>,
pub player_piles: HashMap<String, Pile>,
}
fn default_version() -> String {
"0.0.0".into()
}
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
pub struct Card {
pub image: PathBuf,
pub back_image: Option<PathBuf>,
#[serde(flatten)]
pub other: HashMap<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
pub struct Pile {
#[serde(default)]
pub cards: Vec<String>,
#[serde(default)]
pub face_down: bool,
#[serde(flatten)]
pub other: HashMap<String, serde_json::Value>,
}
impl Config {
pub fn load<P: AsRef<std::path::Path> + std::fmt::Debug>(file: P) -> Self {
let s: Config = serde_json::from_reader(std::fs::File::open(&file).unwrap())
.map_err(|e| {
log::error!(
"Malformed game defintion file @ {}",
file.as_ref().display()
);
log::error!("JSON Error: {}", e);
panic!()
})
.unwrap();
if s.default_back.is_none() {
for (name, card) in &s.available_cards {
if card.back_image.is_none() {
panic!("Card {} from game {} can not have a default back if there's not default back", name, s.name)
}
}
}
s.start_image_caching(file.as_ref().parent().unwrap().to_path_buf());
s
}
fn start_image_caching(&self, folder: PathBuf) {
// TODO Add checks to say if it has finished
if let Some(p) = &self.default_back {
let p = p.clone();
let folder = folder.clone();
tokio::task::spawn_blocking(|| cache_image(p, folder));
}
for (
_,
Card {
image,
back_image,
other: _,
},
) in &self.available_cards
{
{
let folder = folder.clone();
let p = image.clone();
tokio::task::spawn_blocking(|| cache_image(p, folder));
};
if let Some(back_image) = back_image {
let p = back_image.clone();
let folder = folder.clone();
tokio::task::spawn_blocking(|| cache_image(p, folder));
}
}
}
pub fn reload_cache(&self, folder: PathBuf) {
std::fs::remove_dir_all(folder.join(".cache")).unwrap();
self.start_image_caching(folder);
}
}
fn cache_image<P1: AsRef<Path>, P2: AsRef<Path>>(p: P1, folder: P2) {
let original = folder.as_ref().join(p.as_ref());
let cache_folder = folder.as_ref().join(".cache");
let mut cached = cache_folder.join(p);
// log::info!("Caching {} on {}", original.display(), cached.display());
// log::info!("Creating {}", cache_folder.display());
match std::fs::create_dir(cache_folder) {
Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(()), // Ignore if folder already exists
x => x,
}
.unwrap();
cached.set_extension("png");
if cached.exists()
&& cached.metadata().unwrap().modified().unwrap()
> original.metadata().unwrap().modified().unwrap()
{
// Cache is updated, do nothing
log::info!("cache for {} is up to date", original.display());
} else {
// Update cache
// log::info!("Updating cache for: {}", original.display());
let mut face_buf = Vec::new();
image::open(&original)
.expect(&format!("Error loading the image in {:?}", original))
.write_to(&mut face_buf, image::ImageOutputFormat::Png)
.unwrap();
match std::fs::create_dir(cached.parent().unwrap()) {
Err(e) if e.kind() == ErrorKind::AlreadyExists => Ok(()), // Ignore if folder already exists
x => x,
}
.unwrap();
File::create(cached).unwrap().write_all(&face_buf).unwrap();
log::info!("Updated cache for: {}", original.display());
}
}
pub fn setup() {
#[cfg(debug_assertions)]
{
if let Ok(e) = std::env::var("CARGO_MANIFEST_DIR") {
std::fs::write(
AsRef::<std::path::Path>::as_ref(&e)
.join("schema")
.join("game-config.json"),
serde_json::to_string_pretty(&schemars::schema_for!(Config)).unwrap(),
)
.unwrap()
}
}
}