Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add provinces, dynasties, religions, holdings and expand alive data #19

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ libdeflate = ["libdeflater/freestanding"]
serialize = []

[dependencies]
jomini = { version = "0.21", features = ["json"] }
jomini = { git = "https://github.com/rakaly/jomini.git", branch = "deser", features = ["json"] }
zip = { version = "0.6", default-features = false }
serde = { version = "1", features = ["derive"] }
thiserror = "1"
libdeflater = { version = "0.11", optional = true }
miniz_oxide = { version = "0.6", optional = true }
serde_path_to_error = "0.1.11"

[dev-dependencies]
attohttpc = "0.23"
Expand Down
34 changes: 32 additions & 2 deletions src/bin/debug_save.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
use std::env;

use ck3save::{models::Gamestate, Ck3File, EnvTokens};
use serde::Deserialize;

fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = env::args().collect();
let data = std::fs::read(&args[1])?;
let file = Ck3File::from_slice(&data)?;
let mut zip_sink = Vec::new();
let file = file.parse(&mut zip_sink)?;
let save: Gamestate = file.deserializer(&EnvTokens).deserialize()?;
print!("{:#?}", save.meta_data.version);
let deserializer = file.deserializer(&EnvTokens);
let result: Result<Gamestate, _> = serde_path_to_error::deserialize(deserializer);
match result {
Ok(_) => panic!("expected a type error"),
Err(err) => {
let path = err.path().to_string();
assert_eq!(path, "dependencies.serde.version");
}
}
// let save: Gamestate = deserializer.deserialize()?;
// println!("{:#?}", save.meta_data.version);
// println!("{:#?}", save.played_character.character);
// println!("{:#?}", save.living.get(&save.played_character.character));
// let traits = save
// .living
// .get(&save.played_character.character)
// .unwrap()
// .traits
// .clone()
// .unwrap();
// let trait_strings = traits
// .iter()
// .map(|t| save.traits_lookup[*t].clone())
// .collect::<Vec<String>>();
// println!(
// "{:#?}",
// traits
// .iter()
// .map(|t| save.traits_lookup[*t].clone())
// .collect::<Vec<String>>()
// );
Ok(())
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ mod header;
mod melt;
pub mod models;
mod tokens;
mod maybe_object;

pub use ck3date::*;
pub use errors::*;
Expand Down
54 changes: 54 additions & 0 deletions src/maybe_object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::fmt;
use std::marker::PhantomData;
use serde::{Deserialize, Deserializer, de, Serialize};

#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum MaybeObject<T> {
Text(String),
Object(T),
}

impl<'de, T> Deserialize<'de> for MaybeObject<T>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct MaybeObjectVisitor<T1> {
marker: PhantomData<T1>,
}

impl<'de, T1> de::Visitor<'de> for MaybeObjectVisitor<T1>
where
T1: Deserialize<'de>,
{
type Value = MaybeObject<T1>;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an object or string")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(MaybeObject::Text(String::from(v)))
}

fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mvd = de::value::MapAccessDeserializer::new(map);
let result = T1::deserialize(mvd)?;
Ok(MaybeObject::Object(result))
}
}

deserializer.deserialize_map(MaybeObjectVisitor {
marker: PhantomData,
})
}
}
98 changes: 93 additions & 5 deletions src/models/gamestate.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,109 @@
use super::MetadataOwned;
use crate::flavor::reencode_float;
use serde::{Deserialize, Deserializer};
use std::collections::HashMap;


use serde::{Deserialize, Deserializer, Serialize};

use crate::{Ck3Date};
use crate::flavor::reencode_float;
use crate::maybe_object::MaybeObject;

use super::MetadataOwned;

#[derive(Debug, Deserialize)]
pub struct Gamestate {
pub meta_data: MetadataOwned,
pub living: HashMap<u64, LivingCharacter>,
pub provinces: HashMap<u64, Province>,
pub traits_lookup: Vec<String>,
pub dynasties: Dynasties,
pub religion: Religions,
pub played_character: PlayedCharacter,
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Dynasties {
pub dynasty_house: HashMap<u64, MaybeObject<DynastyHouse>>,
pub dynasties: HashMap<u64, MaybeObject<Dynasty>>,
}


#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct PlayedCharacter {
pub name: String,
pub character: u64
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct DynastyHouse {
pub name: Option<String>,
// pub dynasty: Option<String>
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Dynasty {
pub key: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Province {
pub holding: Holding,
pub fort_level: Option<u64>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Holding {
pub r#type: Option<String>,
pub buildings: Vec<Building>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some saves that omit buildings, and my guess is that paradox often excludes fields that are empty or their default values, so I tend to prefer to use #[serde(default)] and not use Option too much unless there is a difference between empty/0 and None in game.

pub levy: Option<u64>,
pub garrison: Option<u64>,
pub income: Option<f32>
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Building {
pub r#type: Option<String>
}


#[derive(Debug, Deserialize, Serialize)]
pub struct LivingCharacter {
pub alive_data: Option<AliveData>,
pub first_name: String,
pub dynasty_house: Option<u64>,
pub birth: Option<Ck3Date>,
#[serde(default = "default_false")]
pub female: bool,
pub traits: Option<Vec<usize>>,
pub skill: Vec<u64>,
pub faith: Option<u64>
}

#[derive(Debug, Deserialize)]
#[derive(Debug, Deserialize, Serialize)]
pub struct AliveData {
#[serde(default, deserialize_with = "deserialize_eu4_float")]
pub gold: Option<f64>,
pub health: Option<f32>,
pub income: Option<f32>,
pub fertility: Option<f32>,
pub faith: Option<u64>
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Religions {
pub religions: HashMap<u64, Religion>,
pub faiths: HashMap<u64, Faith>
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Religion {
pub tag: String,
pub family: String
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Faith {
pub tag: String,
pub religion: u64
}

pub(crate) fn deserialize_eu4_float<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
Expand All @@ -29,3 +113,7 @@ where
let val: Option<f64> = Option::deserialize(deserializer)?;
val.map(reencode_float).map(Ok).transpose()
}

pub (crate) fn default_false() -> bool {
false
}
1 change: 1 addition & 0 deletions src/models/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct HeaderBorrowed<'a> {
#[derive(Debug, Deserialize)]
pub struct MetadataOwned {
pub version: String,
pub meta_player_name: String
}

#[derive(Debug, Deserialize)]
Expand Down