|
|
|
@ -6,149 +6,152 @@ 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>, |
|
|
|
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() |
|
|
|
"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>, |
|
|
|
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(default = "default_visible")] |
|
|
|
pub visible: bool, |
|
|
|
#[serde(flatten)] |
|
|
|
pub other: HashMap<String, serde_json::Value>, |
|
|
|
pub name: String, |
|
|
|
#[serde(default)] |
|
|
|
pub cards: Vec<String>, |
|
|
|
#[serde(default)] |
|
|
|
pub face_down: bool, |
|
|
|
#[serde(default = "default_visible")] |
|
|
|
pub visible: bool, |
|
|
|
#[serde(flatten)] |
|
|
|
pub other: HashMap<String, serde_json::Value>, |
|
|
|
} |
|
|
|
|
|
|
|
fn default_visible() -> bool {true} |
|
|
|
fn default_visible() -> bool { |
|
|
|
true |
|
|
|
} |
|
|
|
|
|
|
|
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 |
|
|
|
} |
|
|
|
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) { |
|
|
|
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)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
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) { |
|
|
|
pub fn reload_cache(&self, folder: PathBuf) { |
|
|
|
std::fs::remove_dir_all(folder.join(".cache")).unwrap(); |
|
|
|
self.start_image_caching(folder); |
|
|
|
} |
|
|
|
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_all(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()); |
|
|
|
} |
|
|
|
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_all(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() |
|
|
|
} |
|
|
|
} |
|
|
|
#[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() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|