mcinterface/lib.rs
1//! Wrapper library for wasmcraft2 datapacks written in Rust.
2//!
3//! [wasmcraft2](https://github.com/SuperTails/wasmcraft2) is a WebAssembly to Minecraft datapack
4//! transpiler. This library provides safe access to wasmcraft's API, containing all functions present
5//! in wasmcraft's `mcinterface.h` as well as some additional helper functions and macros.
6//!
7//! When writing programs for wasmcraft2, it is important to note its limitations - notably, floating
8//! point operations are not supported, so using the [`fixed`](https://docs.rs/fixed/latest/fixed/)
9//! crate is recommended if integers are not enough. Minecraft programs must be `#![no_main]` and `#![no_std]`; this
10//! crate provides a Minecraft-compatible panic handler but there is no allocator. Decreasing the default
11//! stack size is recommended - you can do this by adding the following to your `.cargo/config`:
12//! ```toml
13//! [target.wasm32-unknown-unknown]
14//! rustflags = [ "-C", "link-args=-z stack-size=4096" ]
15//! ```
16//! If more stack space is required, you can change 4096 to some greater number.
17//!
18//! While you're in `.cargo/config`, you should also set the default target to `wasm32-unknown-unknown`
19//! ```toml
20//! [build]
21//! target = "wasm32-unknown-unknown"
22//! ```
23//! Enabling some optimisation even in debug builds is recommended, since Minecraft commands are not
24//! the fastest compilation target ever - add the following to your `Cargo.toml`:
25//! ```toml
26//! [profile.dev]
27//! opt-level = 1
28//! ```
29//! wasmcraft2 does not support the `main` function - your entrypoint must be declared as follows:
30//! ```no_run
31//! #[no_mangle]
32//! pub extern fn _start() -> i32 {
33//! // Your code goes here...
34//! return 0;
35//! }
36//! ```
37
38#![no_std]
39
40#[cfg(feature = "fmt")]
41pub mod fmt;
42
43use core::panic::PanicInfo;
44
45/// An enum representing a Minecraft block.
46/// This contains all the block types currently supported by wasmcraft2, which is a very limited
47/// subset of Minecraft's block selection. There is currently no way to place any other blocks
48/// through wasmcraft2.
49#[repr(C)]
50#[derive(Eq, PartialEq, Copy, Clone, Debug)]
51pub enum Block {
52 Air,
53 Cobblestone,
54 Granite,
55 Andesite,
56 Diorite,
57 Lapis,
58 Iron,
59 Gold,
60 Diamond,
61 Redstone,
62 Emerald,
63 Dirt,
64 OakLog,
65 OakLeaves,
66}
67
68extern {
69 #[link_name = "print"]
70 fn _mci_unsafe_print(value: i32);
71
72 /// Set all bytes in a region of memory (with length `length`, starting from `ptr`) to `value`.
73 ///
74 /// As far as I can tell, behaves like C `memset()`.
75 pub fn memset(ptr: *mut i32, value: i32, length: u32) -> *mut i32;
76
77 #[link_name = "turtle_x"]
78 fn _mci_unsafe_turtle_x(value: i32);
79 #[link_name = "turtle_y"]
80 fn _mci_unsafe_turtle_y(value: i32);
81 #[link_name = "turtle_z"]
82 fn _mci_unsafe_turtle_z(value: i32);
83 #[link_name = "turtle_fill"]
84 fn _mci_unsafe_turtle_fill(block: Block, x_span: i32, y_span: i32, z_span: i32);
85 #[link_name = "turtle_set"]
86 fn _mci_unsafe_turtle_set(block: Block);
87 #[link_name = "turtle_get"]
88 fn _mci_unsafe_turtle_get() -> Block;
89 #[link_name = "turtle_copy_region"]
90 fn _mci_unsafe_turtle_copy_region(x_span: i32, y_span: i32, z_span: i32);
91 #[link_name = "turtle_paste_region_masked"]
92 fn _mci_unsafe_turtle_paste_region_masked(x_span: i32, y_span: i32, z_span: i32);
93 #[link_name = "turtle_copy"]
94 fn _mci_unsafe_turtle_copy();
95 #[link_name = "turtle_paste"]
96 fn _mci_unsafe_turtle_paste();
97
98 #[link_name = "mc_sleep"]
99 fn _mci_unsafe_mc_sleep();
100 #[link_name = "mc_putc"]
101 fn _mci_unsafe_mc_putc(ch: i32);
102}
103
104/// Print an integer to the Minecraft chat.
105#[inline(always)]
106pub fn print(value: i32) {
107 unsafe { _mci_unsafe_print(value) }
108}
109
110/// Set the x position of the turtle
111#[inline(always)]
112pub fn turtle_x(value: i32) {
113 unsafe { _mci_unsafe_turtle_x(value) }
114}
115
116/// Set the y position of the turtle.
117#[inline(always)]
118pub fn turtle_y(value: i32) {
119 unsafe { _mci_unsafe_turtle_y(value) }
120}
121
122/// Set the z position of the turtle.
123#[inline(always)]
124pub fn turtle_z(value: i32) {
125 unsafe { _mci_unsafe_turtle_z(value) }
126}
127
128/// Set the position of the turtle. This will call `turtle_x`, `turtle_y` and `turtle_z`, so it is
129/// more efficient to call those individually if you do not need to change all 3 coordinates.
130#[inline(always)]
131pub fn turtle_pos(x: i32, y: i32, z: i32) {
132 unsafe {
133 _mci_unsafe_turtle_x(x);
134 _mci_unsafe_turtle_y(y);
135 _mci_unsafe_turtle_z(z);
136 }
137}
138
139
140/// Fills a volume relative to the turtle's postion.
141/// The x, y, and z span arguments are effectively the size of the region minus one,
142/// so `turtle_fill(block, 0, 0, 0)` is equivalent to `turtle_set(block)`
143///
144/// This function is unstable, and may cause wasmcraft2 to fail compilation.
145#[inline(always)]
146pub fn turtle_fill(block: Block, x_span: i32, y_span: i32, z_span: i32) {
147 unsafe { _mci_unsafe_turtle_fill(block, x_span, y_span, z_span) }
148}
149
150/// Set the block at the turtle's position.
151#[inline(always)]
152pub fn turtle_set(block: Block) {
153 unsafe { _mci_unsafe_turtle_set(block) }
154}
155
156/// Get the block at the turtle's position.
157#[inline(always)]
158pub fn turtle_get() -> Block {
159 unsafe { _mci_unsafe_turtle_get() }
160}
161
162/// Check if the given block is present at the turtle's position.
163#[inline(always)]
164pub fn turtle_check(block: Block) -> bool {
165 block == unsafe { _mci_unsafe_turtle_get() }
166}
167
168/// Copy a given region from the turtle's position.
169///
170/// Paste the region using [`turtle_paste_region_masked()`].
171#[inline(always)]
172pub fn turtle_copy_region(x_span: i32, y_span: i32, z_span: i32) {
173 unsafe { _mci_unsafe_turtle_copy_region(x_span, y_span, z_span); }
174}
175
176/// Paste the previously copied region from the turtle's position, ignoring air blocks.
177///
178/// To copy a region, use [`turtle_copy_region()`].
179#[inline(always)]
180pub fn turtle_paste_region_masked(x_span: i32, y_span: i32, z_span: i32) {
181 unsafe { _mci_unsafe_turtle_paste_region_masked(x_span, y_span, z_span); }
182}
183
184/// Copy the block at the turtle's position.
185///
186/// Paste the block using [`turtle_paste()`].
187#[inline(always)]
188pub fn turtle_copy() {
189 unsafe { _mci_unsafe_turtle_copy() }
190}
191
192/// Place the previously copied block at the turtle's position.
193///
194/// To copy a block, use [`turtle_copy()`].
195#[inline(always)]
196pub fn turtle_paste() {
197 unsafe { _mci_unsafe_turtle_paste() }
198}
199
200/// Pauses execution until the next game tick.
201///
202/// wasmcraft2 will automatically insert sleep calls before functions and inside loops. However, if
203/// your program contains large stretches of code without loops or function calls, it may be necessary
204/// to manually insert `mc_sleep()` calls. See the wasmcraft2 README for more information.
205#[inline(always)]
206pub fn mc_sleep() {
207 unsafe { _mci_unsafe_mc_sleep() }
208}
209
210/// Write a character to the game chat. Characters will not appear until a newline (`'\n'`) is written.
211///
212/// Only ASCII printable characters will be printed; any other characters will appear as a � symbol.
213#[inline(always)]
214pub fn mc_putc(ch: char) {
215 unsafe { _mci_unsafe_mc_putc(ch as i32) }
216}
217
218/// Print a string to the game chat. Any printed characters will not appear until a newline (`'\n'`) is written.
219/// Only ASCII printable characters will be printed; any other characters will appear as a � symbol.
220///
221/// If you want to print a string to the game chat with a newline, consider using [`println()`].
222/// If formatting is required, consider using [`print!`] or [`println!`].
223pub fn print_str(s: &str) {
224 for c in s.chars() {
225 mc_putc(c);
226 }
227}
228
229/// Print a string to the game chat, with a newline.
230/// Only ASCII printable characters will be printed; any other characters will appear as a � symbol.
231///
232/// If formatting is required, consider using [`println!`].
233pub fn println(s: &str) {
234 for c in s.chars() {
235 mc_putc(c);
236 }
237 mc_putc('\n');
238}
239
240/// Get an i32 value from the compile-time environment, or if the environment variable is not present
241/// or is not an i32, use the provided default value.
242///
243/// This is useful for configuring your program as there is no good way of asking for user input at
244/// runtime.
245///
246/// Usage:
247/// ```ignore
248/// # use mcinterface::env_i32_default;
249/// let x = env_i32_default!("SOME_ENVIRONMENT_VARIABLE", 50);
250/// ```
251#[macro_export]
252macro_rules! env_i32_default {
253 ($var:literal,$default:literal) => {{
254 use core::str::FromStr;
255 option_env!($var).map_or($default, |n| i32::from_str(n).unwrap_or($default))
256 }};
257}
258
259#[panic_handler]
260fn panic(_info: &PanicInfo) -> ! {
261 println("RUST PANIC - entering infinite loop!");
262 loop { mc_sleep(); }
263}