1use alloc::borrow::Cow;
5use alloc::boxed::Box;
6use alloc::sync::Arc;
7use alloc::vec::Vec;
8
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10
11use super::schema;
12
13mod behavior {
14 use super::*;
15 use crate::behavior::{Behavior, BehaviorSet, BehaviorSetTransaction, Host, Persistence};
16 use crate::transaction::{Merge as _, Transactional as _};
17
18 impl<H> Serialize for BehaviorSet<H>
21 where
22 H: Host<Attachment: Serialize>,
23 {
24 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
25 schema::BehaviorSetSer::BehaviorSetV1 {
26 behaviors: self
27 .iter()
28 .filter_map(|entry| {
29 let Persistence(behavior) = entry.behavior.persistence()?;
30 Some(schema::BehaviorSetEntryV1Ser {
31 attachment: entry.attachment.clone(),
32 behavior,
33 })
34 })
35 .collect::<Vec<schema::BehaviorSetEntryV1Ser<H::Attachment>>>(),
36 }
37 .serialize(serializer)
38 }
39 }
40
41 impl<'de, H> Deserialize<'de> for BehaviorSet<H>
42 where
43 H: Host<Attachment: serde::de::DeserializeOwned>,
44 {
45 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
46 match schema::BehaviorSetSer::<H::Attachment>::deserialize(deserializer)? {
47 schema::BehaviorSetSer::BehaviorSetV1 { behaviors } => {
48 let mut set = BehaviorSet::new();
49 set.transact(|txn, _| {
50 #[allow(
51 unreachable_patterns,
52 reason = "remove this when BehaviorV1Ser is nonempty"
53 )]
54 for schema::BehaviorSetEntryV1Ser {
55 behavior,
56 attachment,
57 } in behaviors
58 {
59 txn.merge_from(BehaviorSetTransaction::insert(
60 attachment,
61 behavior.into(),
62 ))?;
63 }
64 Ok(())
65 })
66 .expect("BehaviorSet execute failure");
67 Ok(set)
68 }
69 }
70 }
71 }
72
73 impl<A> From<schema::BehaviorV1Ser> for Arc<dyn Behavior<A>> {
74 fn from(value: schema::BehaviorV1Ser) -> Self {
75 match value {}
76 }
77 }
78}
79
80mod block {
81 use super::*;
82 use crate::block::{
83 AnimationChange, AnimationHint, Atom, Block, BlockAttributes, BlockCollision, Composite,
84 Modifier, Move, PlacementAction, Primitive, Quote, RotationPlacementRule, TickAction, Zoom,
85 text,
86 };
87 use crate::math::{Rgb, Rgba};
88 use crate::tag;
89 use schema::{BlockSer, ModifierSer};
90
91 impl Serialize for Block {
92 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
93 BlockSer::BlockV1 {
94 primitive: match self.primitive() {
95 Primitive::Indirect(definition) => schema::PrimitiveSer::IndirectV1 {
96 definition: definition.clone(),
97 },
98 &Primitive::Atom(Atom {
99 color,
100 emission,
101 collision,
102 }) => schema::PrimitiveSer::AtomV1 {
103 attributes: BlockAttributes::DEFAULT_REF.into(),
106 color: color.into(),
107 light_emission: emission.into(),
108 collision: collision.into(),
109 },
110 &Primitive::Recur {
111 ref space,
112 offset,
113 resolution,
114 } => schema::PrimitiveSer::RecurV1 {
115 attributes: BlockAttributes::DEFAULT_REF.into(),
118 space: space.clone(),
119 offset: offset.into(),
120 resolution,
121 },
122 &Primitive::Air => schema::PrimitiveSer::AirV1,
123 &Primitive::Text { ref text, offset } => {
124 schema::PrimitiveSer::TextPrimitiveV1 {
125 text: text.into(),
126 offset: offset.into(),
127 }
128 }
129 &Primitive::Raw { .. } => {
130 return Err(serde::ser::Error::custom("cannot serialize Primitive::Raw"));
131 }
132 },
133 modifiers: self.modifiers().iter().map(ModifierSer::from).collect(),
134 }
135 .serialize(serializer)
136 }
137 }
138
139 impl<'de> Deserialize<'de> for Block {
140 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
141 Ok(match BlockSer::deserialize(deserializer)? {
142 BlockSer::BlockV1 {
143 primitive,
144 modifiers,
145 } => {
146 let (primitive, attributes) = primitive_from_schema(primitive);
147 let mut block = Block::from_primitive(primitive);
148
149 let attr_mod_iter = attributes
150 .filter(|a| a != BlockAttributes::DEFAULT_REF)
151 .map(Modifier::from)
152 .into_iter();
153 let general_mod_iter = modifiers.into_iter().map(Modifier::from);
154
155 block.modifiers_mut().extend(attr_mod_iter.chain(general_mod_iter));
156 block
157 }
158 })
159 }
160 }
161
162 fn primitive_from_schema(value: schema::PrimitiveSer) -> (Primitive, Option<BlockAttributes>) {
163 match value {
164 schema::PrimitiveSer::IndirectV1 { definition } => {
165 (Primitive::Indirect(definition), None)
166 }
167 schema::PrimitiveSer::AtomV1 {
168 attributes,
169 color,
170 light_emission: emission,
171 collision,
172 } => (
173 Primitive::Atom(Atom {
174 color: Rgba::from(color),
175 emission: Rgb::from(emission),
176 collision: collision.into(),
177 }),
178 Some(attributes.into()),
179 ),
180 schema::PrimitiveSer::RecurV1 {
181 attributes,
182 space,
183 offset,
184 resolution,
185 } => (
186 Primitive::Recur {
187 space,
188 offset: offset.into(),
189 resolution,
190 },
191 Some(attributes.into()),
192 ),
193 schema::PrimitiveSer::AirV1 => (Primitive::Air, None),
194 schema::PrimitiveSer::TextPrimitiveV1 { text, offset } => (
195 Primitive::Text {
196 text: text.into(),
197 offset: offset.into(),
198 },
199 None,
200 ),
201 }
202 }
203
204 impl<'a> From<&'a BlockAttributes> for schema::BlockAttributesV1Ser {
205 fn from(value: &'a BlockAttributes) -> Self {
206 let &BlockAttributes {
207 ref display_name,
208 selectable,
209 ref inventory,
210 ref ambient_sound,
211 rotation_rule,
212 ref placement_action,
213 ref tick_action,
214 ref activation_action,
215 animation_hint,
216 } = value;
217 schema::BlockAttributesV1Ser {
218 display_name: display_name.clone(),
219 selectable,
220 inventory: inventory.into(),
221 ambient_sound: ambient_sound.into(),
222 rotation_rule: rotation_rule.into(),
223 placement_action: placement_action.as_ref().map(
224 |&PlacementAction {
225 ref operation,
226 in_front,
227 }| schema::PlacementActionSer {
228 operation: operation.clone(),
229 in_front,
230 },
231 ),
232 tick_action: tick_action.as_ref().map(
233 |&TickAction {
234 ref operation,
235 schedule,
236 }| schema::TickActionSer {
237 operation: operation.clone(),
238 schedule,
239 },
240 ),
241 activation_action: activation_action.clone(),
242 animation_hint: animation_hint.into(),
243 }
244 }
245 }
246
247 impl From<schema::BlockAttributesV1Ser> for BlockAttributes {
248 fn from(value: schema::BlockAttributesV1Ser) -> Self {
249 let schema::BlockAttributesV1Ser {
250 display_name,
251 selectable,
252 inventory,
253 ambient_sound,
254 rotation_rule,
255 placement_action,
256 tick_action,
257 activation_action,
258 animation_hint,
259 } = value;
260 Self {
261 display_name,
262 selectable,
263 inventory: inventory.into(),
264 ambient_sound: ambient_sound.into(),
265 rotation_rule: rotation_rule.into(),
266 placement_action: placement_action.map(
267 |schema::PlacementActionSer {
268 operation,
269 in_front,
270 }| PlacementAction {
271 operation,
272 in_front,
273 },
274 ),
275 tick_action: tick_action.map(
276 |schema::TickActionSer {
277 operation,
278 schedule,
279 }| {
280 TickAction {
281 operation,
282 schedule,
283 }
284 },
285 ),
286 activation_action,
287 animation_hint: animation_hint.into(),
288 }
289 }
290 }
291
292 impl From<BlockCollision> for schema::BlockCollisionSer {
293 fn from(value: BlockCollision) -> Self {
294 use schema::BlockCollisionSer as S;
295 match value {
296 BlockCollision::None => S::NoneV1,
297 BlockCollision::Hard => S::HardV1,
298 }
299 }
300 }
301
302 impl From<schema::BlockCollisionSer> for BlockCollision {
303 fn from(value: schema::BlockCollisionSer) -> Self {
304 use schema::BlockCollisionSer as S;
305 match value {
306 S::NoneV1 => BlockCollision::None,
307 S::HardV1 => BlockCollision::Hard,
308 }
309 }
310 }
311
312 impl From<RotationPlacementRule> for schema::RotationPlacementRuleSer {
313 fn from(value: RotationPlacementRule) -> Self {
314 use schema::RotationPlacementRuleSer as S;
315 match value {
316 RotationPlacementRule::Never => S::NeverV1,
317 RotationPlacementRule::Attach { by } => S::AttachV1 { by },
318 }
319 }
320 }
321
322 impl From<schema::RotationPlacementRuleSer> for RotationPlacementRule {
323 fn from(value: schema::RotationPlacementRuleSer) -> Self {
324 use schema::RotationPlacementRuleSer as S;
325 match value {
326 S::NeverV1 => RotationPlacementRule::Never,
327 S::AttachV1 { by } => RotationPlacementRule::Attach { by },
328 }
329 }
330 }
331
332 impl From<AnimationHint> for schema::AnimationHintSer {
333 fn from(value: AnimationHint) -> Self {
334 let AnimationHint {
335 redefinition,
336 replacement,
337 } = value;
338 schema::AnimationHintSer::AnimationHintV1 {
339 redefinition: redefinition.into(),
340 replacement: replacement.into(),
341 }
342 }
343 }
344
345 impl From<schema::AnimationHintSer> for AnimationHint {
346 fn from(value: schema::AnimationHintSer) -> Self {
347 use schema::AnimationHintSer as S;
348 match value {
349 S::AnimationHintV1 {
350 redefinition,
351 replacement,
352 } => AnimationHint {
353 redefinition: redefinition.into(),
354 replacement: replacement.into(),
355 },
356 }
357 }
358 }
359
360 impl From<AnimationChange> for schema::AnimationChangeV1Ser {
361 fn from(value: AnimationChange) -> Self {
362 use schema::AnimationChangeV1Ser as S;
363 match value {
364 AnimationChange::None => S::None,
365 AnimationChange::ColorSameCategory => S::ColorSameCategory,
366 AnimationChange::Shape => S::Shape,
367 }
368 }
369 }
370
371 impl From<schema::AnimationChangeV1Ser> for AnimationChange {
372 fn from(value: schema::AnimationChangeV1Ser) -> Self {
373 use schema::AnimationChangeV1Ser as S;
374 match value {
375 S::None => AnimationChange::None,
376 S::ColorSameCategory => AnimationChange::ColorSameCategory,
377 S::Shape => AnimationChange::Shape,
378 }
379 }
380 }
381
382 impl<'a> From<&'a Modifier> for ModifierSer<'a> {
383 fn from(value: &'a Modifier) -> Self {
384 match *value {
385 Modifier::Attributes(ref attributes) => ModifierSer::AttributesV1 {
386 attributes: (&**attributes).into(),
387 },
388 Modifier::Tag(tag::Be(ref tag)) => ModifierSer::TagV1 { tag: tag.clone() },
389 Modifier::Quote(Quote { suppress_ambient }) => {
390 ModifierSer::QuoteV1 { suppress_ambient }
391 }
392 Modifier::Rotate(rotation) => ModifierSer::RotateV1 { rotation },
393 Modifier::Composite(Composite {
394 ref source,
395 operator,
396 reverse,
397 disassemblable,
398 }) => ModifierSer::CompositeV1 {
399 source: source.clone(),
400 operator,
401 reverse,
402 disassemblable,
403 },
404 Modifier::Zoom(ref m) => m.to_serial_schema(),
405 Modifier::Move(ref m) => ModifierSer::Move(m.into()),
406 Modifier::Inventory(ref inventory) => ModifierSer::BlockInventoryV1 {
407 inventory: Cow::Borrowed(inventory),
408 },
409 }
410 }
411 }
412
413 impl From<ModifierSer<'_>> for Modifier {
414 fn from(value: ModifierSer<'_>) -> Self {
415 match value {
416 ModifierSer::AttributesV1 { attributes } => {
417 Modifier::Attributes(Arc::new(attributes.into()))
418 }
419 ModifierSer::TagV1 { tag } => Modifier::Tag(tag::Be(tag)),
420 ModifierSer::QuoteV1 { suppress_ambient } => {
421 Modifier::Quote(Quote { suppress_ambient })
422 }
423 ModifierSer::RotateV1 { rotation } => Modifier::Rotate(rotation),
424 ModifierSer::CompositeV1 {
425 source,
426 operator,
427 reverse,
428 disassemblable,
429 } => Modifier::Composite(Composite {
430 source,
431 operator,
432 reverse,
433 disassemblable,
434 }),
435 ModifierSer::ZoomV1 { scale, offset } => {
436 Modifier::Zoom(Zoom::new(scale, offset.map(i32::from).into()))
437 }
438 ModifierSer::Move(m) => Modifier::Move(m.into()),
439 ModifierSer::BlockInventoryV1 { inventory } => {
440 Modifier::Inventory(inventory.into_owned())
441 }
442 }
443 }
444 }
445
446 impl From<&Move> for schema::MoveSer {
447 fn from(value: &Move) -> Self {
448 let &Move {
449 direction,
450 distance,
451 velocity,
452 schedule,
453 } = value;
454
455 schema::MoveSer::MoveV1 {
456 direction,
457 distance,
458 velocity,
459 schedule,
460 }
461 }
462 }
463
464 impl From<schema::MoveSer> for Move {
465 fn from(value: schema::MoveSer) -> Self {
466 match value {
467 schema::MoveSer::MoveV1 {
468 direction,
469 distance,
470 velocity,
471 schedule,
472 } => Move {
473 direction,
474 distance,
475 velocity,
476 schedule,
477 },
478 }
479 }
480 }
481
482 impl From<&text::Font> for schema::FontSer {
485 fn from(value: &text::Font) -> Self {
486 match value {
487 text::Font::System16 => schema::FontSer::System16V1,
488 text::Font::Logo => schema::FontSer::LogoV1,
489 text::Font::SmallerBodyText => schema::FontSer::UnstableSmallerBodyTextV1,
490 }
491 }
492 }
493
494 impl From<schema::FontSer> for text::Font {
495 fn from(value: schema::FontSer) -> Self {
496 match value {
497 schema::FontSer::System16V1 => text::Font::System16,
498 schema::FontSer::LogoV1 => text::Font::Logo,
499 schema::FontSer::UnstableSmallerBodyTextV1 => text::Font::SmallerBodyText,
500 }
501 }
502 }
503
504 impl From<text::Positioning> for schema::PositioningSerV1 {
505 fn from(value: text::Positioning) -> Self {
506 let text::Positioning { x, line_y, z } = value;
507 schema::PositioningSerV1 {
508 x: x.into(),
509 line_y: line_y.into(),
510 z: z.into(),
511 }
512 }
513 }
514
515 impl From<schema::PositioningSerV1> for text::Positioning {
516 fn from(value: schema::PositioningSerV1) -> Self {
517 let schema::PositioningSerV1 { x, line_y, z } = value;
518 text::Positioning {
519 x: x.into(),
520 line_y: line_y.into(),
521 z: z.into(),
522 }
523 }
524 }
525
526 impl From<text::PositioningX> for schema::PositioningXSer {
527 fn from(value: text::PositioningX) -> Self {
528 match value {
529 text::PositioningX::Left => schema::PositioningXSer::LeftV1,
530 text::PositioningX::Center => schema::PositioningXSer::CenterV1,
531 text::PositioningX::Right => schema::PositioningXSer::RightV1,
532 }
533 }
534 }
535
536 impl From<schema::PositioningXSer> for text::PositioningX {
537 fn from(value: schema::PositioningXSer) -> Self {
538 match value {
539 schema::PositioningXSer::LeftV1 => text::PositioningX::Left,
540 schema::PositioningXSer::CenterV1 => text::PositioningX::Center,
541 schema::PositioningXSer::RightV1 => text::PositioningX::Right,
542 }
543 }
544 }
545
546 impl From<text::PositioningY> for schema::PositioningYSer {
547 fn from(value: text::PositioningY) -> Self {
548 match value {
549 text::PositioningY::BodyTop => schema::PositioningYSer::BodyTopV1,
550 text::PositioningY::BodyMiddle => schema::PositioningYSer::BodyMiddleV1,
551 text::PositioningY::Baseline => schema::PositioningYSer::BaselineV1,
552 text::PositioningY::BodyBottom => schema::PositioningYSer::BodyBottomV1,
553 }
554 }
555 }
556
557 impl From<schema::PositioningYSer> for text::PositioningY {
558 fn from(value: schema::PositioningYSer) -> Self {
559 match value {
560 schema::PositioningYSer::BodyTopV1 => text::PositioningY::BodyTop,
561 schema::PositioningYSer::BodyMiddleV1 => text::PositioningY::BodyMiddle,
562 schema::PositioningYSer::BaselineV1 => text::PositioningY::Baseline,
563 schema::PositioningYSer::BodyBottomV1 => text::PositioningY::BodyBottom,
564 }
565 }
566 }
567
568 impl From<text::PositioningZ> for schema::PositioningZSer {
569 fn from(value: text::PositioningZ) -> Self {
570 match value {
571 text::PositioningZ::Front => schema::PositioningZSer::FrontV1,
572 text::PositioningZ::Back => schema::PositioningZSer::BackV1,
573 }
574 }
575 }
576
577 impl From<schema::PositioningZSer> for text::PositioningZ {
578 fn from(value: schema::PositioningZSer) -> Self {
579 match value {
580 schema::PositioningZSer::FrontV1 => text::PositioningZ::Front,
581 schema::PositioningZSer::BackV1 => text::PositioningZ::Back,
582 }
583 }
584 }
585}
586
587mod inv {
591 use super::*;
592 use crate::inv::{self, Inventory, Slot, Tool};
593
594 impl Serialize for Inventory {
595 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
596 where
597 S: Serializer,
598 {
599 schema::InventorySer::InventoryV1 {
600 slots: self.slots.iter().map(|slot| slot.into()).collect(),
601 }
602 .serialize(serializer)
603 }
604 }
605
606 impl<'de> Deserialize<'de> for Inventory {
607 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
608 where
609 D: Deserializer<'de>,
610 {
611 match schema::InventorySer::deserialize(deserializer)? {
612 schema::InventorySer::InventoryV1 { slots } => Ok(Inventory {
613 slots: slots.into_iter().map(|s| s.into()).collect(),
614 }),
615 }
616 }
617 }
618
619 impl From<Option<schema::InvStackSer>> for Slot {
620 fn from(slot: Option<schema::InvStackSer>) -> Self {
621 match slot {
622 Some(schema::InvStackSer { count, item }) => Slot::Stack(count, item),
623 None => Slot::Empty,
624 }
625 }
626 }
627
628 impl From<&Slot> for Option<schema::InvStackSer> {
629 fn from(slot: &Slot) -> Self {
630 match *slot {
631 Slot::Empty => None,
632 Slot::Stack(count, ref item) => Some(schema::InvStackSer {
633 count,
634 item: item.clone(),
635 }),
636 }
637 }
638 }
639
640 impl Serialize for Tool {
641 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
642 where
643 S: Serializer,
644 {
645 match *self {
646 Tool::Activate => schema::ToolSer::ActivateV1 {},
647 Tool::RemoveBlock { keep } => schema::ToolSer::RemoveBlockV1 { keep },
648 Tool::Block(ref block) => schema::ToolSer::BlockV1 {
649 block: block.clone(),
650 },
651 Tool::InfiniteBlocks(ref block) => schema::ToolSer::InfiniteBlocksV1 {
652 block: block.clone(),
653 },
654 Tool::CopyFromSpace => schema::ToolSer::CopyFromSpaceV1 {},
655 Tool::EditBlock => schema::ToolSer::EditBlockV1 {},
656 Tool::PushPull => schema::ToolSer::PushPullV1 {},
657 Tool::Jetpack { active } => schema::ToolSer::JetpackV1 { active },
658 Tool::Custom { ref op, ref icon } => schema::ToolSer::CustomV1 {
659 op: op.clone(),
660 icon: icon.clone(),
661 },
662 }
663 .serialize(serializer)
664 }
665 }
666
667 impl<'de> Deserialize<'de> for Tool {
668 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
669 where
670 D: Deserializer<'de>,
671 {
672 Ok(match schema::ToolSer::deserialize(deserializer)? {
673 schema::ToolSer::ActivateV1 {} => Tool::Activate,
674 schema::ToolSer::RemoveBlockV1 { keep } => Tool::RemoveBlock { keep },
675 schema::ToolSer::BlockV1 { block } => Tool::Block(block),
676 schema::ToolSer::InfiniteBlocksV1 { block } => Tool::InfiniteBlocks(block),
677 schema::ToolSer::CopyFromSpaceV1 {} => Tool::CopyFromSpace,
678 schema::ToolSer::EditBlockV1 {} => Tool::EditBlock,
679 schema::ToolSer::PushPullV1 {} => Tool::PushPull,
680 schema::ToolSer::JetpackV1 { active } => Tool::Jetpack { active },
681 schema::ToolSer::CustomV1 { op, icon } => Tool::Custom { op, icon },
682 })
683 }
684 }
685
686 impl From<&inv::InvInBlock> for schema::InvInBlockSer {
687 fn from(value: &inv::InvInBlock) -> Self {
688 let inv::InvInBlock {
689 size,
690 icon_scale,
691 icon_resolution,
692 ref icon_rows,
693 } = *value;
694 schema::InvInBlockSer::InvInBlockV1 {
695 size,
696 icon_scale,
697 icon_resolution,
698 icon_rows: icon_rows.iter().map(schema::IconRowSerV1::from).collect(),
699 }
700 }
701 }
702
703 impl From<schema::InvInBlockSer> for inv::InvInBlock {
704 fn from(value: schema::InvInBlockSer) -> Self {
705 match value {
706 schema::InvInBlockSer::InvInBlockV1 {
707 size,
708 icon_scale,
709 icon_resolution,
710 icon_rows,
711 } => inv::InvInBlock {
712 size,
713 icon_scale,
714 icon_resolution,
715 icon_rows: icon_rows.into_iter().map(inv::IconRow::from).collect(),
716 },
717 }
718 }
719 }
720
721 impl From<&inv::IconRow> for schema::IconRowSerV1 {
722 fn from(value: &inv::IconRow) -> Self {
723 let inv::IconRow {
724 first_slot,
725 count,
726 origin,
727 stride,
728 } = *value;
729 schema::IconRowSerV1 {
730 first_slot,
731 count,
732 origin: origin.into(),
733 stride: stride.into(),
734 }
735 }
736 }
737
738 impl From<schema::IconRowSerV1> for inv::IconRow {
739 fn from(value: schema::IconRowSerV1) -> Self {
740 let schema::IconRowSerV1 {
741 first_slot,
742 count,
743 origin,
744 stride,
745 } = value;
746 inv::IconRow {
747 first_slot,
748 count,
749 origin: origin.into(),
750 stride: stride.into(),
751 }
752 }
753 }
754}
755
756mod op {
757 use super::*;
758 use crate::op;
759
760 impl Serialize for op::Operation {
761 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
762 where
763 S: Serializer,
764 {
765 match self {
766 op::Operation::Alt(ops) => schema::OperationSer::AltV1 {
767 ops: Cow::Borrowed(&ops[..]),
768 },
769 op::Operation::Become(block) => schema::OperationSer::BecomeV1 {
770 block: block.clone(),
771 },
772 op::Operation::DestroyTo(block) => schema::OperationSer::DestroyToV1 {
773 block: block.clone(),
774 },
775 &op::Operation::Replace {
776 ref old,
777 ref new,
778 conserved,
779 optional,
780 } => schema::OperationSer::ReplaceV1 {
781 old: old.clone(),
782 new: new.clone(),
783 conserved,
784 optional,
785 },
786
787 op::Operation::AddModifiers(modifiers) => schema::OperationSer::AddModifiersV1 {
788 modifiers: modifiers.iter().map(schema::ModifierSer::from).collect(),
789 },
790 op::Operation::StartMove(m) => {
791 schema::OperationSer::StartMoveV1 { modifier: m.into() }
792 }
793 &op::Operation::MoveInventory {
794 transfer_into_adjacent,
795 } => schema::OperationSer::MoveInventoryV1 {
796 transfer_into_adjacent,
797 },
798 &op::Operation::TakeInventory { destroy_if_empty } => {
799 schema::OperationSer::TakeInventoryV1 { destroy_if_empty }
800 }
801 op::Operation::Neighbors(neighbors) => schema::OperationSer::NeighborsV1 {
802 neighbors: Cow::Owned(
804 neighbors
805 .iter()
806 .map(|&(offset, ref op)| (offset.into(), op.clone()))
807 .collect(),
808 ),
809 },
810 }
811 .serialize(serializer)
812 }
813 }
814
815 impl<'de> Deserialize<'de> for op::Operation {
816 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
817 where
818 D: Deserializer<'de>,
819 {
820 Ok(match schema::OperationSer::deserialize(deserializer)? {
821 schema::OperationSer::AltV1 { ops } => op::Operation::Alt(ops.into()),
822 schema::OperationSer::BecomeV1 { block } => op::Operation::Become(block),
823 schema::OperationSer::DestroyToV1 { block } => op::Operation::DestroyTo(block),
824 schema::OperationSer::ReplaceV1 {
825 old,
826 new,
827 conserved,
828 optional,
829 } => op::Operation::Replace {
830 old,
831 new,
832 conserved,
833 optional,
834 },
835
836 schema::OperationSer::AddModifiersV1 { modifiers } => op::Operation::AddModifiers(
837 cow_into_iter(modifiers).map(crate::block::Modifier::from).collect(),
838 ),
839 schema::OperationSer::StartMoveV1 { modifier: m } => {
840 op::Operation::StartMove(m.into())
841 }
842 schema::OperationSer::MoveInventoryV1 {
843 transfer_into_adjacent,
844 } => op::Operation::MoveInventory {
845 transfer_into_adjacent,
846 },
847 schema::OperationSer::TakeInventoryV1 { destroy_if_empty } => {
848 op::Operation::TakeInventory { destroy_if_empty }
849 }
850 schema::OperationSer::NeighborsV1 { neighbors } => op::Operation::Neighbors(
851 cow_into_iter(neighbors).map(|(offset, op)| (offset.into(), op)).collect(),
852 ),
853 })
854 }
855 }
856}
857
858mod sound {
859 use super::*;
860 use crate::sound;
861
862 impl Serialize for sound::SoundDef {
863 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
864 where
865 S: Serializer,
866 {
867 schema::SoundDefSer::SynthesizedSoundV1 {
868 duration: self.duration,
869 frequency: self.frequency,
870 amplitude: self.amplitude,
871 }
872 .serialize(serializer)
873 }
874 }
875
876 impl<'de> Deserialize<'de> for sound::SoundDef {
877 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
878 where
879 D: Deserializer<'de>,
880 {
881 match schema::SoundDefSer::deserialize(deserializer)? {
882 schema::SoundDefSer::SynthesizedSoundV1 {
883 duration,
884 frequency,
885 amplitude,
886 } => Ok(sound::SoundDef {
887 duration,
888 frequency,
889 amplitude,
890 }),
891 }
892 }
893 }
894
895 impl From<&sound::Ambient> for schema::AmbientSoundSer {
896 fn from(value: &sound::Ambient) -> Self {
897 let &sound::Ambient {
898 noise_bands: sound::Spectrum(noise_bands),
899 } = value;
900 Self::AmbientSoundV1 { noise_bands }
901 }
902 }
903
904 impl From<schema::AmbientSoundSer> for sound::Ambient {
905 fn from(value: schema::AmbientSoundSer) -> Self {
906 match value {
907 schema::AmbientSoundSer::AmbientSoundV1 { noise_bands } => Self {
908 noise_bands: sound::Spectrum(noise_bands),
909 },
910 }
911 }
912 }
913}
914
915mod space {
916 use super::*;
917 use crate::math::{Rgb, Vol};
918 use crate::save::compress::{GzSerde, Leu16};
919 use crate::space::{self, LightPhysics, Sky, Space, SpacePhysics};
920
921 impl Serialize for Space {
922 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
923 where
924 S: Serializer,
925 {
926 self.read().serialize(serializer)
927 }
928 }
929
930 impl Serialize for space::Read<'_> {
931 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
932 where
933 S: Serializer,
934 {
935 schema::SpaceSer::SpaceV1 {
936 bounds: self.bounds(),
937 physics: self.physics().into(),
938 blocks: self.block_data().iter().map(|bd| bd.block().clone()).collect(),
939 contents: GzSerde(Cow::Owned(
940 self.extract(self.bounds(), |e| Leu16::from(e.block_index())).into_elements(),
941 )),
942 light: if matches!(self.physics().light, LightPhysics::None) {
943 None
944 } else {
945 Some(GzSerde(Cow::Owned(
946 self.extract(self.bounds(), |e| {
947 let mut light = schema::LightSerV1::from(e.light());
948 if self.in_light_update_queue(e.cube()) {
949 light.status = schema::LightStatusSerV1::Uninitialized
950 }
951 light
952 })
953 .into_elements(),
954 )))
955 },
956 behaviors: Cow::Borrowed(self.behaviors()),
957 spawn: Cow::Borrowed(self.spawn()),
958 }
959 .serialize(serializer)
960 }
961 }
962
963 impl<'de> Deserialize<'de> for Space {
964 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
965 where
966 D: Deserializer<'de>,
967 {
968 match schema::SpaceSer::deserialize(deserializer)? {
969 schema::SpaceSer::SpaceV1 {
970 bounds,
971 physics,
972 blocks,
973 contents: GzSerde(contents),
974 light,
975 behaviors,
976 spawn,
977 } => {
978 let contents = Vol::from_elements(
980 bounds,
981 Vec::from(contents)
982 .into_iter()
983 .map(space::BlockIndex::from)
984 .collect::<Box<[_]>>(),
985 )
986 .map_err(serde::de::Error::custom)?;
987 let light = light
988 .map(|GzSerde(data)| {
989 Vol::from_elements(
990 bounds,
991 Vec::from(data)
992 .into_iter()
993 .map(space::PackedLight::from)
994 .collect::<Box<[_]>>(),
995 )
996 })
997 .transpose()
998 .map_err(serde::de::Error::custom)?;
999
1000 let space = Space::builder(bounds)
1001 .physics(physics.into())
1002 .palette_and_contents(blocks, contents, light)
1004 .map_err(serde::de::Error::custom)?
1005 .behaviors(behaviors.into_owned())
1006 .spawn(spawn.into_owned())
1007 .build();
1008
1009 Ok(space)
1010 }
1011 }
1012 }
1013 }
1014
1015 impl From<&SpacePhysics> for schema::SpacePhysicsSerV1 {
1016 fn from(value: &SpacePhysics) -> Self {
1017 let &SpacePhysics {
1018 gravity,
1019 ref sky,
1020 ref light,
1021 } = value;
1022 Self {
1023 gravity: gravity.into(),
1024 sky: sky.into(),
1025 light: light.into(),
1026 }
1027 }
1028 }
1029
1030 impl From<schema::SpacePhysicsSerV1> for SpacePhysics {
1031 fn from(value: schema::SpacePhysicsSerV1) -> Self {
1032 let schema::SpacePhysicsSerV1 {
1033 gravity,
1034 sky,
1035 light,
1036 } = value;
1037 Self {
1038 gravity: gravity.into(),
1039 sky: sky.into(),
1040 light: light.into(),
1041 }
1042 }
1043 }
1044
1045 impl From<&Sky> for schema::SkySer {
1046 fn from(value: &Sky) -> Self {
1047 use schema::SkySer as S;
1048 match value {
1049 &Sky::Uniform(color) => S::UniformV1 {
1050 color: color.into(),
1051 },
1052 &Sky::Octants(colors) => S::OctantsV1 {
1053 colors: colors.map(Rgb::into),
1054 },
1055 }
1056 }
1057 }
1058
1059 impl From<schema::SkySer> for Sky {
1060 fn from(value: schema::SkySer) -> Self {
1061 use schema::SkySer as S;
1062 match value {
1063 S::UniformV1 { color } => Sky::Uniform(color.into()),
1064 S::OctantsV1 { colors } => Sky::Octants(colors.map(Rgb::from)),
1065 }
1066 }
1067 }
1068
1069 impl From<&LightPhysics> for schema::LightPhysicsSerV1 {
1070 fn from(value: &LightPhysics) -> Self {
1071 use schema::LightPhysicsSerV1 as S;
1072 match value {
1073 LightPhysics::None => S::NoneV1,
1074 &LightPhysics::Rays { maximum_distance } => S::RaysV1 { maximum_distance },
1075 }
1076 }
1077 }
1078
1079 impl From<schema::LightPhysicsSerV1> for LightPhysics {
1080 fn from(value: schema::LightPhysicsSerV1) -> Self {
1081 use schema::LightPhysicsSerV1 as S;
1082 match value {
1083 S::NoneV1 => LightPhysics::None,
1084 S::RaysV1 { maximum_distance } => LightPhysics::Rays { maximum_distance },
1085 }
1086 }
1087 }
1088}
1089
1090mod tag {
1091 use super::*;
1092 use crate::tag::{Tag, TagDef};
1093
1094 impl Serialize for Tag {
1095 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1096 where
1097 S: Serializer,
1098 {
1099 match self {
1100 Tag::Handle(handle) => schema::TagSer::TagHandleV1 {
1101 handle: handle.clone(),
1102 },
1103 }
1104 .serialize(serializer)
1105 }
1106 }
1107
1108 impl<'de> Deserialize<'de> for Tag {
1109 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1110 where
1111 D: Deserializer<'de>,
1112 {
1113 match schema::TagSer::deserialize(deserializer)? {
1114 schema::TagSer::TagHandleV1 { handle } => Ok(Tag::Handle(handle)),
1115 }
1116 }
1117 }
1118 impl Serialize for TagDef {
1119 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1120 where
1121 S: Serializer,
1122 {
1123 let TagDef = *self;
1124 schema::TagDefSer::TagDefV1 {}.serialize(serializer)
1125 }
1126 }
1127
1128 impl<'de> Deserialize<'de> for TagDef {
1129 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1130 where
1131 D: Deserializer<'de>,
1132 {
1133 match schema::TagDefSer::deserialize(deserializer)? {
1134 schema::TagDefSer::TagDefV1 {} => Ok(TagDef),
1135 }
1136 }
1137 }
1138}
1139
1140mod time {
1141 use super::*;
1142 use crate::time;
1143
1144 impl Serialize for time::Schedule {
1145 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1146 schema::ScheduleSer::ScheduleV1 {
1147 period: self.to_period().unwrap(),
1148 }
1149 .serialize(serializer)
1150 }
1151 }
1152
1153 impl<'de> Deserialize<'de> for time::Schedule {
1154 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1155 where
1156 D: Deserializer<'de>,
1157 {
1158 Ok(match schema::ScheduleSer::deserialize(deserializer)? {
1159 schema::ScheduleSer::ScheduleV1 { period } => time::Schedule::from_period(period),
1160 })
1161 }
1162 }
1163}
1164
1165mod universe {
1166 use super::*;
1167 use crate::block::BlockDef;
1168 use crate::save::schema::MemberEntrySer;
1169 use crate::time;
1170 use crate::universe::{
1171 self, AnyHandle, Handle, HandleSet, Name, PartialUniverse, Universe, tl,
1172 };
1173 use schema::{HandleSer, MemberDe, NameSer};
1174
1175 impl From<&BlockDef> for schema::MemberSer<'_> {
1176 fn from(block_def: &BlockDef) -> Self {
1177 schema::MemberSer::Block {
1178 value: block_def.block().clone(),
1179 }
1180 }
1181 }
1182
1183 impl Serialize for PartialUniverse<'_> {
1184 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1185 let &Self {
1186 read_ticket,
1187 ref handles,
1188 } = self;
1189
1190 let members = handles
1191 .iter()
1192 .map(|handle: &AnyHandle| match handle {
1193 AnyHandle::BlockDef(member_handle) => {
1194 let name = member_handle.name();
1195 let member_repr = schema::MemberSer::from(
1196 member_handle.read(read_ticket).map_err(|e| {
1197 serde::ser::Error::custom(format!(
1198 "Failed to read universe member {name}: {e}"
1199 ))
1200 })?,
1201 );
1202 Ok(MemberEntrySer {
1203 name: member_handle.name(),
1204 value: member_repr,
1205 })
1206 }
1207 AnyHandle::Character(member_handle) => Ok(MemberEntrySer {
1208 name: member_handle.name(),
1209 value: schema::MemberSer::Character {
1210 value: schema::SerializeHandle(read_ticket, member_handle.clone()),
1211 },
1212 }),
1213 AnyHandle::SoundDef(member_handle) => Ok(MemberEntrySer {
1214 name: member_handle.name(),
1215 value: schema::MemberSer::Sound {
1216 value: schema::SerializeHandle(read_ticket, member_handle.clone()),
1217 },
1218 }),
1219 AnyHandle::Space(member_handle) => Ok(MemberEntrySer {
1220 name: member_handle.name(),
1221 value: schema::MemberSer::Space {
1222 value: schema::SerializeHandle(read_ticket, member_handle.clone()),
1223 },
1224 }),
1225 AnyHandle::TagDef(member_handle) => Ok(MemberEntrySer {
1226 name: member_handle.name(),
1227 value: schema::MemberSer::Tag {
1228 value: schema::SerializeHandle(read_ticket, member_handle.clone()),
1229 },
1230 }),
1231 })
1232 .collect::<Result<Vec<MemberEntrySer<schema::MemberSer<'_>>>, S::Error>>()?;
1233 schema::UniverseSer::UniverseV1 { members }.serialize(serializer)
1234 }
1235 }
1236
1237 impl Serialize for Universe {
1238 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1239 PartialUniverse {
1241 handles: HandleSet::all_of(self),
1242 read_ticket: self.read_ticket(),
1243 }
1244 .serialize(serializer)
1245 }
1246 }
1247
1248 impl<'de> Deserialize<'de> for Universe {
1249 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1250 let (data, mut universe) = {
1251 let scope = tl::Scope::install(tl::Context {
1252 purpose: tl::Purpose::Deserialization,
1253 universe: Universe::new(),
1254 });
1255 let data = schema::UniverseDe::deserialize(deserializer)?;
1256 let universe = scope.take(tl::Purpose::Deserialization).universe;
1257 (data, universe)
1258 };
1259
1260 match data {
1261 schema::UniverseDe::UniverseV1 { members } => {
1262 for MemberEntrySer { name, value } in members {
1263 match value {
1264 MemberDe::Block { value: block } => universe.insert_deserialized(
1265 name,
1266 Box::new(BlockDef::new(universe.read_ticket(), block)),
1267 ),
1268 MemberDe::Character { value } => {
1269 universe.insert_deserialized(name, value)
1270 }
1271 MemberDe::Sound { value } => universe.insert_deserialized(name, value),
1272 MemberDe::Space { value } => universe.insert_deserialized(name, value),
1273 MemberDe::Tag { value } => universe.insert_deserialized(name, value),
1274 }
1275 .map_err(serde::de::Error::custom)?;
1276 }
1277 }
1278 }
1279
1280 universe.validate_deserialized_members().map_err(serde::de::Error::custom)?;
1281
1282 universe.step(true, time::Deadline::Asap);
1286
1287 Ok(*universe)
1288 }
1289 }
1290
1291 impl<T: 'static> Serialize for Handle<T> {
1292 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1293 HandleSer::HandleV1 { name: self.name() }.serialize(serializer)
1294 }
1295 }
1296
1297 impl<'de, T: universe::UniverseMember + 'static> Deserialize<'de> for Handle<T> {
1298 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1299 match HandleSer::deserialize(deserializer)? {
1300 HandleSer::HandleV1 { name } => {
1301 match tl::get_from_context(tl::Purpose::Deserialization, {
1302 let name = name.clone();
1303 |context| context.universe.get_or_insert_deserializing(name)
1304 }) {
1305 Some(result) => result.map_err(serde::de::Error::custom),
1306 None => {
1307 Ok(Handle::new_gone(name))
1311 }
1312 }
1313 }
1314 }
1315 }
1316 }
1317
1318 impl Serialize for Name {
1319 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1320 match self {
1321 Name::Specific(s) => NameSer::Specific(s.clone()),
1322 &Name::Anonym(number) => NameSer::Anonym(number),
1323 Name::Pending => {
1324 return Err(serde::ser::Error::custom(
1325 "cannot serialize a pending Handle",
1326 ));
1327 }
1328 }
1329 .serialize(serializer)
1330 }
1331 }
1332
1333 impl<'de> Deserialize<'de> for Name {
1334 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1335 Ok(match NameSer::deserialize(deserializer)? {
1336 NameSer::Specific(s) => Name::Specific(s),
1337 NameSer::Anonym(n) => Name::Anonym(n),
1338 })
1339 }
1340 }
1341
1342 impl<'ticket, T> Serialize for schema::SerializeHandle<'ticket, T>
1343 where
1344 T: universe::UniverseMember<Read<'ticket>: Serialize>,
1346 {
1347 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1348 where
1349 S: Serializer,
1350 {
1351 let &schema::SerializeHandle(read_ticket, ref handle) = self;
1352 let read = handle.read(read_ticket).map_err(|e| {
1353 serde::ser::Error::custom(format!(
1354 "Failed to read universe member {name}: {e}",
1355 name = handle.name()
1356 ))
1357 })?;
1358 read.serialize(serializer)
1359 }
1360 }
1361}
1362
1363fn cow_into_iter<T: Clone>(cow: Cow<'_, [T]>) -> impl Iterator<Item = T> + '_ {
1369 use itertools::Either;
1370 match cow {
1371 Cow::Borrowed(slice) => Either::Left(slice.iter().cloned()),
1372 Cow::Owned(vec) => Either::Right(vec.into_iter()),
1373 }
1374}