Beatsaber Rust Utilities: A Beatsaber V3 parsing library.
beatsaber beatmap
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at dev 502 lines 17 kB view raw
1//! Events that control animations unique to each environment. 2//! 3//! Unlike the other V3 group event types, FX events use a template-like JSON syntax. 4//! In order to have standardized structure across all V3 events, custom serialization has been written in [`FxEventContainer`]. 5//! Because of this, neither [`FxEventBox`] nor [`FxEventGroup`] implement [`Serialize`] nor [`Deserialize`] directly. 6 7use crate::difficulty::lightshow::DistributionType; 8use crate::difficulty::lightshow::easing::Easing; 9use crate::difficulty::lightshow::filter::Filter; 10use crate::loose_bool::LooseBool; 11use crate::{TransitionType, impl_event_box, impl_event_data, impl_event_group, impl_timed}; 12use indexmap::IndexSet; 13use ordered_float::OrderedFloat; 14use serde::ser::SerializeStruct; 15use serde::{Deserialize, Deserializer, Serialize, Serializer}; 16use std::ops::{Deref, DerefMut}; 17 18/// Contains a list of [`FxEventBox`] as well as the [`Serialize`] and [`Deserialize`] implementations for FX events. 19#[derive(Debug, Clone, PartialEq, Default)] 20#[cfg_attr( 21 feature = "bevy_reflect", 22 derive(bevy_reflect::Reflect), 23 reflect(Debug, Clone, PartialEq) 24)] 25pub struct FxEventContainer { 26 pub event_boxes: Vec<FxEventBox>, 27} 28 29impl Deref for FxEventContainer { 30 type Target = Vec<FxEventBox>; 31 32 fn deref(&self) -> &Self::Target { 33 &self.event_boxes 34 } 35} 36 37impl DerefMut for FxEventContainer { 38 fn deref_mut(&mut self) -> &mut Self::Target { 39 &mut self.event_boxes 40 } 41} 42 43/// The format that is actually stored in JSON. 44#[derive(Deserialize)] 45struct FxEventInput { 46 #[serde(rename = "vfxEventBoxGroups")] 47 event_boxes: Vec<FxEventBoxRaw>, 48 #[serde(rename = "_fxEventsCollection")] 49 arrays: FxEventArrays, 50} 51 52#[derive(Deserialize, Serialize)] 53struct FxEventArrays { 54 #[serde(rename = "_fl")] 55 event_data: Vec<FxEventData>, 56} 57 58impl<'de> Deserialize<'de> for FxEventContainer { 59 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 60 where 61 D: Deserializer<'de>, 62 { 63 let FxEventInput { 64 event_boxes, 65 arrays, 66 } = FxEventInput::deserialize(deserializer)?; 67 68 let event_boxes = event_boxes 69 .into_iter() 70 .map(|raw_box| { 71 let groups = raw_box 72 .groups 73 .into_iter() 74 .map(|raw_group| { 75 let data = raw_group 76 .data_ids 77 .into_iter() 78 .map(|id| { 79 arrays.event_data.get(id).cloned().ok_or_else(|| { 80 serde::de::Error::custom(format!( 81 "Missing FxEventData with id {}", 82 id 83 )) 84 }) 85 }) 86 .collect::<Result<Vec<FxEventData>, _>>()?; 87 88 Ok(FxEventGroup { 89 filter: raw_group.filter, 90 beat_dist_type: raw_group.beat_dist_type, 91 beat_dist_value: raw_group.beat_dist_value, 92 fx_dist_type: raw_group.fx_dist_type, 93 fx_dist_value: raw_group.fx_dist_value, 94 fx_dist_effect_first: raw_group.fx_dist_effect_first, 95 fx_dist_easing: raw_group.fx_dist_easing, 96 data, 97 }) 98 }) 99 .collect::<Result<Vec<FxEventGroup>, _>>()?; 100 101 Ok(FxEventBox { 102 beat: raw_box.beat, 103 group_id: raw_box.group_id, 104 groups, 105 }) 106 }) 107 .collect::<Result<Vec<FxEventBox>, _>>()?; 108 109 Ok(FxEventContainer { event_boxes }) 110 } 111} 112 113// Todo avoid allocations. 114impl Serialize for FxEventContainer { 115 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 116 where 117 S: Serializer, 118 { 119 let mut event_data: IndexSet<FxEventDataKey> = IndexSet::new(); 120 121 // Todo Deduplicate. 122 let raw_boxes = self 123 .event_boxes 124 .iter() 125 .map(|event_box| { 126 let groups_raw = event_box 127 .groups 128 .iter() 129 .map(|event_group| { 130 let mut ids = Vec::new(); 131 132 for data in &event_group.data { 133 let data_key: FxEventDataKey = data.into(); 134 135 let index = event_data.get_index_of(&data_key).unwrap_or_else(|| { 136 let index = event_data.len(); 137 event_data.insert(data_key); 138 index 139 }); 140 141 ids.push(index); 142 } 143 144 FxEventGroupRaw { 145 filter: event_group.filter.clone(), 146 beat_dist_type: event_group.beat_dist_type.clone(), 147 beat_dist_value: event_group.beat_dist_value, 148 fx_dist_type: event_group.fx_dist_type.clone(), 149 fx_dist_value: event_group.fx_dist_value, 150 fx_dist_effect_first: event_group.fx_dist_effect_first, 151 fx_dist_easing: event_group.fx_dist_easing.clone(), 152 data_ids: ids, 153 } 154 }) 155 .collect::<Vec<FxEventGroupRaw>>(); 156 157 FxEventBoxRaw { 158 beat: event_box.beat, 159 group_id: event_box.group_id, 160 groups: groups_raw, 161 } 162 }) 163 .collect::<Vec<FxEventBoxRaw>>(); 164 165 let mut state = serializer.serialize_struct("FxEventContainer", 2)?; 166 state.serialize_field("vfxEventBoxGroups", &raw_boxes)?; 167 state.serialize_field( 168 "_fxEventsCollection", 169 &FxEventArrays { 170 event_data: event_data.into_iter().map(FxEventData::from).collect(), 171 }, 172 )?; 173 state.end() 174 } 175} 176 177/// A collection of [`FxEventGroup`]s that share the same group ID and beat. 178/// 179/// Does not implement [`Serialize`] nor [`Deserialize`]. For more info, see the [module docs](super::fx). 180#[derive(Debug, Clone, PartialEq)] 181#[cfg_attr( 182 feature = "bevy_reflect", 183 derive(bevy_reflect::Reflect), 184 reflect(Debug, Clone, PartialEq) 185)] 186pub struct FxEventBox { 187 /// The time the event takes place. 188 pub beat: f32, 189 /// The ID of the collection of objects that this event effects. 190 pub group_id: i32, 191 pub groups: Vec<FxEventGroup>, 192} 193 194/// The raw JSON structure that uses [data IDs](FxEventGroupRaw::data_ids) rather than actual [event data](FxEventData). 195#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 196struct FxEventBoxRaw { 197 #[serde(rename = "b")] 198 beat: f32, 199 #[serde(rename = "g")] 200 group_id: i32, 201 #[serde(rename = "e")] 202 groups: Vec<FxEventGroupRaw>, 203} 204 205impl Default for FxEventBox { 206 fn default() -> Self { 207 Self { 208 beat: 0.0, 209 group_id: 0, 210 groups: vec![FxEventGroup::default()], 211 } 212 } 213} 214 215impl_timed!(FxEventBox::beat); 216impl_event_box!(FxEventBox, FxEventGroup, FxEventData); 217 218/// A collection of [`FxEventData`] that share the same [`Filter`] and distribution. 219/// 220/// Does not implement [`Serialize`] nor [`Deserialize`]. For more info, see the [module docs](super::fx). 221#[derive(Debug, Clone, PartialEq)] 222#[cfg_attr( 223 feature = "bevy_reflect", 224 derive(bevy_reflect::Reflect), 225 reflect(Debug, Clone, PartialEq) 226)] 227pub struct FxEventGroup { 228 pub filter: Filter, 229 pub beat_dist_type: DistributionType, 230 /// The strength of the beat distribution. Dependent on the [distribution type](Self::beat_dist_type). 231 /// 232 /// A value of zero will have no effect. 233 pub beat_dist_value: f32, 234 pub fx_dist_type: DistributionType, 235 /// The strength of the brightness distribution. Dependent on the [distribution type](Self::fx_dist_type). 236 /// 237 /// A value of zero will have no effect. 238 pub fx_dist_value: f32, 239 /// Whether the first [`FxEventData`] of the group will be effected by brightness distribution. 240 pub fx_dist_effect_first: LooseBool, 241 pub fx_dist_easing: Option<Easing>, 242 /// In the actual JSON structure, this is a list of indexes to a separate list of event data. 243 /// For consistency, this is merged during parsing. 244 pub data: Vec<FxEventData>, 245} 246 247/// The raw JSON structure that uses [data IDs](self::data_ids) rather than actual [event data](FxEventData). 248#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 249struct FxEventGroupRaw { 250 #[serde(rename = "f")] 251 filter: Filter, 252 #[serde(rename = "d")] 253 beat_dist_type: DistributionType, 254 #[serde(rename = "w")] 255 beat_dist_value: f32, 256 #[serde(rename = "t")] 257 fx_dist_type: DistributionType, 258 #[serde(rename = "s")] 259 fx_dist_value: f32, 260 #[serde(rename = "b")] 261 fx_dist_effect_first: LooseBool, 262 #[serde(rename = "i")] 263 fx_dist_easing: Option<Easing>, 264 #[serde(rename = "l")] 265 data_ids: Vec<usize>, 266} 267 268impl Default for FxEventGroup { 269 fn default() -> Self { 270 Self { 271 filter: Default::default(), 272 beat_dist_type: Default::default(), 273 beat_dist_value: 0.0, 274 fx_dist_type: Default::default(), 275 fx_dist_value: 0.0, 276 fx_dist_effect_first: Default::default(), 277 fx_dist_easing: Some(Easing::Linear), 278 data: vec![FxEventData::default()], 279 } 280 } 281} 282 283impl_event_group!(FxEventGroup::get_fx_offset, FxEventData); 284 285impl FxEventGroup { 286 /// Returns the FX value that the event will be offset for a given light ID. 287 /// # Panics 288 /// Will panic if the light ID is greater than or equal to the group size. 289 #[deprecated(note = "Experimental. Does not consider random in filter calculations.")] 290 #[allow(deprecated)] 291 pub fn get_fx_offset(&self, light_id: i32, group_size: i32) -> f32 { 292 self.fx_dist_type.compute_value_offset( 293 light_id, 294 group_size, 295 &self.filter, 296 self.fx_dist_value, 297 self.data.last().map(|data| data.beat_offset), 298 self.fx_dist_easing, 299 ) 300 } 301} 302 303/// The lowest-level group event type, which determines the base value of the event. 304#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 305#[cfg_attr( 306 feature = "bevy_reflect", 307 derive(bevy_reflect::Reflect), 308 reflect(Debug, Clone, PartialEq) 309)] 310pub struct FxEventData { 311 /// The number of beats the event will be offset from the [`FxEventBox`]'s beat. 312 #[serde(rename = "b")] 313 pub beat_offset: f32, 314 #[serde(rename = "p")] 315 pub transition_type: TransitionType, 316 #[serde(rename = "i")] 317 pub easing: Easing, 318 /// The base value of the effect. 319 #[serde(rename = "v")] 320 pub value: f32, 321} 322 323/// A `PartialEq` and `Hash` version of [`FxEventData`], allowing for deduplication. 324#[derive(Debug, Clone, Eq, PartialEq, Hash)] 325struct FxEventDataKey { 326 beat_offset: OrderedFloat<f32>, 327 transition_type: TransitionType, 328 easing: Easing, 329 value: OrderedFloat<f32>, 330} 331 332impl From<&FxEventData> for FxEventDataKey { 333 fn from(value: &FxEventData) -> Self { 334 Self { 335 beat_offset: value.beat_offset.into(), 336 transition_type: value.transition_type, 337 easing: value.easing, 338 value: value.value.into(), 339 } 340 } 341} 342 343impl From<FxEventDataKey> for FxEventData { 344 fn from(value: FxEventDataKey) -> Self { 345 Self { 346 beat_offset: value.beat_offset.into(), 347 transition_type: value.transition_type, 348 easing: value.easing, 349 value: value.value.into(), 350 } 351 } 352} 353 354impl Default for FxEventData { 355 fn default() -> Self { 356 Self { 357 beat_offset: 0.0, 358 transition_type: Default::default(), 359 easing: Easing::default(), 360 value: 1.0, 361 } 362 } 363} 364 365impl_event_data!(FxEventData); 366 367#[cfg(test)] 368mod tests { 369 use super::*; 370 use crate::loose_bool::LooseBool; 371 use serde_json::{Value, json}; 372 373 fn get_test_container() -> FxEventContainer { 374 FxEventContainer { 375 event_boxes: vec![FxEventBox { 376 beat: 2.0, 377 group_id: 0, 378 groups: vec![get_test_group(), get_test_group()], 379 }], 380 } 381 } 382 383 fn get_test_group() -> FxEventGroup { 384 FxEventGroup { 385 filter: Default::default(), 386 beat_dist_type: DistributionType::Wave, 387 beat_dist_value: 1.0, 388 fx_dist_type: DistributionType::Wave, 389 fx_dist_value: 1.0, 390 fx_dist_effect_first: LooseBool::True, 391 fx_dist_easing: Some(Easing::None), 392 data: vec![FxEventData { 393 beat_offset: 0.0, 394 transition_type: TransitionType::Transition, 395 easing: Easing::Linear, 396 value: 100.0, 397 }], 398 } 399 } 400 401 fn get_test_json() -> Value { 402 json!( 403 { 404 "vfxEventBoxGroups": 405 [ 406 { 407 "b": 2.0, 408 "g": 0, 409 "e": 410 [ 411 { 412 "f": 413 { 414 "c": 0, 415 "f": 1, 416 "p": 1, 417 "t": 0, 418 "r": 0, 419 "n": 0, 420 "s": 0, 421 "l": 1.0, 422 "d": 0 423 }, 424 "w": 1.0, 425 "d": 1, 426 "s": 1.0, 427 "t": 1, 428 "b": 1, 429 "i": -1, 430 "l": 431 [ 432 0 433 ] 434 }, 435 { 436 "f": 437 { 438 "c": 0, 439 "f": 1, 440 "p": 1, 441 "t": 0, 442 "r": 0, 443 "n": 0, 444 "s": 0, 445 "l": 1.0, 446 "d": 0 447 }, 448 "w": 1.0, 449 "d": 1, 450 "s": 1.0, 451 "t": 1, 452 "b": 1, 453 "i": -1, 454 "l": 455 [ 456 0 457 ] 458 } 459 ] 460 } 461 ], 462 "_fxEventsCollection": 463 { 464 "_fl": 465 [ 466 { 467 "b": 0.0, 468 "p": 0, 469 "i": 0, 470 "v": 100.0 471 } 472 ] 473 } 474 } 475 ) 476 } 477 478 #[test] 479 fn test_deserialize() { 480 let container: FxEventContainer = serde_json::from_value(get_test_json()).unwrap(); 481 482 assert_eq!(container, get_test_container()); 483 } 484 485 #[test] 486 fn test_serialize() { 487 let out_json = serde_json::to_value(&get_test_container()).unwrap(); 488 489 assert_eq!(out_json, get_test_json()); 490 } 491 492 #[test] 493 fn test_round_trip() { 494 let container: FxEventContainer = serde_json::from_value(get_test_json()).unwrap(); 495 496 let out_json = serde_json::to_string_pretty(&container).unwrap(); 497 498 let round_trip: FxEventContainer = serde_json::from_str(&out_json).unwrap(); 499 500 assert_eq!(container, round_trip); 501 } 502}