1use std::fmt::{self, Debug, Formatter};
2use std::io::{Cursor, Error, Result, Write};
3
4use rustc_hash::FxHashMap;
5use steel_utils::codec::VarInt;
6use steel_utils::serial::{ReadFrom, WriteTo};
7use steel_utils::{BlockPos, Downcast as _, DowncastType, DowncastTypeKey, ErasedType, Identifier};
8
9use crate::{REGISTRY, RegistryExt};
10
11pub trait PositionSourceCodec:
13 DowncastType + Clone + Debug + PartialEq + Send + Sync + 'static
14{
15 fn read_network(data: &mut Cursor<&[u8]>) -> Result<Self>;
16 fn write_network(&self, writer: &mut Vec<u8>) -> Result<()>;
17}
18
19trait ErasedPositionSource: ErasedType + Debug + Send + Sync {
20 fn clone_source(&self) -> Box<dyn ErasedPositionSource>;
21 fn source_eq(&self, other: &dyn ErasedPositionSource) -> bool;
22}
23
24impl<T: PositionSourceCodec> ErasedPositionSource for T {
25 fn clone_source(&self) -> Box<dyn ErasedPositionSource> {
26 Box::new(self.clone())
27 }
28
29 fn source_eq(&self, other: &dyn ErasedPositionSource) -> bool {
30 other.downcast_ref::<T>() == Some(self)
31 }
32}
33
34type NetworkReader = fn(&mut Cursor<&[u8]>) -> Result<Box<dyn ErasedPositionSource>>;
35type NetworkWriter = fn(&dyn ErasedPositionSource, &mut Vec<u8>) -> Result<()>;
36
37pub struct PositionSourceType {
39 pub key: Identifier,
40 expected_type_key: DowncastTypeKey,
41 network_reader: NetworkReader,
42 network_writer: NetworkWriter,
43}
44
45impl PositionSourceType {
46 #[must_use]
47 pub const fn of<T: PositionSourceCodec>(key: Identifier) -> Self {
48 Self {
49 key,
50 expected_type_key: T::TYPE_KEY,
51 network_reader: read_network::<T>,
52 network_writer: write_network::<T>,
53 }
54 }
55}
56
57impl Debug for PositionSourceType {
58 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
59 formatter
60 .debug_struct("PositionSourceType")
61 .field("key", &self.key)
62 .field("expected_type_key", &self.expected_type_key)
63 .finish_non_exhaustive()
64 }
65}
66
67pub type PositionSourceTypeRef = &'static PositionSourceType;
68
69pub struct PositionSource {
71 source_type: PositionSourceTypeRef,
72 value: Box<dyn ErasedPositionSource>,
73}
74
75impl PositionSource {
76 #[must_use]
77 pub fn new<T: PositionSourceCodec>(source_type: PositionSourceTypeRef, value: T) -> Self {
78 assert_eq!(
79 source_type.expected_type_key,
80 T::TYPE_KEY,
81 "position source value does not match its registered type"
82 );
83 Self {
84 source_type,
85 value: Box::new(value),
86 }
87 }
88
89 #[must_use]
90 pub const fn source_type(&self) -> PositionSourceTypeRef {
91 self.source_type
92 }
93
94 #[must_use]
95 pub fn downcast_ref<T: DowncastType>(&self) -> Option<&T> {
96 self.value.downcast_ref::<T>()
97 }
98}
99
100impl Clone for PositionSource {
101 fn clone(&self) -> Self {
102 Self {
103 source_type: self.source_type,
104 value: self.value.clone_source(),
105 }
106 }
107}
108
109impl Debug for PositionSource {
110 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
111 formatter
112 .debug_struct("PositionSource")
113 .field("source_type", &self.source_type.key)
114 .field("value", &self.value)
115 .finish()
116 }
117}
118
119impl PartialEq for PositionSource {
120 fn eq(&self, other: &Self) -> bool {
121 self.source_type.key == other.source_type.key && self.value.source_eq(other.value.as_ref())
122 }
123}
124
125impl WriteTo for PositionSource {
126 fn write(&self, writer: &mut impl Write) -> Result<()> {
127 let (id, source_type) = REGISTRY
128 .position_source_types
129 .registered_entry_with_id(self.source_type)
130 .ok_or_else(|| {
131 Error::other(format!(
132 "Position source type is not the registered value for key: {}",
133 self.source_type.key
134 ))
135 })?;
136 let id = i32::try_from(id)
137 .map_err(|_| Error::other(format!("Position source type id out of range: {id}")))?;
138 VarInt(id).write(writer)?;
139
140 let mut payload = Vec::new();
141 (source_type.network_writer)(self.value.as_ref(), &mut payload)?;
142 writer.write_all(&payload)
143 }
144}
145
146impl ReadFrom for PositionSource {
147 fn read(data: &mut Cursor<&[u8]>) -> Result<Self> {
148 let id = VarInt::read(data)?.0;
149 let id = usize::try_from(id)
150 .map_err(|_| Error::other(format!("Negative position source type id: {id}")))?;
151 let source_type = REGISTRY
152 .position_source_types
153 .by_id(id)
154 .ok_or_else(|| Error::other(format!("Unknown position source type id: {id}")))?;
155 let value = (source_type.network_reader)(data)?;
156 Ok(Self { source_type, value })
157 }
158}
159
160pub struct PositionSourceTypeRegistry {
161 types_by_id: Vec<PositionSourceTypeRef>,
162 types_by_key: FxHashMap<Identifier, usize>,
163 allows_registering: bool,
164}
165
166impl PositionSourceTypeRegistry {
167 #[must_use]
168 pub fn new() -> Self {
169 Self {
170 types_by_id: Vec::new(),
171 types_by_key: FxHashMap::default(),
172 allows_registering: true,
173 }
174 }
175
176 fn registered_entry_with_id(
177 &self,
178 entry: PositionSourceTypeRef,
179 ) -> Option<(usize, PositionSourceTypeRef)> {
180 let id = self.types_by_key.get(&entry.key).copied()?;
181 let registered = self.types_by_id.get(id).copied()?;
182 std::ptr::eq(registered, entry).then_some((id, registered))
183 }
184}
185
186crate::impl_standard_methods!(
187 PositionSourceTypeRegistry,
188 PositionSourceTypeRef,
189 types_by_id,
190 types_by_key,
191 allows_registering,
192 "Cannot register duplicate position source type key: {}"
193);
194crate::impl_registry!(
195 PositionSourceTypeRegistry,
196 PositionSourceType,
197 types_by_id,
198 types_by_key,
199 position_source_types
200);
201
202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
203pub struct BlockPositionSource {
204 pos: BlockPos,
205}
206
207impl BlockPositionSource {
208 #[must_use]
209 pub const fn new(pos: BlockPos) -> Self {
210 Self { pos }
211 }
212
213 #[must_use]
214 pub const fn pos(&self) -> BlockPos {
215 self.pos
216 }
217}
218
219unsafe impl DowncastType for BlockPositionSource {
221 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:position_source/block");
222}
223
224impl PositionSourceCodec for BlockPositionSource {
225 fn read_network(data: &mut Cursor<&[u8]>) -> Result<Self> {
226 Ok(Self::new(BlockPos::read(data)?))
227 }
228
229 fn write_network(&self, writer: &mut Vec<u8>) -> Result<()> {
230 self.pos.write(writer)
231 }
232}
233
234#[derive(Debug, Clone, Copy, PartialEq)]
235pub struct EntityPositionSource {
236 entity_id: i32,
237 y_offset: f32,
238}
239
240impl EntityPositionSource {
241 #[must_use]
242 pub const fn new(entity_id: i32, y_offset: f32) -> Self {
243 Self {
244 entity_id,
245 y_offset,
246 }
247 }
248
249 #[must_use]
250 pub const fn entity_id(&self) -> i32 {
251 self.entity_id
252 }
253
254 #[must_use]
255 pub const fn y_offset(&self) -> f32 {
256 self.y_offset
257 }
258}
259
260unsafe impl DowncastType for EntityPositionSource {
262 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:position_source/entity");
263}
264
265impl PositionSourceCodec for EntityPositionSource {
266 fn read_network(data: &mut Cursor<&[u8]>) -> Result<Self> {
267 Ok(Self::new(VarInt::read(data)?.0, f32::read(data)?))
268 }
269
270 fn write_network(&self, writer: &mut Vec<u8>) -> Result<()> {
271 VarInt(self.entity_id).write(writer)?;
272 self.y_offset.write(writer)
273 }
274}
275
276fn read_network<T: PositionSourceCodec>(
277 data: &mut Cursor<&[u8]>,
278) -> Result<Box<dyn ErasedPositionSource>> {
279 Ok(Box::new(T::read_network(data)?))
280}
281
282fn write_network<T: PositionSourceCodec>(
283 value: &dyn ErasedPositionSource,
284 writer: &mut Vec<u8>,
285) -> Result<()> {
286 let value = value.downcast_ref::<T>().ok_or_else(|| {
287 Error::other(format!(
288 "Position source payload does not match {}",
289 T::TYPE_KEY
290 ))
291 })?;
292 value.write_network(writer)
293}
294
295#[cfg(test)]
296mod tests {
297 use steel_utils::Identifier;
298 use steel_utils::serial::WriteTo;
299
300 use crate::{init_vanilla_registry, vanilla_position_source_types};
301
302 use super::{
303 EntityPositionSource, PositionSource, PositionSourceType, PositionSourceTypeRegistry,
304 };
305
306 static FORGED_BLOCK_SOURCE: PositionSourceType =
307 PositionSourceType::of::<EntityPositionSource>(Identifier::vanilla_static("block"));
308
309 #[test]
310 fn position_source_write_rejects_noncanonical_same_key_codec() {
311 init_vanilla_registry();
312
313 let source =
314 PositionSource::new(&FORGED_BLOCK_SOURCE, EntityPositionSource::new(1234, 1.5));
315 let mut encoded = Vec::new();
316 let result = source.write(&mut encoded);
317
318 assert!(result.is_err());
319 assert_eq!(encoded.len(), 0);
320 }
321
322 #[test]
323 #[should_panic(expected = "Cannot register duplicate position source type key")]
324 fn position_source_type_registry_rejects_duplicate_keys() {
325 let mut registry = PositionSourceTypeRegistry::new();
326 registry.register(&vanilla_position_source_types::BLOCK);
327 registry.register(&FORGED_BLOCK_SOURCE);
328 }
329}