From f0e0db63d39fb5731483d6c5a93af950de4433e4 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 25 Nov 2023 20:34:50 +0100 Subject: [PATCH] world: process sign data, prepare for serialization --- src/world/block_entity.rs | 89 +++++++++++++++++++++++++++++++++++++++ src/world/chunk.rs | 36 ++++++++++++---- src/world/json_text.rs | 12 ++++-- src/world/mod.rs | 1 + src/world/sign.rs | 3 ++ 5 files changed, 130 insertions(+), 11 deletions(-) create mode 100644 src/world/block_entity.rs diff --git a/src/world/block_entity.rs b/src/world/block_entity.rs new file mode 100644 index 0000000..65d95e4 --- /dev/null +++ b/src/world/block_entity.rs @@ -0,0 +1,89 @@ +//! Processing of block entity data + +use serde::{Deserialize, Serialize}; + +use super::{ + de, + sign::{BlockEntitySignExt, SignText}, +}; + +/// Kind of sign block +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum SignKind { + /// Standing or attached sign + Sign, + /// Hanging sign + HangingSign, +} + +/// Processed sign data +#[derive(Debug, Serialize, Deserialize)] +pub struct Sign { + /// The kind of the sign + pub kind: SignKind, + /// The sign's front text + #[serde(skip_serializing_if = "SignText::is_empty", default)] + pub front_text: SignText, + /// The sign's back text + #[serde(skip_serializing_if = "SignText::is_empty", default)] + pub back_text: SignText, +} + +impl Sign { + /// Processes a [de::BlockEntitySign] into a [Sign] + fn new(sign: &de::BlockEntitySign, kind: SignKind) -> Sign { + let (front_text, back_text) = sign.text(); + let front_text = front_text.decode(); + let back_text = back_text.decode(); + Sign { + kind, + front_text, + back_text, + } + } +} + +/// Data for different kinds of [BlockEntity] +#[derive(Debug, Serialize, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum BlockEntityData { + /// A sign block + Sign(Sign), +} + +/// A processed block entity +#[derive(Debug, Serialize, Deserialize)] +pub struct BlockEntity { + /// Global X coordinate + pub x: i32, + /// Global Y coordinate + pub y: i32, + /// Global Z coordinate + pub z: i32, + /// Entity data + #[serde(flatten)] + pub data: BlockEntityData, +} + +impl BlockEntity { + /// Processes a [de::BlockEntity] into a [BlockEntity] + pub fn new(entity: &de::BlockEntity) -> Option { + let data = match &entity.data { + de::BlockEntityData::Sign(sign) => { + BlockEntityData::Sign(Sign::new(sign, SignKind::Sign)) + } + de::BlockEntityData::HangingSign(sign) => { + BlockEntityData::Sign(Sign::new(sign, SignKind::HangingSign)) + } + de::BlockEntityData::Other => return None, + }; + + Some(BlockEntity { + x: entity.x, + y: entity.y, + z: entity.z, + data, + }) + } +} diff --git a/src/world/chunk.rs b/src/world/chunk.rs index e3ba58a..5a8937e 100644 --- a/src/world/chunk.rs +++ b/src/world/chunk.rs @@ -10,7 +10,7 @@ use std::{ use anyhow::{bail, Context, Result}; -use super::{de, section::*}; +use super::{block_entity::BlockEntity, de, section::*}; use crate::{ resource::{BiomeTypes, BlockTypes}, types::*, @@ -55,6 +55,8 @@ pub enum ChunkInner<'a> { pub struct Chunk<'a> { /// Version-specific data inner: ChunkInner<'a>, + /// Unprocessed block entities + block_entities: &'a Vec, } impl<'a> Chunk<'a> { @@ -66,17 +68,27 @@ impl<'a> Chunk<'a> { ) -> Result<(Self, bool)> { let data_version = data.data_version.unwrap_or_default(); - let (inner, has_unknown) = match &data.chunk { + let ((inner, has_unknown), block_entities) = match &data.chunk { de::ChunkVariant::V1_18 { sections, - block_entities: _, - } => Self::new_v1_18(data_version, sections, block_types, biome_types)?, - de::ChunkVariant::V0 { level } => { - Self::new_v0(data_version, level, block_types, biome_types)? - } + block_entities, + } => ( + Self::new_v1_18(data_version, sections, block_types, biome_types)?, + block_entities, + ), + de::ChunkVariant::V0 { level } => ( + Self::new_v0(data_version, level, block_types, biome_types)?, + &level.tile_entities, + ), }; - Ok((Chunk { inner }, has_unknown)) + Ok(( + Chunk { + inner, + block_entities, + }, + has_unknown, + )) } /// [Chunk::new] implementation for Minecraft v1.18+ chunks @@ -230,6 +242,14 @@ impl<'a> Chunk<'a> { }, } } + + /// Processes all of the chunk's block entities + pub fn block_entities(&self) -> Vec { + self.block_entities + .iter() + .filter_map(BlockEntity::new) + .collect() + } } /// Reference to block, biome and block light data of a section diff --git a/src/world/json_text.rs b/src/world/json_text.rs index 8e53ead..a9c1f13 100644 --- a/src/world/json_text.rs +++ b/src/world/json_text.rs @@ -2,7 +2,7 @@ use std::{collections::VecDeque, sync::Arc}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; /// A span of formatted text /// @@ -11,22 +11,28 @@ use serde::Deserialize; /// is handled by [DeserializedText]. /// /// Formatting that is not set in a node is inherited from the parent. -#[derive(Debug, Deserialize, Default)] +#[derive(Debug, Serialize, Deserialize, Default)] pub struct FormattedText { #[serde(default)] /// Text content pub text: String, /// Text color + #[serde(skip_serializing_if = "Option::is_none")] pub color: Option>, /// Bold formatting + #[serde(skip_serializing_if = "Option::is_none")] pub bold: Option, /// Italic formatting + #[serde(skip_serializing_if = "Option::is_none")] pub italic: Option, /// Underlines formatting + #[serde(skip_serializing_if = "Option::is_none")] pub underlined: Option, /// Strikethrough formatting + #[serde(skip_serializing_if = "Option::is_none")] pub strikethrough: Option, /// Obfuscated formatting + #[serde(skip_serializing_if = "Option::is_none")] pub obfuscated: Option, } @@ -71,7 +77,7 @@ impl From for FormattedTextTree { } /// List of [FormattedText] -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct FormattedTextList(pub Vec); impl FormattedTextList { diff --git a/src/world/mod.rs b/src/world/mod.rs index 38d5174..6426c92 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -1,5 +1,6 @@ //! Data structures describing Minecraft save data +pub mod block_entity; pub mod chunk; pub mod de; pub mod json_text; diff --git a/src/world/sign.rs b/src/world/sign.rs index c13eebe..e53be87 100644 --- a/src/world/sign.rs +++ b/src/world/sign.rs @@ -2,6 +2,8 @@ use std::sync::Arc; +use serde::{Deserialize, Serialize}; + use super::{ de, json_text::{FormattedText, FormattedTextList, JSONText}, @@ -77,6 +79,7 @@ impl BlockEntitySignExt for de::BlockEntitySign { } } +#[derive(Debug, Default, Serialize, Deserialize)] /// Deserialized and linearized sign text pub struct SignText(pub Vec);