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::Empty)
90 | Weapon(ToolKind::Natural)
91 | Weapon(ToolKind::Shovel) => panic!(
92 "Tried to add unsupported skill group to database: {:?}",
93 skill_group
94 ),
95 };
96 skill_group_string.to_string()
97}
98
99pub fn db_string_to_skill_group(skill_group_string: &str) -> comp::skillset::SkillGroupKind {
100 use comp::{item::tool::ToolKind, skillset::SkillGroupKind::*};
101 match skill_group_string {
102 "General" => General,
103 "Weapon Sword" => Weapon(ToolKind::Sword),
104 "Weapon Axe" => Weapon(ToolKind::Axe),
105 "Weapon Hammer" => Weapon(ToolKind::Hammer),
106 "Weapon Bow" => Weapon(ToolKind::Bow),
107 "Weapon Staff" => Weapon(ToolKind::Staff),
108 "Weapon Sceptre" => Weapon(ToolKind::Sceptre),
109 "Weapon Pick" => Weapon(ToolKind::Pick),
110
111 _ => panic!(
112 "Tried to convert an unsupported string from the database: {}",
113 skill_group_string
114 ),
115 }
116}
117
118#[derive(Serialize, Deserialize)]
119pub struct DatabaseAbilitySet {
120 mainhand: String,
121 offhand: String,
122 abilities: Vec<String>,
123}
124
125fn aux_ability_to_string(ability: comp::ability::AuxiliaryAbility) -> String {
126 use common::comp::ability::AuxiliaryAbility;
127 match ability {
128 AuxiliaryAbility::MainWeapon(index) => format!("Main Weapon:index:{}", index),
129 AuxiliaryAbility::OffWeapon(index) => format!("Off Weapon:index:{}", index),
130 AuxiliaryAbility::Glider(index) => format!("Glider:index:{}", index),
131 AuxiliaryAbility::Empty => String::from("Empty"),
132 }
133}
134
135fn aux_ability_from_string(ability: &str) -> comp::ability::AuxiliaryAbility {
136 use common::comp::ability::AuxiliaryAbility;
137 let mut parts = ability.split(":index:");
138 match parts.next() {
139 Some("Main Weapon") => match parts
140 .next()
141 .map(|index| index.parse::<usize>().map_err(|_| index))
142 {
143 Some(Ok(index)) => AuxiliaryAbility::MainWeapon(index),
144 Some(Err(error)) => {
145 dev_panic!(format!(
146 "Conversion from database to ability set failed. Unable to parse index for \
147 mainhand abilities: {}",
148 error
149 ));
150 AuxiliaryAbility::Empty
151 },
152 None => {
153 dev_panic!(String::from(
154 "Conversion from database to ability set failed. Unable to find an index for \
155 mainhand abilities"
156 ));
157 AuxiliaryAbility::Empty
158 },
159 },
160 Some("Off Weapon") => match parts
161 .next()
162 .map(|index| index.parse::<usize>().map_err(|_| index))
163 {
164 Some(Ok(index)) => AuxiliaryAbility::OffWeapon(index),
165 Some(Err(error)) => {
166 dev_panic!(format!(
167 "Conversion from database to ability set failed. Unable to parse index for \
168 offhand abilities: {}",
169 error
170 ));
171 AuxiliaryAbility::Empty
172 },
173 None => {
174 dev_panic!(String::from(
175 "Conversion from database to ability set failed. Unable to find an index for \
176 offhand abilities"
177 ));
178 AuxiliaryAbility::Empty
179 },
180 },
181 Some("Glider") => match parts
182 .next()
183 .map(|index| index.parse::<usize>().map_err(|_| index))
184 {
185 Some(Ok(index)) => AuxiliaryAbility::Glider(index),
186 Some(Err(error)) => {
187 dev_panic!(format!(
188 "Conversion from database to ability set failed. Unable to parse index for \
189 offhand abilities: {}",
190 error
191 ));
192 AuxiliaryAbility::Empty
193 },
194 None => {
195 dev_panic!(String::from(
196 "Conversion from database to ability set failed. Unable to find an index for \
197 offhand abilities"
198 ));
199 AuxiliaryAbility::Empty
200 },
201 },
202 Some("Empty") => AuxiliaryAbility::Empty,
203 unknown => {
204 dev_panic!(format!(
205 "Conversion from database to ability set failed. Unknown auxiliary ability: {:#?}",
206 unknown
207 ));
208 AuxiliaryAbility::Empty
209 },
210 }
211}
212
213fn tool_kind_to_string(tool: Option<comp::item::tool::ToolKind>) -> String {
214 use common::comp::item::tool::ToolKind::*;
215 String::from(match tool {
216 Some(Sword) => "Sword",
217 Some(Axe) => "Axe",
218 Some(Hammer) => "Hammer",
219 Some(Bow) => "Bow",
220 Some(Staff) => "Staff",
221 Some(Sceptre) => "Sceptre",
222 Some(Dagger) => "Dagger",
223 Some(Shield) => "Shield",
224 Some(Spear) => "Spear",
225 Some(Blowgun) => "Blowgun",
226 Some(Pick) => "Pick",
227 Some(Shovel) => "Shovel",
228
229 Some(Farming) => "Farming",
231 Some(Debug) => "Debug",
232 Some(Natural) => "Natural",
233 Some(Instrument) => "Instrument",
234 Some(Empty) => "Empty",
235 None => "None",
236 })
237}
238
239fn tool_kind_from_string(tool: String) -> Option<comp::item::tool::ToolKind> {
240 use common::comp::item::tool::ToolKind::*;
241 match tool.as_str() {
242 "Sword" => Some(Sword),
243 "Axe" => Some(Axe),
244 "Hammer" => Some(Hammer),
245 "Bow" => Some(Bow),
246 "Staff" => Some(Staff),
247 "Sceptre" => Some(Sceptre),
248 "Dagger" => Some(Dagger),
249 "Shield" => Some(Shield),
250 "Spear" => Some(Spear),
251 "Blowgun" => Some(Blowgun),
252 "Pick" => Some(Pick),
253 "Farming" => Some(Farming),
254 "Debug" => Some(Debug),
255 "Natural" => Some(Natural),
256 "Empty" => Some(Empty),
257 "None" => None,
258 unknown => {
259 dev_panic!(format!(
260 "Conversion from database to ability set failed. Unknown toolkind: {:#?}",
261 unknown
262 ));
263 None
264 },
265 }
266}
267
268pub fn active_abilities_to_db_model(
269 active_abilities: &comp::ability::ActiveAbilities,
270) -> Vec<DatabaseAbilitySet> {
271 active_abilities
272 .auxiliary_sets
273 .iter()
274 .map(|((mainhand, offhand), abilities)| DatabaseAbilitySet {
275 mainhand: tool_kind_to_string(*mainhand),
276 offhand: tool_kind_to_string(*offhand),
277 abilities: abilities
278 .iter()
279 .map(|ability| aux_ability_to_string(*ability))
280 .collect(),
281 })
282 .collect::<Vec<_>>()
283}
284
285pub fn active_abilities_from_db_model(
286 ability_sets: Vec<DatabaseAbilitySet>,
287) -> comp::ability::ActiveAbilities {
288 let ability_sets = ability_sets
289 .into_iter()
290 .map(
291 |DatabaseAbilitySet {
292 mainhand,
293 offhand,
294 abilities,
295 }| {
296 let mut auxiliary_abilities =
297 vec![comp::ability::AuxiliaryAbility::Empty; comp::ability::BASE_ABILITY_LIMIT];
298 for (empty, ability) in auxiliary_abilities.iter_mut().zip(abilities.into_iter()) {
299 *empty = aux_ability_from_string(&ability);
300 }
301 (
302 (
303 tool_kind_from_string(mainhand),
304 tool_kind_from_string(offhand),
305 ),
306 auxiliary_abilities,
307 )
308 },
309 )
310 .collect::<HashMap<_, _>>();
311 comp::ability::ActiveAbilities::from_auxiliary(
312 ability_sets,
313 Some(comp::ability::BASE_ABILITY_LIMIT),
314 )
315}
316
317#[derive(Serialize, Deserialize)]
321pub struct DatabaseItemProperties {
322 #[serde(skip_serializing_if = "Option::is_none")]
323 durability: Option<NonZeroU32>,
324}
325
326pub fn item_properties_to_db_model(item: &comp::Item) -> DatabaseItemProperties {
327 DatabaseItemProperties {
328 durability: item.persistence_durability(),
329 }
330}
331
332pub fn apply_db_item_properties(item: &mut comp::Item, properties: &DatabaseItemProperties) {
333 let DatabaseItemProperties { durability } = properties;
334 item.persistence_set_durability(*durability);
335}
336
337#[cfg(test)]
338pub mod tests {
339 #[test]
340 fn test_default_item_properties() {
341 use super::DatabaseItemProperties;
342 const DEFAULT_ITEM_PROPERTIES: &str = "{}";
343 let _ = serde_json::de::from_str::<DatabaseItemProperties>(DEFAULT_ITEM_PROPERTIES).expect(
344 "Default value should always load to ensure that changes to item properties is always \
345 forward compatible with migration V50.",
346 );
347 }
348}