Skip to content

Commit

Permalink
Make SearchResults::path_internal public. This is required so its res…
Browse files Browse the repository at this point in the history
…ult can be used with creep.move_by_path()
  • Loading branch information
smessmer committed Oct 18, 2021
1 parent 292a040 commit f82fbb1
Showing 1 changed file with 54 additions and 48 deletions.
102 changes: 54 additions & 48 deletions src/pathfinder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use std::convert::TryInto;

use crate::{CostMatrix, Position, RoomName, objects::RoomPosition};
use crate::{objects::RoomPosition, CostMatrix, Position, RoomName};
use js_sys::{Array, JsString, Object};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
Expand All @@ -34,11 +34,7 @@ extern "C" {
///
/// [Screeps documentation](https://docs.screeps.com/api/#PathFinder.search)
#[wasm_bindgen(static_method_of = PathFinder, js_name = search)]
fn search_internal(
origin: &RoomPosition,
goal: &JsValue,
options: &JsValue,
) -> SearchResults;
fn search_internal(origin: &RoomPosition, goal: &JsValue, options: &JsValue) -> SearchResults;
}

#[wasm_bindgen]
Expand All @@ -51,7 +47,10 @@ extern "C" {
/// Room callback, which should return a [`CostMatrix`], or
/// [`JsValue::FALSE`] to avoid pathing through a room.
#[wasm_bindgen(method, setter = roomCallback)]
pub fn room_callback(this: &JsSearchOptions, callback: &Closure<dyn FnMut(JsString) -> JsValue>);
pub fn room_callback(
this: &JsSearchOptions,
callback: &Closure<dyn FnMut(JsString) -> JsValue>,
);

/// Set the cost of moving on plains tiles during this pathfinder search.
/// Defaults to 1.
Expand Down Expand Up @@ -103,7 +102,7 @@ extern "C" {
/// Get the path that was found, an [`Array`] of [`RoomPosition`]. May be
/// incomplete.
#[wasm_bindgen(method, getter, js_name = path)]
fn path_internal(this: &SearchResults) -> Array;
pub fn path_internal(this: &SearchResults) -> Array;

/// The number of operations the pathfinding operation performed.
#[wasm_bindgen(method, getter)]
Expand All @@ -119,9 +118,8 @@ extern "C" {
}

impl SearchResults {
pub fn path (&self) -> Vec<Position> {
self
.path_internal()
pub fn path(&self) -> Vec<Position> {
self.path_internal()
.iter()
.map(|p| p.unchecked_into())
.map(|p: RoomPosition| p.into())
Expand All @@ -134,7 +132,7 @@ pub trait RoomCostResult: Into<JsValue> {}
pub enum MultiRoomCostResult {
CostMatrix(CostMatrix),
Impassable,
Default
Default,
}

impl RoomCostResult for MultiRoomCostResult {}
Expand All @@ -150,14 +148,14 @@ impl<'a> Into<JsValue> for MultiRoomCostResult {
match self {
MultiRoomCostResult::CostMatrix(m) => m.into(),
MultiRoomCostResult::Impassable => JsValue::from_bool(false),
MultiRoomCostResult::Default => JsValue::undefined()
MultiRoomCostResult::Default => JsValue::undefined(),
}
}
}

pub enum SingleRoomCostResult {
CostMatrix(CostMatrix),
Default
Default,
}

impl RoomCostResult for SingleRoomCostResult {}
Expand All @@ -172,7 +170,7 @@ impl<'a> Into<JsValue> for SingleRoomCostResult {
fn into(self) -> JsValue {
match self {
SingleRoomCostResult::CostMatrix(m) => m.into(),
SingleRoomCostResult::Default => JsValue::undefined()
SingleRoomCostResult::Default => JsValue::undefined(),
}
}
}
Expand All @@ -191,43 +189,41 @@ where
heuristic_weight: Option<f64>,
}

impl<F> SearchOptions<F> where F: FnMut(RoomName) -> MultiRoomCostResult,
impl<F> SearchOptions<F>
where
F: FnMut(RoomName) -> MultiRoomCostResult,
{
pub(crate) fn as_js_options<R>(self, callback: impl Fn(&JsSearchOptions) -> R) -> R {
let mut raw_callback = self.room_callback;

let mut owned_callback = move |room: RoomName| -> JsValue {
raw_callback(room).into()
};

let mut owned_callback = move |room: RoomName| -> JsValue { raw_callback(room).into() };
//
// Type erased and boxed callback: no longer a type specific to the closure
// passed in, now unified as &Fn
//

let callback_type_erased: &mut (dyn FnMut(RoomName) -> JsValue) = &mut owned_callback;

// Overwrite lifetime of reference so it can be passed to javascript.
// It's now pretending to be static data. This should be entirely safe
// because we control the only use of it and it remains valid during the
// pathfinder callback. This transmute is necessary because "some lifetime
// above the current scope but otherwise unknown" is not a valid lifetime.
//

let callback_lifetime_erased: &'static mut (dyn FnMut(RoomName) -> JsValue) = unsafe { std::mem::transmute(callback_type_erased) };

let callback_lifetime_erased: &'static mut (dyn FnMut(RoomName) -> JsValue) =
unsafe { std::mem::transmute(callback_type_erased) };
let boxed_callback = Box::new(move |room: JsString| -> JsValue {
let room = room.try_into().expect("expected room name in room callback");
let room = room
.try_into()
.expect("expected room name in room callback");

callback_lifetime_erased(room)
}) as Box<dyn FnMut(JsString) -> JsValue>;

let closure = Closure::wrap(boxed_callback);

//
// Create JS object and set properties.
//

let js_options = JsSearchOptions::new();

js_options.room_callback(&closure);
Expand All @@ -239,23 +235,18 @@ impl<F> SearchOptions<F> where F: FnMut(RoomName) -> MultiRoomCostResult,
if let Some(swamp_cost) = self.swamp_cost {
js_options.swamp_cost(swamp_cost);
}

if let Some(flee) = self.flee {
js_options.flee(flee);
}

if let Some(max_ops) = self.max_ops {
js_options.max_ops(max_ops);
}

if let Some(max_rooms) = self.max_rooms {
js_options.max_rooms(max_rooms);
}

if let Some(max_cost) = self.max_cost {
js_options.max_cost(max_cost);
}

if let Some(heuristic_weight) = self.heuristic_weight {
js_options.heuristic_weight(heuristic_weight);
}
Expand Down Expand Up @@ -374,15 +365,12 @@ where
#[wasm_bindgen]
pub struct SearchGoal {
pos: Position,
range: u32
range: u32,
}

impl SearchGoal {
pub fn new(pos: Position, range: u32) -> Self {
SearchGoal {
pos,
range
}
SearchGoal { pos, range }
}
}

Expand All @@ -396,36 +384,54 @@ impl SearchGoal {
#[wasm_bindgen(getter)]
pub fn range(&self) -> u32 {
self.range
}
}
}

pub fn search<F>(from: Position, to: Position, range: u32, options: Option<SearchOptions<F>>) -> SearchResults where F: FnMut(RoomName) -> MultiRoomCostResult {
pub fn search<F>(
from: Position,
to: Position,
range: u32,
options: Option<SearchOptions<F>>,
) -> SearchResults
where
F: FnMut(RoomName) -> MultiRoomCostResult,
{
let goal = SearchGoal {
pos: to.into(),
range
range,
};

let goal = JsValue::from(goal);

search_real(from, &goal, options)
}

pub fn search_many<F>(from: Position, to: impl Iterator<Item = SearchGoal>, options: Option<SearchOptions<F>>) -> SearchResults where F: FnMut(RoomName) -> MultiRoomCostResult {
let goals: Array = to
.map(|g| JsValue::from(g))
.collect();
pub fn search_many<F>(
from: Position,
to: impl Iterator<Item = SearchGoal>,
options: Option<SearchOptions<F>>,
) -> SearchResults
where
F: FnMut(RoomName) -> MultiRoomCostResult,
{
let goals: Array = to.map(|g| JsValue::from(g)).collect();

search_real(from, goals.as_ref(), options)
}

fn search_real<F>(from: Position, goal: &JsValue, options: Option<SearchOptions<F>>) -> SearchResults where F: FnMut(RoomName) -> MultiRoomCostResult {
fn search_real<F>(
from: Position,
goal: &JsValue,
options: Option<SearchOptions<F>>,
) -> SearchResults
where
F: FnMut(RoomName) -> MultiRoomCostResult,
{
let from = from.into();

if let Some(options) = options {
options.as_js_options(|js_options| {
PathFinder::search_internal(&from, goal, &js_options)
})
options.as_js_options(|js_options| PathFinder::search_internal(&from, goal, &js_options))
} else {
PathFinder::search_internal(&from, goal, &JsValue::UNDEFINED)
}
}
}

0 comments on commit f82fbb1

Please sign in to comment.