use crate::types::{ImportFileInfo, MoveDirection, RobotActionResult, RotationDirection};
use miku_macros::{define_device, rpc};
use miniserde_miku::Deserialize;
use std::io;
use std::thread;
use std::time::Duration;
const ROBOT_ACTION_SLEEP: Duration = Duration::from_millis(100);
pub trait RPCDevice {
fn id(&self) -> &str;
fn from_id(id: String) -> Self;
}
pub trait IdentifiedDevice: RPCDevice {
const IDENTITY: &'static str;
}
pub trait EnergyStorage: RPCDevice {
#[rpc("getEnergyStored")]
fn get_energy_stored() -> i32;
#[rpc("getMaxEnergyStored")]
fn get_max_energy_stored() -> i32;
#[rpc("canExtractEnergy")]
fn can_extract_energy() -> bool;
#[rpc("canReceiveEnergy")]
fn can_receive_energy() -> bool;
}
pub trait ItemHandler: RPCDevice {
#[rpc("getItemSlotCount")]
fn get_item_slot_count() -> i32;
#[rpc("getItemSlotLimit")]
fn get_item_slot_limit(slot: i32) -> i32;
#[rpc("getItemStackInSlot")]
fn get_item_stack_in_slot<T: Deserialize>(slot: i32) -> T;
}
pub trait FluidHandler: RPCDevice {
#[rpc("getFluidTanks")]
fn get_fluid_tanks() -> i32;
#[rpc("getFluidTankCapacity")]
fn get_fluid_tank_capacity(tank: i32) -> i32;
#[rpc("getFluidInTank")]
fn get_fluid_in_tank<T: Deserialize>(tank: i32) -> T;
}
pub trait RedstoneInterface: RPCDevice {
#[rpc("getRedstoneInput", docs = "block/redstone_interface.md")]
fn get_redstone_input(side: &str) -> i32;
#[rpc("getRedstoneOutput", docs = "block/redstone_interface.md")]
fn get_redstone_output(side: &str) -> i32;
#[rpc("setRedstoneOutput", docs = "block/redstone_interface.md")]
fn set_redstone_output(side: &str, val: i32);
}
pub trait SoundInterface: RPCDevice {
#[rpc("findSound", docs = "item/sound_card.md")]
fn find_sound(name: &str) -> Vec<String>;
#[rpc("playSound", docs = "item/sound_card.md")]
fn play_sound(name: &str, volume: f64, pitch: f64);
}
pub trait FileImportExport: RPCDevice {
#[rpc("requestImportFile")]
fn request_import_file() -> bool;
#[rpc("beginImportFile")]
fn begin_import_file() -> Option<ImportFileInfo>;
#[rpc("readImportFile")]
fn read_import_file() -> Option<Vec<u8>>;
#[rpc("beginExportFile")]
fn begin_export_file(name: &str);
#[rpc("writeExportFile")]
fn write_export_file(data: &[u8]);
#[rpc("finishExportFile")]
fn finish_export_file();
#[rpc("reset")]
fn reset();
}
pub trait BlockOperationsInterface: RPCDevice {
#[rpc("excavate", docs = "item/block_operations_module.md")]
fn excavate(side: &str) -> bool;
#[rpc("place", docs = "item/block_operations_module.md")]
fn place(side: &str) -> bool;
#[rpc("durability", docs = "item/block_operations_module.md")]
fn durability() -> i32;
#[rpc("repair", docs = "item/block_operations_module.md")]
fn repair() -> bool;
}
pub trait InventoryOperationsInterface: RPCDevice {
#[rpc("move", docs = "item/inventory_operations.md")]
fn move_stack(from: i32, into: i32, count: i32);
#[rpc("drop", docs = "item/inventory_operations.md")]
fn drop(count: i32, side: &str) -> i32;
#[rpc("dropInto", docs = "item/inventory_operations.md")]
fn drop_into(into: i32, count: i32, side: &str) -> i32;
#[rpc("take", docs = "item/inventory_operations.md")]
fn take(count: i32, side: &str) -> i32;
#[rpc("take_from", docs = "item/inventory_operations.md")]
fn take_from(from: i32, count: i32, side: &str) -> i32;
}
pub trait RobotInterface: RPCDevice {
#[rpc("getEnergyStored", docs = "item/robot.md")]
fn get_energy_stored() -> i32;
#[rpc("getMaxEnergyStored", docs = "item/robot.md")]
fn get_max_energy_stored() -> i32;
#[rpc("getSelectedSlot", docs = "item/robot.md")]
fn get_selected_slot() -> i32;
#[rpc("setSelectedSlot", docs = "item/robot.md")]
fn set_selected_slot(slot: i32);
#[rpc("getStackInSlot", docs = "item/robot.md")]
fn get_stack_in_slot<T: Deserialize>(slot: i32) -> T;
#[rpc("getLastActionId", docs = "item/robot.md")]
fn get_last_action_id() -> i32;
#[rpc("getQueuedActionCount", docs = "item/robot.md")]
fn get_queued_action_count() -> i32;
#[rpc("getActionResult", docs = "item/robot.md")]
fn get_action_result(id: i32) -> RobotActionResult;
#[rpc("move", docs = "item/robot.md")]
fn move_async(direction: MoveDirection) -> bool;
#[rpc("turn", docs = "item/robot.md")]
fn turn_async(direction: RotationDirection) -> bool;
fn move_wait(&self, bus: &mut crate::DeviceBus, direction: MoveDirection) -> io::Result<bool> {
while !self.move_async(bus, direction)? {
thread::sleep(ROBOT_ACTION_SLEEP)
}
let id = self.get_last_action_id(bus)?;
self.wait_for_action(bus, id)
}
fn turn_wait(
&self,
bus: &mut crate::DeviceBus,
direction: RotationDirection,
) -> io::Result<bool> {
while !self.turn_async(bus, direction)? {
thread::sleep(ROBOT_ACTION_SLEEP)
}
let id = self.get_last_action_id(bus)?;
self.wait_for_action(bus, id)
}
fn wait_for_action(&self, bus: &mut crate::DeviceBus, action: i32) -> io::Result<bool> {
let result = loop {
let result = self.get_action_result(bus, action)?;
match result {
RobotActionResult::Success | RobotActionResult::Failure => break result,
RobotActionResult::Incomplete => thread::sleep(ROBOT_ACTION_SLEEP),
}
};
Ok(result == RobotActionResult::Success)
}
}
define_device!(
RedstoneDevice,
"redstone",
"A device capable of interacting with redstone",
[RedstoneInterface]
);
define_device!(
SoundCard,
"sound",
"A device capable of playing sounds",
[SoundInterface]
);
define_device!(
FileImportExportCard,
"file_import_export",
"A device capable of importing and exporting files",
[FileImportExport]
);
define_device!(Robot, "robot", "A robit!", [RobotInterface]);
define_device!(
BlockOperationsModule,
"block_operations",
"A device capable of manipulating blocks in the world",
[BlockOperationsInterface]
);
define_device!(
InventoryOperationsModule,
"inventory_operations",
"A device capable of manipulating inventories in the world",
[InventoryOperationsInterface]
);