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