steel_core/player/player_data_storage/
stats.rs1use crate::player::player_data::PersistentStat;
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeMap;
4use steel_registry::stat::Stat;
5use steel_registry::{REGISTRY, RegistryExt};
6use steel_utils::Identifier;
7use tokio::io;
8
9#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
10#[serde(default, deny_unknown_fields)]
11pub(super) struct PlayerStatsFile {
12 pub(super) stats: BTreeMap<Identifier, BTreeMap<Identifier, i32>>,
13}
14
15impl PlayerStatsFile {
16 pub(super) fn from_persistent_stats(persistent_stats: &[PersistentStat]) -> io::Result<Self> {
17 let mut stats = BTreeMap::new();
18
19 for PersistentStat { stat, count } in persistent_stats {
20 if stats
21 .entry(stat.stat_type_key().clone())
22 .or_insert_with(BTreeMap::new)
23 .insert(stat.stat_value_key().clone(), *count)
24 .is_some()
25 {
26 return Err(io::Error::new(
27 io::ErrorKind::InvalidData,
28 format!("duplicate stat {stat} found in slice"),
29 ));
30 }
31 }
32
33 Ok(Self { stats })
34 }
35
36 pub(super) fn into_persistent_stats(self) -> Vec<PersistentStat> {
37 let mut stats = Vec::new();
38
39 for (stat_type_key, stats_in_stat_type) in self.stats {
40 let Some(stat_type_entry) = REGISTRY.stat_types.by_key(&stat_type_key) else {
41 log::warn!(
42 "Player stats file referenced unknown stat type {stat_type_key}, skipping stats in it"
43 );
44 continue;
45 };
46
47 for (stat_value_key, count) in stats_in_stat_type {
48 let Some(stat_value) = stat_type_entry.value_from_key(&stat_value_key) else {
49 log::warn!(
50 "Player stats file referenced unknown stat {}.{}:{}.{}, skipping it",
51 stat_type_key.namespace,
52 stat_type_key.path,
53 stat_value_key.namespace,
54 stat_value_key.path
55 );
56 continue;
57 };
58
59 stats.push(PersistentStat {
60 stat: Stat::from_erased(stat_type_entry, stat_value),
61 count,
62 });
63 }
64 }
65
66 stats
67 }
68}
69
70pub(super) fn serialize_player_stats_file(file: &PlayerStatsFile) -> String {
71 let mut output = String::new();
72
73 for (stat_type, stats_in_stat_type) in &file.stats {
74 output.push_str("[stats.\"");
75 output.push_str(&stat_type.namespace);
76 output.push(':');
77 output.push_str(&stat_type.path);
78 output.push_str("\"]\n");
79
80 for (stat_value, count) in stats_in_stat_type {
81 output.push('\"');
82 output.push_str(&stat_value.namespace);
83 output.push(':');
84 output.push_str(&stat_value.path);
85 output.push_str("\" = ");
86 output.push_str(&count.to_string());
87 output.push('\n');
88 }
89
90 output.push('\n');
91 }
92
93 output
94}