1use crate::types::{ImportFileInfo, MoveDirection, RobotActionResult, RotationDirection};
2use miku_macros::{define_device, rpc};
3use miniserde_miku::Deserialize;
4use std::io;
5use std::thread;
6use std::time::Duration;
7
8const ROBOT_ACTION_SLEEP: Duration = Duration::from_millis(100);
9
10pub trait RPCDevice {
12 fn id(&self) -> &str;
14
15 fn from_id(id: String) -> Self;
17}
18
19pub trait IdentifiedDevice: RPCDevice {
21 const IDENTITY: &'static str;
22}
23
24pub trait EnergyStorage: RPCDevice {
26 #[rpc("getEnergyStored")]
27 fn get_energy_stored() -> i32;
28
29 #[rpc("getMaxEnergyStored")]
30 fn get_max_energy_stored() -> i32;
31
32 #[rpc("canExtractEnergy")]
33 fn can_extract_energy() -> bool;
34
35 #[rpc("canReceiveEnergy")]
36 fn can_receive_energy() -> bool;
37}
38
39pub trait ItemHandler: RPCDevice {
41 #[rpc("getItemSlotCount")]
42 fn get_item_slot_count() -> i32;
43
44 #[rpc("getItemSlotLimit")]
45 fn get_item_slot_limit(slot: i32) -> i32;
46
47 #[rpc("getItemStackInSlot")]
48 fn get_item_stack_in_slot<T: Deserialize>(slot: i32) -> T;
49}
50
51pub trait FluidHandler: RPCDevice {
53 #[rpc("getFluidTanks")]
54 fn get_fluid_tanks() -> i32;
55
56 #[rpc("getFluidTankCapacity")]
57 fn get_fluid_tank_capacity(tank: i32) -> i32;
58
59 #[rpc("getFluidInTank")]
60 fn get_fluid_in_tank<T: Deserialize>(tank: i32) -> T;
61}
62
63pub trait RedstoneInterface: RPCDevice {
65 #[rpc("getRedstoneInput", docs = "block/redstone_interface.md")]
66 fn get_redstone_input(side: &str) -> i32;
68
69 #[rpc("getRedstoneOutput", docs = "block/redstone_interface.md")]
70 fn get_redstone_output(side: &str) -> i32;
72
73 #[rpc("setRedstoneOutput", docs = "block/redstone_interface.md")]
74 fn set_redstone_output(side: &str, val: i32);
76}
77
78pub trait SoundInterface: RPCDevice {
80 #[rpc("findSound", docs = "item/sound_card.md")]
82 fn find_sound(name: &str) -> Vec<String>;
83
84 #[rpc("playSound", docs = "item/sound_card.md")]
85 fn play_sound(name: &str, volume: f64, pitch: f64);
87}
88
89pub trait FileImportExport: RPCDevice {
91 #[rpc("requestImportFile")]
92 fn request_import_file() -> bool;
93
94 #[rpc("beginImportFile")]
95 fn begin_import_file() -> Option<ImportFileInfo>;
96
97 #[rpc("readImportFile")]
98 fn read_import_file() -> Option<Vec<u8>>;
99
100 #[rpc("beginExportFile")]
101 fn begin_export_file(name: &str);
102
103 #[rpc("writeExportFile")]
104 fn write_export_file(data: &[u8]);
105
106 #[rpc("finishExportFile")]
107 fn finish_export_file();
108
109 #[rpc("reset")]
110 fn reset();
111}
112
113pub trait BlockOperationsInterface: RPCDevice {
117 #[rpc("excavate", docs = "item/block_operations_module.md")]
118 fn excavate(side: &str) -> bool;
121
122 #[rpc("place", docs = "item/block_operations_module.md")]
123 fn place(side: &str) -> bool;
126
127 #[rpc("durability", docs = "item/block_operations_module.md")]
128 fn durability() -> i32;
130
131 #[rpc("repair", docs = "item/block_operations_module.md")]
132 fn repair() -> bool;
135}
136
137pub trait InventoryOperationsInterface: RPCDevice {
141 #[rpc("move", docs = "item/inventory_operations.md")]
142 fn move_stack(from: i32, into: i32, count: i32);
144
145 #[rpc("drop", docs = "item/inventory_operations.md")]
146 fn drop(count: i32, side: &str) -> i32;
149
150 #[rpc("dropInto", docs = "item/inventory_operations.md")]
151 fn drop_into(into: i32, count: i32, side: &str) -> i32;
154
155 #[rpc("take", docs = "item/inventory_operations.md")]
156 fn take(count: i32, side: &str) -> i32;
159
160 #[rpc("take_from", docs = "item/inventory_operations.md")]
161 fn take_from(from: i32, count: i32, side: &str) -> i32;
164}
165
166pub trait RobotInterface: RPCDevice {
168 #[rpc("getEnergyStored", docs = "item/robot.md")]
169 fn get_energy_stored() -> i32;
171
172 #[rpc("getMaxEnergyStored", docs = "item/robot.md")]
173 fn get_max_energy_stored() -> i32;
175
176 #[rpc("getSelectedSlot", docs = "item/robot.md")]
177 fn get_selected_slot() -> i32;
179
180 #[rpc("setSelectedSlot", docs = "item/robot.md")]
181 fn set_selected_slot(slot: i32);
183
184 #[rpc("getStackInSlot", docs = "item/robot.md")]
185 fn get_stack_in_slot<T: Deserialize>(slot: i32) -> T;
187
188 #[rpc("getLastActionId", docs = "item/robot.md")]
189 fn get_last_action_id() -> i32;
191
192 #[rpc("getQueuedActionCount", docs = "item/robot.md")]
193 fn get_queued_action_count() -> i32;
195
196 #[rpc("getActionResult", docs = "item/robot.md")]
197 fn get_action_result(id: i32) -> RobotActionResult;
199
200 #[rpc("move", docs = "item/robot.md")]
201 fn move_async(direction: MoveDirection) -> bool;
204
205 #[rpc("turn", docs = "item/robot.md")]
206 fn turn_async(direction: RotationDirection) -> bool;
209
210 fn move_wait(&self, bus: &mut crate::DeviceBus, direction: MoveDirection) -> io::Result<bool> {
212 while !self.move_async(bus, direction)? {
213 thread::sleep(ROBOT_ACTION_SLEEP)
214 }
215 let id = self.get_last_action_id(bus)?;
216 self.wait_for_action(bus, id)
217 }
218
219 fn turn_wait(
221 &self,
222 bus: &mut crate::DeviceBus,
223 direction: RotationDirection,
224 ) -> io::Result<bool> {
225 while !self.turn_async(bus, direction)? {
226 thread::sleep(ROBOT_ACTION_SLEEP)
227 }
228 let id = self.get_last_action_id(bus)?;
229 self.wait_for_action(bus, id)
230 }
231
232 fn wait_for_action(&self, bus: &mut crate::DeviceBus, action: i32) -> io::Result<bool> {
234 let result = loop {
235 let result = self.get_action_result(bus, action)?;
236 match result {
237 RobotActionResult::Success | RobotActionResult::Failure => break result,
238 RobotActionResult::Incomplete => thread::sleep(ROBOT_ACTION_SLEEP),
239 }
240 };
241
242 Ok(result == RobotActionResult::Success)
243 }
244}
245
246define_device!(
247 RedstoneDevice,
248 "redstone",
249 "A device capable of interacting with redstone",
250 [RedstoneInterface]
251);
252define_device!(
253 SoundCard,
254 "sound",
255 "A device capable of playing sounds",
256 [SoundInterface]
257);
258define_device!(
259 FileImportExportCard,
260 "file_import_export",
261 "A device capable of importing and exporting files",
262 [FileImportExport]
263);
264define_device!(Robot, "robot", "A robit!", [RobotInterface]);
265define_device!(
266 BlockOperationsModule,
267 "block_operations",
268 "A device capable of manipulating blocks in the world",
269 [BlockOperationsInterface]
270);
271define_device!(
272 InventoryOperationsModule,
273 "inventory_operations",
274 "A device capable of manipulating inventories in the world",
275 [InventoryOperationsInterface]
276);