oc_wasm_opencomputers/
redstone.rs

1//! Provides high-level access to the redstone APIs.
2
3use crate::common::{Colour, COLOURS};
4use crate::error::Error;
5use crate::helpers::Ignore;
6use minicbor::{Decode, Encode};
7use oc_wasm_futures::invoke::{component_method, Buffer};
8use oc_wasm_helpers::{
9	sides::{Side, BLOCK_SIDES},
10	Lockable,
11};
12use oc_wasm_safe::{component::Invoker, Address};
13
14/// The type name for redstone components.
15pub const TYPE: &str = "redstone";
16
17/// A redstone component.
18#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
19pub struct Redstone(Address);
20
21impl Redstone {
22	/// Creates a wrapper around a redstone block or card.
23	///
24	/// The `address` parameter is the address of the component. It is not checked for correctness
25	/// at this time because network topology could change after this function returns; as such,
26	/// each usage of the value may fail instead.
27	#[must_use = "This function is only useful for its return value"]
28	pub fn new(address: Address) -> Self {
29		Self(address)
30	}
31
32	/// Returns the address of the component.
33	#[must_use = "This function is only useful for its return value"]
34	pub fn address(&self) -> &Address {
35		&self.0
36	}
37}
38
39impl<'invoker, 'buffer, B: 'buffer + Buffer> Lockable<'invoker, 'buffer, B> for Redstone {
40	type Locked = Locked<'invoker, 'buffer, B>;
41
42	fn lock(&self, invoker: &'invoker mut Invoker, buffer: &'buffer mut B) -> Self::Locked {
43		Locked {
44			address: self.0,
45			invoker,
46			buffer,
47		}
48	}
49}
50
51/// A redstone component on which methods can be invoked.
52///
53/// This type combines a redstone block or card address, an [`Invoker`] that can be used to make
54/// method calls, and a scratch buffer used to perform CBOR encoding and decoding. A value of this
55/// type can be created by calling [`Redstone::lock`], and it can be dropped to return the borrow
56/// of the invoker and buffer to the caller so they can be reused for other purposes.
57///
58/// The `'invoker` lifetime is the lifetime of the invoker. The `'buffer` lifetime is the lifetime
59/// of the buffer. The `B` type is the type of scratch buffer to use.
60pub struct Locked<'invoker, 'buffer, B: Buffer> {
61	/// The component address.
62	address: Address,
63
64	/// The invoker.
65	invoker: &'invoker mut Invoker,
66
67	/// The buffer.
68	buffer: &'buffer mut B,
69}
70
71impl<'invoker, 'buffer, B: Buffer> Locked<'invoker, 'buffer, B> {
72	/// Returns the signal strengths received on all six sides.
73	///
74	/// The returned array is indexed by side. For a redstone block, the indices should be
75	/// [absolute sides](crate::common::AbsoluteSide). For a redstone card in a computer, the
76	/// indices should be [relative sides](crate::common::RelativeSide).
77	///
78	/// # Errors
79	/// * [`BadComponent`](Error::BadComponent)
80	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
81	pub async fn get_input(&mut self) -> Result<[u8; BLOCK_SIDES], Error> {
82		self.get_vanilla("getInput").await
83	}
84
85	/// Returns the signal strengths received on a side.
86	///
87	/// For a redstone block, the `side` parameter must be an [absolute
88	/// side](crate::common::AbsoluteSide). For a redstone card in a computer, the `side` parameter
89	/// must be a [relative side](crate::common::RelativeSide).
90	///
91	/// # Errors
92	/// * [`BadComponent`](Error::BadComponent)
93	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
94	pub async fn get_side_input(&mut self, side: impl Side) -> Result<u8, Error> {
95		self.get_side_vanilla("getInput", side.into()).await
96	}
97
98	/// Returns the bundled signal strengths received on all six sides.
99	///
100	/// The returned array is indexed by side. For a redstone block, the indices should be
101	/// [absolute sides](crate::common::AbsoluteSide). For a redstone card in a computer, the
102	/// indices should be [relative sides](crate::common::RelativeSide). Each element of the outer
103	/// array is itself an array indexed by colour.
104	///
105	/// # Errors
106	/// * [`BadComponent`](Error::BadComponent)
107	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
108	pub async fn get_bundled_input(&mut self) -> Result<[[u8; COLOURS]; BLOCK_SIDES], Error> {
109		self.get_bundled("getBundledInput").await
110	}
111
112	/// Returns the bundled signal strengths received on a side.
113	///
114	/// For a redstone block, the `side` parameter must be an [absolute
115	/// side](crate::common::AbsoluteSide). For a redstone card in a computer, the `side` parameter
116	/// must be a [relative side](crate::common::RelativeSide).
117	///
118	/// The returned array is indexed by colour.
119	///
120	/// # Errors
121	/// * [`BadComponent`](Error::BadComponent)
122	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
123	pub async fn get_side_bundled_input(
124		&mut self,
125		side: impl Side,
126	) -> Result<[u8; COLOURS], Error> {
127		self.get_side_bundled("getBundledInput", side.into()).await
128	}
129
130	/// Returns the bundled signal strength received on a side on a single colour of wire.
131	///
132	/// For a redstone block, the `side` parameter must be an [absolute
133	/// side](crate::common::AbsoluteSide). For a redstone card in a computer, the `side` parameter
134	/// must be a [relative side](crate::common::RelativeSide).
135	///
136	/// # Errors
137	/// * [`BadComponent`](Error::BadComponent)
138	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
139	pub async fn get_side_colour_bundled_input(
140		&mut self,
141		side: impl Side,
142		colour: Colour,
143	) -> Result<u8, Error> {
144		self.get_side_colour_bundled("getBundledInput", side.into(), colour.into())
145			.await
146	}
147
148	/// Returns the signal strengths emitted on all six sides.
149	///
150	/// The returned array is indexed by side. For a redstone block, the indices should be
151	/// [absolute sides](crate::common::AbsoluteSide). For a redstone card in a computer, the
152	/// indices should be [relative sides](crate::common::RelativeSide).
153	///
154	/// # Errors
155	/// * [`BadComponent`](Error::BadComponent)
156	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
157	pub async fn get_output(&mut self) -> Result<[u8; BLOCK_SIDES], Error> {
158		self.get_vanilla("getOutput").await
159	}
160
161	/// Returns the signal strength emitted on a side.
162	///
163	/// For a redstone block, the `side` parameter must be an [absolute
164	/// side](crate::common::AbsoluteSide). For a redstone card in a computer, the `side` parameter
165	/// must be a [relative side](crate::common::RelativeSide).
166	///
167	/// # Errors
168	/// * [`BadComponent`](Error::BadComponent)
169	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
170	pub async fn get_side_output(&mut self, side: impl Side) -> Result<u8, Error> {
171		self.get_side_vanilla("getOutput", side.into()).await
172	}
173
174	/// Returns the bundled signal strengths emitted on all six sides.
175	///
176	/// The returned array is indexed by side. For a redstone block, the indices should be
177	/// [absolute sides](crate::common::AbsoluteSide). For a redstone card in a computer, the
178	/// indices should be [relative sides](crate::common::RelativeSide). Each element of the outer
179	/// array is itself an array indexed by colour.
180	///
181	/// # Errors
182	/// * [`BadComponent`](Error::BadComponent)
183	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
184	pub async fn get_bundled_output(&mut self) -> Result<[[u8; COLOURS]; BLOCK_SIDES], Error> {
185		self.get_bundled("getBundledOutput").await
186	}
187
188	/// Returns the bundled signal strengths emitted on a side.
189	///
190	/// For a redstone block, the `side` parameter must be an [absolute
191	/// side](crate::common::AbsoluteSide). For a redstone card in a computer, the `side` parameter
192	/// must be a [relative side](crate::common::RelativeSide).
193	///
194	/// The returned array is indexed by colour.
195	///
196	/// # Errors
197	/// * [`BadComponent`](Error::BadComponent)
198	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
199	pub async fn get_side_bundled_output(
200		&mut self,
201		side: impl Side,
202	) -> Result<[u8; COLOURS], Error> {
203		self.get_side_bundled("getBundledOutput", side.into()).await
204	}
205
206	/// Returns the bundled signal strength emitted on a side on a single colour of wire.
207	///
208	/// For a redstone block, the `side` parameter must be an [absolute
209	/// side](crate::common::AbsoluteSide). For a redstone card in a computer, the `side` parameter
210	/// must be a [relative side](crate::common::RelativeSide).
211	///
212	/// # Errors
213	/// * [`BadComponent`](Error::BadComponent)
214	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
215	pub async fn get_side_colour_bundled_output(
216		&mut self,
217		side: impl Side,
218		colour: Colour,
219	) -> Result<u8, Error> {
220		self.get_side_colour_bundled("getBundledOutput", side.into(), colour.into())
221			.await
222	}
223
224	/// Sets the signal strengths to emit on any subset of the sides.
225	///
226	/// The `levels` parameter contains an element for each side. For a redstone block, the indices
227	/// into this array are [absolute sides](crate::common::AbsoluteSide). For a redstone card in a
228	/// computer, the indices are [relative sides](crate::common::RelativeSide). Each element of
229	/// the array can be `Some` with the new signal strength to emit, or `None` to leave that side
230	/// unmodified.
231	///
232	/// The old signal levels, prior to modification, are returned.
233	///
234	/// # Errors
235	/// * [`BadComponent`](Error::BadComponent)
236	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
237	pub async fn set_output(
238		&mut self,
239		levels: &[Option<u8>; BLOCK_SIDES],
240	) -> Result<[u8; BLOCK_SIDES], Error> {
241		let ret: (ArrayAsMap<u8, BLOCK_SIDES>,) = component_method(
242			self.invoker,
243			self.buffer,
244			&self.address,
245			"setOutput",
246			Some(&(ArrayAsMap(*levels),)),
247		)
248		.await?;
249		Ok(ret.0 .0)
250	}
251
252	/// Sets the signal strength to emit on a single side.
253	///
254	/// The `side` parameter selects the side to modify. For a redstone block, the value must be an
255	/// [absolute side](crate::common::AbsoluteSide). For a redstone card in a computer, the value
256	/// must be a [relative side](crate::common::RelativeSide).
257	///
258	/// The old signal level, prior to modification, is returned.
259	///
260	/// # Errors
261	/// * [`BadComponent`](Error::BadComponent)
262	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
263	pub async fn set_side_output(&mut self, side: impl Side, level: u8) -> Result<u8, Error> {
264		let side: u8 = side.into();
265		let ret: (u8,) = component_method(
266			self.invoker,
267			self.buffer,
268			&self.address,
269			"setOutput",
270			Some(&(side, level)),
271		)
272		.await?;
273		Ok(ret.0)
274	}
275
276	/// Sets the bundled signal strengths to emit on any subset of wires on any subset of sides.
277	///
278	/// The `levels` parameter contains an element for each side. For a redstone block, the indices
279	/// into this array are [absolute sides](crate::common::AbsoluteSide). For a redstone card in a
280	/// computer, the indices are [relative sides](crate::common::RelativeSide). Each element of
281	/// the array is itself another array. The inner arrays are indexed by colour. Each element of
282	/// the inner array can be `Some` with the new signal strength to emit, or `None` to leave that
283	/// colour unmodified.
284	///
285	/// # Errors
286	/// * [`BadComponent`](Error::BadComponent)
287	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
288	pub async fn set_bundled_output(
289		&mut self,
290		levels: &[[Option<u8>; COLOURS]; BLOCK_SIDES],
291	) -> Result<(), Error> {
292		let param: (ArrayAsMap<ArrayAsMap<Option<u8>, COLOURS>, BLOCK_SIDES>,) = (ArrayAsMap([
293			ArrayAsMap(levels[0]),
294			ArrayAsMap(levels[1]),
295			ArrayAsMap(levels[2]),
296			ArrayAsMap(levels[3]),
297			ArrayAsMap(levels[4]),
298			ArrayAsMap(levels[5]),
299		]),);
300		component_method::<_, Ignore, _>(
301			self.invoker,
302			self.buffer,
303			&self.address,
304			"setBundledOutput",
305			Some(&param),
306		)
307		.await?;
308		Ok(())
309	}
310
311	/// Sets the bundled signal strengths to emit on any subset of wires on a single side.
312	///
313	/// The `side` parameter selects the side to modify. For a redstone block, the value must be an
314	/// [absolute side](crate::common::AbsoluteSide). For a redstone card in a computer, the value
315	/// must be a [relative side](crate::common::RelativeSide).
316	///
317	/// The `levels` parameter contains an element for each colour. Each element of `levels` can be
318	/// `Some` with the new signal strength to emit, or `None` to leave that colour unmodified.
319	///
320	/// # Errors
321	/// * [`BadComponent`](Error::BadComponent)
322	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
323	pub async fn set_side_bundled_output(
324		&mut self,
325		side: impl Side,
326		levels: &[Option<u8>; COLOURS],
327	) -> Result<(), Error> {
328		let side: u8 = side.into();
329		component_method::<_, Ignore, _>(
330			self.invoker,
331			self.buffer,
332			&self.address,
333			"setBundledOutput",
334			Some(&(side, ArrayAsMap(*levels))),
335		)
336		.await?;
337		Ok(())
338	}
339
340	/// Sets the bundled signal strength to emit on a single wire on a single side.
341	///
342	/// The `side` parameter selects the side to modify. For a redstone block, the value must be an
343	/// [absolute side](crate::common::AbsoluteSide). For a redstone card in a computer, the value
344	/// must be a [relative side](crate::common::RelativeSide).
345	///
346	/// The old signal level, prior to modification, is returned.
347	///
348	/// # Errors
349	/// * [`BadComponent`](Error::BadComponent)
350	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
351	pub async fn set_side_colour_bundled_output(
352		&mut self,
353		side: impl Side,
354		colour: Colour,
355		level: u8,
356	) -> Result<u8, Error> {
357		let side: u8 = side.into();
358		let colour: u8 = colour.into();
359		let ret: (u8,) = component_method(
360			self.invoker,
361			self.buffer,
362			&self.address,
363			"setBundledOutput",
364			Some(&(side, colour, level)),
365		)
366		.await?;
367		Ok(ret.0)
368	}
369
370	/// Returns the comparator value on a given side.
371	///
372	/// For a redstone block, the `side` parameter must be an [absolute
373	/// side](crate::common::AbsoluteSide). For a redstone card in a computer, the `side` parameter
374	/// must be a [relative side](crate::common::RelativeSide).
375	///
376	/// The returned value is the signal level that would be emitted by a comparator sensing the
377	/// adjacent block. If the target block is not readable by a comparator, zero is returned.
378	///
379	/// # Errors
380	/// * [`BadComponent`](Error::BadComponent)
381	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
382	pub async fn get_comparator_input(&mut self, side: impl Side) -> Result<u8, Error> {
383		let side: u8 = side.into();
384		let ret: (u8,) = component_method(
385			self.invoker,
386			self.buffer,
387			&self.address,
388			"getComparatorInput",
389			Some(&(side,)),
390		)
391		.await?;
392		Ok(ret.0)
393	}
394
395	/// Returns the wake threshold.
396	///
397	/// When any redstone input changes from being strictly less than the threshold to greater than
398	/// or equal to the threshold, the containing computer (in the case of a redstone card) or all
399	/// connected computers (in the case of a redstone block) are powered on if they are off.
400	///
401	/// # Errors
402	/// * [`BadComponent`](Error::BadComponent)
403	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
404	pub async fn get_wake_threshold(&mut self) -> Result<u32, Error> {
405		let ret: (u32,) = component_method::<(), _, _>(
406			self.invoker,
407			self.buffer,
408			&self.address,
409			"getWakeThreshold",
410			None,
411		)
412		.await?;
413		Ok(ret.0)
414	}
415
416	/// Sets the wake threshold.
417	///
418	/// When any redstone input changes from being strictly less than the threshold to greater than
419	/// or equal to the threshold, the containing computer (in the case of a redstone card) or all
420	/// connected computers (in the case of a redstone block) are powered on if they are off.
421	///
422	/// The old wake threshold, prior to modification, is returned.
423	///
424	/// # Errors
425	/// * [`BadComponent`](Error::BadComponent)
426	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
427	pub async fn set_wake_threshold(&mut self, threshold: u32) -> Result<u32, Error> {
428		let ret: (u32,) = component_method(
429			self.invoker,
430			self.buffer,
431			&self.address,
432			"setWakeThreshold",
433			Some(&(threshold,)),
434		)
435		.await?;
436		Ok(ret.0)
437	}
438
439	async fn get_vanilla(&mut self, method: &str) -> Result<[u8; BLOCK_SIDES], Error> {
440		let ret: (ArrayAsMap<u8, BLOCK_SIDES>,) =
441			component_method::<(), _, _>(self.invoker, self.buffer, &self.address, method, None)
442				.await?;
443		Ok(ret.0 .0)
444	}
445
446	async fn get_side_vanilla(&mut self, method: &str, side: u8) -> Result<u8, Error> {
447		let ret: (u8,) = component_method(
448			self.invoker,
449			self.buffer,
450			&self.address,
451			method,
452			Some(&(side,)),
453		)
454		.await?;
455		Ok(ret.0)
456	}
457
458	async fn get_bundled(&mut self, method: &str) -> Result<[[u8; COLOURS]; BLOCK_SIDES], Error> {
459		let ret: (ArrayAsMap<ArrayAsMap<u8, COLOURS>, BLOCK_SIDES>,) =
460			component_method::<(), _, _>(self.invoker, self.buffer, &self.address, method, None)
461				.await?;
462		Ok([
463			ret.0 .0[0].0,
464			ret.0 .0[1].0,
465			ret.0 .0[2].0,
466			ret.0 .0[3].0,
467			ret.0 .0[4].0,
468			ret.0 .0[5].0,
469		])
470	}
471
472	async fn get_side_bundled(&mut self, method: &str, side: u8) -> Result<[u8; COLOURS], Error> {
473		let ret: (ArrayAsMap<u8, COLOURS>,) = component_method(
474			self.invoker,
475			self.buffer,
476			&self.address,
477			method,
478			Some(&(side,)),
479		)
480		.await?;
481		Ok(ret.0 .0)
482	}
483
484	async fn get_side_colour_bundled(
485		&mut self,
486		method: &str,
487		side: u8,
488		colour: u8,
489	) -> Result<u8, Error> {
490		let ret: (u8,) = component_method(
491			self.invoker,
492			self.buffer,
493			&self.address,
494			method,
495			Some(&(side, colour)),
496		)
497		.await?;
498		Ok(ret.0)
499	}
500}
501
502/// An array that is CBOR-encoded as a map keyed by array index, rather than a CBOR array.
503///
504/// To be decoded, the element type must implement [`Copy`], [`Decode`](minicbor::Decode) and
505/// [`Default`], and any elements not included in the CBOR-encoded map will be returned at their
506/// default values.
507///
508/// To be encoded, the element type must be an [`Option`] and the type contained therein must
509/// implement [`Encode`](minicbor::Encode). `None` values are omitted from the map entirely, while
510/// `Some` values are encoded as their contents, keyed by array position.
511#[derive(Clone, Copy)]
512struct ArrayAsMap<T, const LENGTH: usize>(pub [T; LENGTH]);
513
514impl<'buffer, Context, T: Copy + Decode<'buffer, Context> + Default, const LENGTH: usize>
515	Decode<'buffer, Context> for ArrayAsMap<T, LENGTH>
516{
517	fn decode(
518		d: &mut minicbor::Decoder<'buffer>,
519		context: &mut Context,
520	) -> Result<Self, minicbor::decode::Error> {
521		let mut ret = [T::default(); LENGTH];
522		// The CBOR fits in memory, so it must be <2³² elements.
523		#[allow(clippy::cast_possible_truncation)]
524		let length = d
525			.map()?
526			.ok_or_else(|| minicbor::decode::Error::message(""))? as usize;
527		for _ in 0..length {
528			let key = d.u32()?;
529			ret[key as usize] = d.decode_with(context)?;
530		}
531		Ok(Self(ret))
532	}
533}
534
535impl<Context, T: Encode<Context>, const LENGTH: usize> Encode<Context>
536	for ArrayAsMap<Option<T>, LENGTH>
537{
538	fn encode<W: minicbor::encode::Write>(
539		&self,
540		e: &mut minicbor::Encoder<W>,
541		context: &mut Context,
542	) -> Result<(), minicbor::encode::Error<W::Error>> {
543		let count = self.0.iter().filter(|i| i.is_some()).count();
544		e.map(count as u64)?;
545		for i in 0..LENGTH {
546			if let Some(elt) = &self.0[i] {
547				e.u64(i as u64)?;
548				e.encode_with(elt, context)?;
549			}
550		}
551		Ok(())
552	}
553}
554
555impl<Context, T: Encode<Context>, const INNER_LENGTH: usize, const OUTER_LENGTH: usize>
556	Encode<Context> for ArrayAsMap<ArrayAsMap<Option<T>, INNER_LENGTH>, OUTER_LENGTH>
557{
558	fn encode<W: minicbor::encode::Write>(
559		&self,
560		e: &mut minicbor::Encoder<W>,
561		context: &mut Context,
562	) -> Result<(), minicbor::encode::Error<W::Error>> {
563		e.map(OUTER_LENGTH as u64)?;
564		for i in 0..OUTER_LENGTH {
565			e.u64(i as u64)?;
566			e.encode_with(&self.0[i], context)?;
567		}
568		Ok(())
569	}
570}
571
572impl<T, const LENGTH: usize> Default for ArrayAsMap<T, LENGTH>
573where
574	[T; LENGTH]: Default,
575{
576	fn default() -> Self {
577		Self(Default::default())
578	}
579}