Skip to main content

miku_rpc/
wrappers.rs

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
10/// An opencomputers HLApi device.
11pub trait RPCDevice {
12    /// Returns uuid of this device.
13    fn id(&self) -> &str;
14
15    /// Create a device wrapper from an id.
16    fn from_id(id: String) -> Self;
17}
18
19/// A HLApi device that has an identity - like "redstone". This is used for the [crate::DeviceBus::wrap] method.
20pub trait IdentifiedDevice: RPCDevice {
21    const IDENTITY: &'static str;
22}
23
24/// An interface that allows for interacting with an energy storage device.
25pub 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
39/// An interface that allows for interacting with items.
40pub 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
51/// An interface that allows for interacting with fluid tanks.
52pub 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
63/// An interface that allows for interacting with redstone signals.
64pub trait RedstoneInterface: RPCDevice {
65    #[rpc("getRedstoneInput", docs = "block/redstone_interface.md")]
66    /// gets the received redstone signal for the specified side.
67    fn get_redstone_input(side: &str) -> i32;
68
69    #[rpc("getRedstoneOutput", docs = "block/redstone_interface.md")]
70    /// gets the emitted redstone signal for the specified side.
71    fn get_redstone_output(side: &str) -> i32;
72
73    #[rpc("setRedstoneOutput", docs = "block/redstone_interface.md")]
74    /// sets the emitted redstone signal for the specified side.
75    fn set_redstone_output(side: &str, val: i32);
76}
77
78/// A device capable of playing sounds.
79pub trait SoundInterface: RPCDevice {
80    /// returns a list of available sound effects matching the given name. Note that the number of results is limited, so overly generic queries will result in truncated results.
81    #[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    /// plays back the sound effect with the specified name.
86    fn play_sound(name: &str, volume: f64, pitch: f64);
87}
88
89/// An interface that allows exporting and importing files.
90pub 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
113/// An interface that allows for the manipulation of blocks in the world.
114///
115/// The side parameter in the following methods represents a direction from the perspective of the robot. Valid values are: "front", "up" and "down"
116pub trait BlockOperationsInterface: RPCDevice {
117    #[rpc("excavate", docs = "item/block_operations_module.md")]
118    /// tries to break a block in the specified direction. Collected blocks will be inserted starting at the currently selected inventory slot. If the selected slot is full, the next slot will be used, and so on. If the inventory has no space for the dropped block, it will drop into the world.
119    /// Returns whether the operation was successful.
120    fn excavate(side: &str) -> bool;
121
122    #[rpc("place", docs = "item/block_operations_module.md")]
123    /// tries to place a block in the specified direction. Blocks will be placed from the currently selected inventory slot. If the slot is empty, no block will be placed.
124    /// Returns whether the operation was successful.
125    fn place(side: &str) -> bool;
126
127    #[rpc("durability", docs = "item/block_operations_module.md")]
128    /// returns the remaining durability of the module's excavation tool. Once the durability has reached zero, no further excavation operations can be performed until it is repaired.
129    fn durability() -> i32;
130
131    #[rpc("repair", docs = "item/block_operations_module.md")]
132    /// attempts to repair the module's excavation tool using materials in the currently selected inventory slot. This method will consume one item at a time. Any regular tool may act as the source for repair materials, such as pickaxes and shovels. The quality of the tool directly effects the amount of durability restored.
133    /// Returns whether some material could be used to repair the module's excavation tool.
134    fn repair() -> bool;
135}
136
137/// An interface that allows for the manipulation of inventories in the world.
138///
139/// The side parameter in the following methods represents a direction from the perspective of the robot. Valid values are: "front", "up" and "down"
140pub trait InventoryOperationsInterface: RPCDevice {
141    #[rpc("move", docs = "item/inventory_operations.md")]
142    /// tries to move the specified number of items from one robot inventory slot to another.
143    fn move_stack(from: i32, into: i32, count: i32);
144
145    #[rpc("drop", docs = "item/inventory_operations.md")]
146    /// tries to drop items from the specified slot in the specified direction. It will drop items either into an inventory, or the world if no inventory is present.
147    /// Returns the number of items dropped.
148    fn drop(count: i32, side: &str) -> i32;
149
150    #[rpc("dropInto", docs = "item/inventory_operations.md")]
151    /// tries to drop items from the specified slot into the specified slot of an inventory in the specified direction. It will only drop items into an inventory.
152    /// Returns the number of items dropped.
153    fn drop_into(into: i32, count: i32, side: &str) -> i32;
154
155    #[rpc("take", docs = "item/inventory_operations.md")]
156    /// tries to take the specified number of items from the specified direction. It will take items from either an inventory, or the world if no inventory is present.
157    /// Returns the number of items taken.
158    fn take(count: i32, side: &str) -> i32;
159
160    #[rpc("take_from", docs = "item/inventory_operations.md")]
161    /// tries to take the specified number of items from the specified slot from an inventory in the specified direction. It will only take items from an inventory.
162    /// Returns the number of items taken.
163    fn take_from(from: i32, count: i32, side: &str) -> i32;
164}
165
166/// A robit!
167pub trait RobotInterface: RPCDevice {
168    #[rpc("getEnergyStored", docs = "item/robot.md")]
169    /// returns the current amount of energy stored in the robot's internal energy storage.
170    fn get_energy_stored() -> i32;
171
172    #[rpc("getMaxEnergyStored", docs = "item/robot.md")]
173    /// returns the maximum amount of energy that can be stored in the robot's internal energy storage.
174    fn get_max_energy_stored() -> i32;
175
176    #[rpc("getSelectedSlot", docs = "item/robot.md")]
177    /// returns the currently selected robot inventory slot. This is used by many modules as an implicit input.
178    fn get_selected_slot() -> i32;
179
180    #[rpc("setSelectedSlot", docs = "item/robot.md")]
181    /// sets the currently selected robot inventory slot. This is used by many modules as an implicit input.
182    fn set_selected_slot(slot: i32);
183
184    #[rpc("getStackInSlot", docs = "item/robot.md")]
185    /// gets a description of the item in the specified slot.
186    fn get_stack_in_slot<T: Deserialize>(slot: i32) -> T;
187
188    #[rpc("getLastActionId", docs = "item/robot.md")]
189    /// returns the opaque id of the last enqueued action. Call this after a successful move_async() or turn_async() call to obtain the id associated with the enqueued action.
190    fn get_last_action_id() -> i32;
191
192    #[rpc("getQueuedActionCount", docs = "item/robot.md")]
193    /// returns the number of actions currently waiting in the action queue to be processed. Use this to wait for actions to finish when enqueueing fails.
194    fn get_queued_action_count() -> i32;
195
196    #[rpc("getActionResult", docs = "item/robot.md")]
197    /// returns the result of the action with the specified id. Action ids can be obtained from get_last_action_id(). Only a limited number of past action results are available.
198    fn get_action_result(id: i32) -> RobotActionResult;
199
200    #[rpc("move", docs = "item/robot.md")]
201    /// tries to enqueue a movement action in the specified direction.
202    /// Returns whether the action was enqueued successfully.
203    fn move_async(direction: MoveDirection) -> bool;
204
205    #[rpc("turn", docs = "item/robot.md")]
206    /// tries to enqueue a turn action in the specified direction.
207    /// Returns whether the action was enqueued successfully.
208    fn turn_async(direction: RotationDirection) -> bool;
209
210    /// Same as move_async(), but waits until action is succesfully enqueued and completed.
211    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    /// Same as turn_async(), but waits until action is succesfully enqueued and completed.
220    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    /// Waits for an action to complete; returns if it was sucessful or not.
233    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);