Browse Source

Add RC cube & colour data read & write support

tags/v0.5.1
NGnius 3 years ago
parent
commit
0f2046a206
4 changed files with 214 additions and 1 deletions
  1. +1
    -0
      Cargo.toml
  2. +189
    -0
      src/robocraft/cubes.rs
  3. +3
    -0
      src/robocraft/mod.rs
  4. +21
    -1
      tests/robocraft_factory.rs

+ 1
- 0
Cargo.toml View File

@@ -13,6 +13,7 @@ serde_json = "^1"
reqwest = { version = "^0.11", features = ["json"]}
url = "^2.2"
ureq = { version = "^2", features = ["json"], optional = true}
base64 = "^0.13"

[dev-dependencies]
tokio = { version = "1.4.0", features = ["macros"]}


+ 189
- 0
src/robocraft/cubes.rs View File

@@ -0,0 +1,189 @@
use base64::{decode_config_buf, STANDARD};
use std::io::Read;

// TODO(maybe) parse iteratively instead of one-shot

#[derive(Clone)]
pub struct Cubes {
pub provided_len: u32,
cubes: Vec<Cube>,
}

impl Cubes {
pub fn parse(cube_data: &mut Vec<u8>, colour_data: &mut Vec<u8>) -> Result<Self, ()> {
// read first 4 bytes (cube count) from both arrays and make sure they match
let mut cube_buf = [0; 4];
let mut colour_buf = [0; 4];
let mut cube_slice = cube_data.as_slice();
let mut colour_slice = colour_data.as_slice();
if let Ok(len) = cube_slice.read(&mut cube_buf) {
if len != 4 {
//println!("Failed reading cube_data len");
return Err(());
}
} else {
//println!("Failed to read cube_data");
return Err(());
}
if let Ok(len) = colour_slice.read(&mut colour_buf) {
if len != 4 {
//println!("Failed reading colour_data len");
return Err(());
}
} else {
//println!("Failed to read colour_data");
return Err(());
}
if !(cube_buf[0] == colour_buf[0]
&& cube_buf[1] == colour_buf[1]
&& cube_buf[2] == colour_buf[2]
&& cube_buf[3] == colour_buf[3]) {
//println!("Values do not match");
return Err(());
}
let mut cube_i = 4;
let mut colour_i = 4;
let mut parsed_cubes = Vec::with_capacity(cube_data.len() / 8);
while cube_i < cube_data.len() && colour_i < colour_data.len() {
let mut new_cube = Cube::default();
if let Ok(cube_add) = new_cube.parse_cube_data(&mut cube_slice) {
if let Ok(colour_add) = new_cube.parse_colour_data(&mut colour_slice) {
cube_i += cube_add;
colour_i += colour_add;
parsed_cubes.push(new_cube);
} else {
// colour_data read error
return Err(());
}
} else {
// cube_data read error
return Err(());
}
}
Ok(Self {
provided_len: u32::from_le_bytes(cube_buf),
cubes: parsed_cubes,
})
}
pub fn dump(&self) -> (Vec<u8>, Vec<u8>) {
let mut cube_buf = Vec::new();
let mut colour_buf = Vec::new();
cube_buf.extend(&self.provided_len.to_le_bytes());
colour_buf.extend(&self.provided_len.to_le_bytes());
for c in self.into_iter() {
cube_buf.extend(&c.dump_cube_data());
colour_buf.extend(&c.dump_colour_data());
}
(cube_buf, colour_buf)
}
pub fn len(&self) -> usize {
self.cubes.len()
}
}

impl<'a> std::iter::IntoIterator for &'a Cubes {
type Item = &'a Cube;
type IntoIter = std::slice::Iter<'a, Cube>;
fn into_iter(self) -> Self::IntoIter {
self.cubes.iter()
}
}

#[derive(Copy, Clone)]
pub struct Cube {
pub id: u32,
pub x: u8, // left to right
pub y: u8, // bottom to top
pub z: u8, // back to front
pub orientation: u8,
pub colour: u8,
}

impl Cube {
fn parse_cube_data(&mut self, reader: &mut &[u8]) -> Result<usize, ()> {
let mut buf = [0; 4];
// read cube id
if let Ok(len) = reader.read(&mut buf) {
if len != 4 {
return Err(());
}
self.id = u32::from_le_bytes(buf);
} else {
return Err(());
}
// read x, y, z, orientation
if let Ok(len) = reader.read(&mut buf) {
if len != 4 {
return Err(());
}
self.x = buf[0];
self.y = buf[1];
self.z = buf[2];
self.orientation = buf[3];
} else {
return Err(());
}
Ok(8)
}
fn parse_colour_data(&mut self, reader: &mut &[u8]) -> Result<usize, ()> {
let mut buf = [0; 4];
if let Ok(len) = reader.read(&mut buf) {
if len != 4 {
return Err(());
}
self.colour = buf[0];
} else {
return Err(());
}
Ok(4)
}
pub fn dump_cube_data(&self) -> [u8; 8] {
let id_buf = self.id.to_le_bytes();
[id_buf[0], id_buf[1], id_buf[2], id_buf[3], self.x, self.y, self.z, self.orientation]
}
pub fn dump_colour_data(&self) -> [u8; 4] {
[self.colour, self.x, self.y, self.z]
}
}

impl std::default::Default for Cube {
fn default() -> Self {
Self {
id: 0,
x: 0,
y: 0,
z: 0,
orientation: 0,
colour: 0,
}
}
}

impl std::convert::From<crate::robocraft::FactoryRobotGetInfo> for Cubes {
fn from(other: crate::robocraft::FactoryRobotGetInfo) -> Self {
let mut cube_buf = Vec::new();
let mut colour_buf = Vec::new();
decode_config_buf(other.cube_data, STANDARD, &mut cube_buf).unwrap();
decode_config_buf(other.colour_data, STANDARD, &mut colour_buf).unwrap();
Self::parse(&mut cube_buf, &mut colour_buf).unwrap()
}
}

impl std::convert::From<crate::robocraft::FactoryInfo<crate::robocraft::FactoryRobotGetInfo>> for Cubes {
fn from(other: crate::robocraft::FactoryInfo<crate::robocraft::FactoryRobotGetInfo>) -> Self {
Self::from(other.response)
}
}

impl std::string::ToString for Cube {
fn to_string(&self) -> String {
format!("{{x: {}, y: {}, z: {}}} ({})", self.x, self.y, self.z, self.id)
}
}

+ 3
- 0
src/robocraft/mod.rs View File

@@ -7,6 +7,9 @@ pub use self::factory_request_builder::{FactorySearchBuilder, FactoryMovementTyp
#[cfg(feature = "simple")]
pub(crate) use self::factory_json::{ListPayload};

mod cubes;
pub use self::cubes::{Cube, Cubes};

mod auth;
pub use self::auth::{ITokenProvider, DefaultTokenProvider};



+ 21
- 1
tests/robocraft_factory.rs View File

@@ -1,4 +1,5 @@
use libfj::robocraft;
use std::convert::From;

#[test]
fn robocraft_factory_api_init() -> Result<(), ()> {
@@ -80,7 +81,7 @@ async fn robocraft_factory_player_query() -> Result<(), ()> {
#[tokio::test]
async fn robocraft_factory_robot_query() -> Result<(), ()> {
let api = robocraft::FactoryAPI::new();
let result = api.get(6478345 /* featured robot id*/).await;
let result = api.get(6478345 /* featured robot id */).await;
assert!(result.is_ok());
let bot_info = result.unwrap();
assert_ne!(bot_info.response.item_name, "");
@@ -89,3 +90,22 @@ async fn robocraft_factory_robot_query() -> Result<(), ()> {
assert_ne!(bot_info.response.colour_data, "");
Ok(())
}

#[tokio::test]
async fn robocraft_factory_robot_cubes() -> Result<(), ()> {
let api = robocraft::FactoryAPI::new();
let result = api.get(6478345 /* featured robot id */).await;
assert!(result.is_ok());
let bot_info = result.unwrap();
let cubes = robocraft::Cubes::from(bot_info.clone());
println!("cube count: {} or {}", cubes.provided_len, cubes.len());
/*for c in cubes.into_iter() {
println!("Cube.to_string() -> `{}`", c.to_string());
}*/
let (cube_d, colour_d) = cubes.dump();
let cube_str = base64::encode(&cube_d);
let colour_str = base64::encode(&colour_d);
assert_eq!(cube_str, bot_info.response.cube_data);
assert_eq!(colour_str, bot_info.response.colour_data);
Ok(())
}

Loading…
Cancel
Save