changed return value from `String` to `Box<str>` which is smaller and makes more sense since we don't mutate packet data String — 24 bytes (ptr + len + capacity) Box<str> — 16 bytes (ptr + len)
296 lines
8.2 KiB
Rust
296 lines
8.2 KiB
Rust
use std::{
|
|
pin::Pin,
|
|
sync::{
|
|
Arc,
|
|
atomic::{AtomicBool, AtomicI8, Ordering},
|
|
},
|
|
};
|
|
|
|
use super::BlockEntity;
|
|
use pumpkin_nbt::{compound::NbtCompound, tag::NbtTag};
|
|
use pumpkin_util::math::position::BlockPos;
|
|
use tokio::sync::Mutex;
|
|
|
|
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
|
|
#[repr(i8)]
|
|
pub enum DyeColor {
|
|
White = 0,
|
|
Orange = 1,
|
|
Magenta = 2,
|
|
LightBlue = 3,
|
|
Yellow = 4,
|
|
Lime = 5,
|
|
Pink = 6,
|
|
Gray = 7,
|
|
LightGray = 8,
|
|
Cyan = 9,
|
|
Purple = 10,
|
|
Blue = 11,
|
|
Brown = 12,
|
|
Green = 13,
|
|
Red = 14,
|
|
#[default]
|
|
Black = 15,
|
|
}
|
|
|
|
impl From<DyeColor> for String {
|
|
fn from(value: DyeColor) -> Self {
|
|
match value {
|
|
DyeColor::White => "white".to_string(),
|
|
DyeColor::Orange => "orange".to_string(),
|
|
DyeColor::Magenta => "magenta".to_string(),
|
|
DyeColor::LightBlue => "light_blue".to_string(),
|
|
DyeColor::Yellow => "yellow".to_string(),
|
|
DyeColor::Lime => "lime".to_string(),
|
|
DyeColor::Pink => "pink".to_string(),
|
|
DyeColor::Gray => "gray".to_string(),
|
|
DyeColor::LightGray => "light_gray".to_string(),
|
|
DyeColor::Cyan => "cyan".to_string(),
|
|
DyeColor::Purple => "purple".to_string(),
|
|
DyeColor::Blue => "blue".to_string(),
|
|
DyeColor::Brown => "brown".to_string(),
|
|
DyeColor::Green => "green".to_string(),
|
|
DyeColor::Red => "red".to_string(),
|
|
DyeColor::Black => "black".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&str> for DyeColor {
|
|
fn from(s: &str) -> Self {
|
|
match s {
|
|
"white" => Self::White,
|
|
"orange" => Self::Orange,
|
|
"magenta" => Self::Magenta,
|
|
"light_blue" => Self::LightBlue,
|
|
"yellow" => Self::Yellow,
|
|
"lime" => Self::Lime,
|
|
"pink" => Self::Pink,
|
|
"gray" => Self::Gray,
|
|
"light_gray" => Self::LightGray,
|
|
"cyan" => Self::Cyan,
|
|
"purple" => Self::Purple,
|
|
"blue" => Self::Blue,
|
|
"brown" => Self::Brown,
|
|
"green" => Self::Green,
|
|
"red" => Self::Red,
|
|
"black" => Self::Black,
|
|
_ => Self::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<i8> for DyeColor {
|
|
fn from(s: i8) -> Self {
|
|
match s {
|
|
0 => Self::White,
|
|
1 => Self::Orange,
|
|
2 => Self::Magenta,
|
|
3 => Self::LightBlue,
|
|
4 => Self::Yellow,
|
|
5 => Self::Lime,
|
|
6 => Self::Pink,
|
|
7 => Self::Gray,
|
|
8 => Self::LightGray,
|
|
9 => Self::Cyan,
|
|
10 => Self::Purple,
|
|
11 => Self::Blue,
|
|
12 => Self::Brown,
|
|
13 => Self::Green,
|
|
14 => Self::Red,
|
|
15 => Self::Black,
|
|
_ => Self::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<DyeColor> for NbtTag {
|
|
fn from(value: DyeColor) -> Self {
|
|
Self::Byte(value as i8)
|
|
}
|
|
}
|
|
|
|
pub struct SignBlockEntity {
|
|
pub front_text: Text,
|
|
pub back_text: Text,
|
|
pub is_waxed: AtomicBool,
|
|
position: BlockPos,
|
|
pub currently_editing_player: Arc<Mutex<Option<uuid::Uuid>>>,
|
|
}
|
|
|
|
pub struct Text {
|
|
pub has_glowing_text: AtomicBool,
|
|
color: AtomicI8,
|
|
pub messages: Arc<std::sync::Mutex<[Box<str>; 4]>>,
|
|
}
|
|
|
|
impl Clone for Text {
|
|
fn clone(&self) -> Self {
|
|
Self {
|
|
has_glowing_text: AtomicBool::new(self.has_glowing_text.load(Ordering::Relaxed)),
|
|
color: AtomicI8::new(self.color.load(Ordering::Relaxed)),
|
|
messages: self.messages.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for Text {
|
|
fn default() -> Self {
|
|
Self {
|
|
has_glowing_text: AtomicBool::new(false),
|
|
color: AtomicI8::new(DyeColor::default() as i8),
|
|
messages: Arc::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::fallible_impl_from)]
|
|
impl From<Text> for NbtTag {
|
|
fn from(value: Text) -> Self {
|
|
let mut nbt = NbtCompound::new();
|
|
nbt.put_bool(
|
|
"has_glowing_text",
|
|
value.has_glowing_text.load(Ordering::Relaxed),
|
|
);
|
|
nbt.put_string("color", value.get_color().into());
|
|
nbt.put_list(
|
|
"messages",
|
|
value
|
|
.messages
|
|
.lock()
|
|
.unwrap()
|
|
.iter()
|
|
.map(|s| Self::String(s.clone()))
|
|
.collect(),
|
|
);
|
|
Self::Compound(nbt)
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::fallible_impl_from)]
|
|
impl From<NbtTag> for Text {
|
|
fn from(tag: NbtTag) -> Self {
|
|
let nbt = tag.extract_compound().unwrap();
|
|
let has_glowing_text = nbt.get_bool("has_glowing_text").unwrap_or(false);
|
|
let color = nbt.get_string("color").unwrap();
|
|
let messages: Vec<Box<str>> = nbt
|
|
.get_list("messages")
|
|
.unwrap()
|
|
.iter()
|
|
.filter_map(|tag| tag.extract_string().map(Box::from))
|
|
.collect();
|
|
let get_message =
|
|
|i: usize| -> Box<str> { messages.get(i).cloned().unwrap_or_else(|| Box::from("")) };
|
|
|
|
Self {
|
|
has_glowing_text: AtomicBool::new(has_glowing_text),
|
|
color: AtomicI8::new(DyeColor::from(color) as i8),
|
|
messages: Arc::new(std::sync::Mutex::new([
|
|
get_message(0),
|
|
get_message(1),
|
|
get_message(2),
|
|
get_message(3),
|
|
])),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Text {
|
|
fn new(messages: [Box<str>; 4]) -> Self {
|
|
Self {
|
|
has_glowing_text: AtomicBool::new(false),
|
|
color: AtomicI8::new(DyeColor::default() as i8),
|
|
messages: Arc::new(std::sync::Mutex::new(messages)),
|
|
}
|
|
}
|
|
|
|
pub fn get_color(&self) -> DyeColor {
|
|
self.color.load(Ordering::Relaxed).into()
|
|
}
|
|
|
|
pub fn set_color(&self, color: DyeColor) {
|
|
self.color.store(color as i8, Ordering::Relaxed);
|
|
}
|
|
}
|
|
|
|
impl BlockEntity for SignBlockEntity {
|
|
fn resource_location(&self) -> &'static str {
|
|
Self::ID
|
|
}
|
|
|
|
fn get_position(&self) -> BlockPos {
|
|
self.position
|
|
}
|
|
|
|
fn from_nbt(nbt: &pumpkin_nbt::compound::NbtCompound, position: BlockPos) -> Self
|
|
where
|
|
Self: Sized,
|
|
{
|
|
let front_text = Text::from(nbt.get("front_text").unwrap().clone());
|
|
let back_text = Text::from(nbt.get("back_text").unwrap().clone());
|
|
let is_waxed = nbt.get_bool("is_waxed").unwrap_or(false);
|
|
Self {
|
|
position,
|
|
front_text,
|
|
back_text,
|
|
is_waxed: AtomicBool::new(is_waxed),
|
|
currently_editing_player: Arc::new(Mutex::new(None)),
|
|
}
|
|
}
|
|
|
|
fn write_nbt<'a>(
|
|
&'a self,
|
|
nbt: &'a mut NbtCompound,
|
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
|
Box::pin(async move {
|
|
nbt.put("front_text", self.front_text.clone());
|
|
nbt.put("back_text", self.back_text.clone());
|
|
nbt.put_bool("is_waxed", self.is_waxed.load(Ordering::Relaxed));
|
|
})
|
|
}
|
|
|
|
fn chunk_data_nbt(&self) -> Option<NbtCompound> {
|
|
let mut nbt = NbtCompound::new();
|
|
nbt.put("front_text", self.front_text.clone());
|
|
nbt.put("back_text", self.back_text.clone());
|
|
nbt.put_bool("is_waxed", self.is_waxed.load(Ordering::Relaxed));
|
|
Some(nbt)
|
|
}
|
|
|
|
fn as_any(&self) -> &dyn std::any::Any {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl SignBlockEntity {
|
|
pub const ID: &'static str = "minecraft:sign";
|
|
#[must_use]
|
|
pub fn new(position: BlockPos, is_front: bool, messages: [Box<str>; 4]) -> Self {
|
|
Self {
|
|
position,
|
|
is_waxed: AtomicBool::new(false),
|
|
front_text: if is_front {
|
|
Text::new(messages.clone())
|
|
} else {
|
|
Text::default()
|
|
},
|
|
back_text: if is_front {
|
|
Text::default()
|
|
} else {
|
|
Text::new(messages)
|
|
},
|
|
currently_editing_player: Arc::new(Mutex::new(None)),
|
|
}
|
|
}
|
|
#[must_use]
|
|
pub fn empty(position: BlockPos) -> Self {
|
|
Self {
|
|
position,
|
|
is_waxed: AtomicBool::new(false),
|
|
front_text: Text::default(),
|
|
back_text: Text::default(),
|
|
currently_editing_player: Arc::new(Mutex::new(None)),
|
|
}
|
|
}
|
|
}
|