veloren_server/persistence/
json_models.rs1use common::comp;
2use common_base::dev_panic;
3use hashbrown::HashMap;
4use serde::{Deserialize, Serialize};
5use std::{num::NonZeroU32, string::ToString};
6use vek::{Vec2, Vec3};
7
8#[derive(Serialize, Deserialize)]
9pub struct HumanoidBody {
10 pub species: u8,
11 pub body_type: u8,
12 pub hair_style: u8,
13 pub beard: u8,
14 pub eyes: u8,
15 pub accessory: u8,
16 pub hair_color: u8,
17 pub skin: u8,
18 pub eye_color: u8,
19}
20
21impl From<&comp::humanoid::Body> for HumanoidBody {
22 fn from(body: &comp::humanoid::Body) -> Self {
23 HumanoidBody {
24 species: body.species as u8,
25 body_type: body.body_type as u8,
26 hair_style: body.hair_style,
27 beard: body.beard,
28 eyes: body.eyes,
29 accessory: body.accessory,
30 hair_color: body.hair_color,
31 skin: body.skin,
32 eye_color: body.eye_color,
33 }
34 }
35}
36
37#[derive(Serialize, Deserialize)]
41pub struct GenericBody {
42 pub species: String,
43 pub body_type: String,
44}
45
46macro_rules! generic_body_from_impl {
47 ($body_type:ty) => {
48 impl From<&$body_type> for GenericBody {
49 fn from(body: &$body_type) -> Self {
50 GenericBody {
51 species: body.species.to_string(),
52 body_type: body.body_type.to_string(),
53 }
54 }
55 }
56 };
57}
58
59generic_body_from_impl!(comp::quadruped_low::Body);
60generic_body_from_impl!(comp::quadruped_medium::Body);
61generic_body_from_impl!(comp::quadruped_small::Body);
62generic_body_from_impl!(comp::bird_medium::Body);
63generic_body_from_impl!(comp::crustacean::Body);
64
65#[derive(Serialize, Deserialize)]
66pub struct CharacterPosition {
67 pub waypoint: Option<Vec3<f32>>,
68 pub map_marker: Option<Vec2<i32>>,
69}
70
71pub fn skill_group_to_db_string(skill_group: comp::skillset::SkillGroupKind) -> String {
72 use comp::{item::tool::ToolKind, skillset::SkillGroupKind::*};
73 let skill_group_string = match skill_group {
74 General => "General",
75 Weapon(ToolKind::Sword) => "Weapon Sword",
76 Weapon(ToolKind::Axe) => "Weapon Axe",
77 Weapon(ToolKind::Hammer) => "Weapon Hammer",
78 Weapon(ToolKind::Bow) => "Weapon Bow",
79 Weapon(ToolKind::Staff) => "Weapon Staff",
80 Weapon(ToolKind::Sceptre) => "Weapon Sceptre",
81 Weapon(ToolKind::Pick) => "Weapon Pick",
82 Weapon(ToolKind::Dagger)
83 | Weapon(ToolKind::Shield)
84 | Weapon(ToolKind::Spear)
85 | Weapon(ToolKind::Blowgun)
86 | Weapon(ToolKind::Debug)
87 | Weapon(ToolKind::Farming)
88 | Weapon(ToolKind::Instrument)
89 | Weapon(ToolKind::Throwable)
90 | Weapon(ToolKind::Empty)
91 | Weapon(ToolKind::Natural)
92 | Weapon(ToolKind::Shovel) => panic!(
93 "Tried to add unsupported skill group to database: {:?}",
94 skill_group
95 ),
96 };
97 skill_group_string.to_string()
98}
99
100pub fn db_string_to_skill_group(skill_group_string: &str) -> comp::skillset::SkillGroupKind {
101 use comp::{item::tool::ToolKind, skillset::SkillGroupKind::*};
102 match skill_group_string {
103 "General" => General,
104 "Weapon Sword" => Weapon(ToolKind::Sword),
105 "Weapon Axe" => Weapon(ToolKind::Axe),
106 "Weapon Hammer" => Weapon(ToolKind::Hammer),
107 "Weapon Bow" => Weapon(ToolKind::Bow),
108 "Weapon Staff" => Weapon(ToolKind::Staff),
109 "Weapon Sceptre" => Weapon(ToolKind::Sceptre),
110 "Weapon Pick" => Weapon(ToolKind::Pick),
111
112 _ => panic!(
113 "Tried to convert an unsupported string from the database: {}",
114 skill_group_string
115 ),
116 }
117}
118
119#[derive(Serialize, Deserialize)]
120pub struct DatabaseAbilitySet {
121 mainhand: String,
122 offhand: String,
123 abilities: Vec<String>,
124}
125
126fn aux_ability_to_string(ability: comp::ability::AuxiliaryAbility) -> String {
127 use common::comp::ability::AuxiliaryAbility;
128 match ability {
129 AuxiliaryAbility::MainWeapon(index) => format!("Main Weapon:index:{}", index),
130 AuxiliaryAbility::OffWeapon(index) => format!("Off Weapon:index:{}", index),
131 AuxiliaryAbility::Glider(index) => format!("Glider:index:{}", index),
132 AuxiliaryAbility::Empty => String::from("Empty"),
133 }
134}
135
136fn aux_ability_from_string(ability: &str) -> comp::ability::AuxiliaryAbility {
137 use common::comp::ability::AuxiliaryAbility;
138 let mut parts = ability.split(":index:");
139 match parts.next() {
140 Some("Main Weapon") => match parts
141 .next()
142 .map(|index| index.parse::<usize>().map_err(|_| index))
143 {
144 Some(Ok(index)) => AuxiliaryAbility::MainWeapon(index),
145 Some(Err(error)) => {
146 dev_panic!(format!(
147 "Conversion from database to ability set failed. Unable to parse index for \
148 mainhand abilities: {}",
149 error
150 ));
151 AuxiliaryAbility::Empty
152 },
153 None => {
154 dev_panic!(String::from(
155 "Conversion from database to ability set failed. Unable to find an index for \
156 mainhand abilities"
157 ));
158 AuxiliaryAbility::Empty
159 },
160 },
161 Some("Off Weapon") => match parts
162 .next()
163 .map(|index| index.parse::<usize>().map_err(|_| index))
164 {
165 Some(Ok(index)) => AuxiliaryAbility::OffWeapon(index),
166 Some(Err(error)) => {
167 dev_panic!(format!(
168 "Conversion from database to ability set failed. Unable to parse index for \
169 offhand abilities: {}",
170 error
171 ));
172 AuxiliaryAbility::Empty
173 },
174 None => {
175 dev_panic!(String::from(
176 "Conversion from database to ability set failed. Unable to find an index for \
177 offhand abilities"
178 ));
179 AuxiliaryAbility::Empty
180 },
181 },
182 Some("Glider") => match parts
183 .next()
184 .map(|index| index.parse::<usize>().map_err(|_| index))
185 {
186 Some(Ok(index)) => AuxiliaryAbility::Glider(index),
187 Some(Err(error)) => {
188 dev_panic!(format!(
189 "Conversion from database to ability set failed. Unable to parse index for \
190 offhand abilities: {}",
191 error
192 ));
193 AuxiliaryAbility::Empty
194 },
195 None => {
196 dev_panic!(String::from(
197 "Conversion from database to ability set failed. Unable to find an index for \
198 offhand abilities"
199 ));
200 AuxiliaryAbility::Empty
201 },
202 },
203 Some("Empty") => AuxiliaryAbility::Empty,
204 unknown => {
205 dev_panic!(format!(
206 "Conversion from database to ability set failed. Unknown auxiliary ability: {:#?}",
207 unknown
208 ));
209 AuxiliaryAbility::Empty
210 },
211 }
212}
213
214fn tool_kind_to_string(tool: Option<comp::item::tool::ToolKind>) -> String {
215 use common::comp::item::tool::ToolKind::*;
216 String::from(match tool {
217 Some(Sword) => "Sword",
218 Some(Axe) => "Axe",
219 Some(Hammer) => "Hammer",
220 Some(Bow) => "Bow",
221 Some(Staff) => "Staff",
222 Some(Sceptre) => "Sceptre",
223 Some(Dagger) => "Dagger",
224 Some(Shield) => "Shield",
225 Some(Spear) => "Spear",
226 Some(Blowgun) => "Blowgun",
227 Some(Pick) => "Pick",
228 Some(Shovel) => "Shovel",
229
230 Some(Farming) => "Farming",
232 Some(Debug) => "Debug",
233 Some(Natural) => "Natural",
234 Some(Instrument) => "Instrument",
235 Some(Throwable) => "Throwable",
236 Some(Empty) => "Empty",
237 None => "None",
238 })
239}
240
241fn tool_kind_from_string(tool: String) -> Option<comp::item::tool::ToolKind> {
242 use common::comp::item::tool::ToolKind::*;
243 match tool.as_str() {
244 "Sword" => Some(Sword),
245 "Axe" => Some(Axe),
246 "Hammer" => Some(Hammer),
247 "Bow" => Some(Bow),
248 "Staff" => Some(Staff),
249 "Sceptre" => Some(Sceptre),
250 "Dagger" => Some(Dagger),
251 "Shield" => Some(Shield),
252 "Spear" => Some(Spear),
253 "Blowgun" => Some(Blowgun),
254 "Pick" => Some(Pick),
255 "Farming" => Some(Farming),
256 "Debug" => Some(Debug),
257 "Natural" => Some(Natural),
258 "Empty" => Some(Empty),
259 "None" => None,
260 unknown => {
261 dev_panic!(format!(
262 "Conversion from database to ability set failed. Unknown toolkind: {:#?}",
263 unknown
264 ));
265 None
266 },
267 }
268}
269
270pub fn active_abilities_to_db_model(
271 active_abilities: &comp::ability::ActiveAbilities,
272) -> Vec<DatabaseAbilitySet> {
273 active_abilities
274 .auxiliary_sets
275 .iter()
276 .map(|((mainhand, offhand), abilities)| DatabaseAbilitySet {
277 mainhand: tool_kind_to_string(*mainhand),
278 offhand: tool_kind_to_string(*offhand),
279 abilities: abilities
280 .iter()
281 .map(|ability| aux_ability_to_string(*ability))
282 .collect(),
283 })
284 .collect::<Vec<_>>()
285}
286
287pub fn active_abilities_from_db_model(
288 ability_sets: Vec<DatabaseAbilitySet>,
289) -> comp::ability::ActiveAbilities {
290 let ability_sets = ability_sets
291 .into_iter()
292 .map(
293 |DatabaseAbilitySet {
294 mainhand,
295 offhand,
296 abilities,
297 }| {
298 let mut auxiliary_abilities =
299 vec![comp::ability::AuxiliaryAbility::Empty; comp::ability::BASE_ABILITY_LIMIT];
300 for (empty, ability) in auxiliary_abilities.iter_mut().zip(abilities.into_iter()) {
301 *empty = aux_ability_from_string(&ability);
302 }
303 (
304 (
305 tool_kind_from_string(mainhand),
306 tool_kind_from_string(offhand),
307 ),
308 auxiliary_abilities,
309 )
310 },
311 )
312 .collect::<HashMap<_, _>>();
313 comp::ability::ActiveAbilities::from_auxiliary(
314 ability_sets,
315 Some(comp::ability::BASE_ABILITY_LIMIT),
316 )
317}
318
319#[derive(Serialize, Deserialize)]
323pub struct DatabaseItemProperties {
324 #[serde(skip_serializing_if = "Option::is_none")]
325 durability: Option<NonZeroU32>,
326}
327
328pub fn item_properties_to_db_model(item: &comp::Item) -> DatabaseItemProperties {
329 DatabaseItemProperties {
330 durability: item.persistence_durability(),
331 }
332}
333
334pub fn apply_db_item_properties(item: &mut comp::Item, properties: &DatabaseItemProperties) {
335 let DatabaseItemProperties { durability } = properties;
336 item.persistence_set_durability(*durability);
337}
338
339#[cfg(test)]
340pub mod tests {
341 #[test]
342 fn test_default_item_properties() {
343 use super::DatabaseItemProperties;
344 const DEFAULT_ITEM_PROPERTIES: &str = "{}";
345 let _ = serde_json::de::from_str::<DatabaseItemProperties>(DEFAULT_ITEM_PROPERTIES).expect(
346 "Default value should always load to ensure that changes to item properties is always \
347 forward compatible with migration V50.",
348 );
349 }
350}