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
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()
|
|
}
|
|
}
|
|
}
|
|
|