From 24074a635469c63281ae893bc643bc7f4ee35baa Mon Sep 17 00:00:00 2001 From: Adam Salvail Date: Wed, 19 Sep 2018 23:43:22 -0400 Subject: [PATCH 1/7] Implements Room.findPath() --- screeps-game-api/src/constants.rs | 2 +- screeps-game-api/src/objects/impls/room.rs | 263 ++++++++++++++++++++- 2 files changed, 256 insertions(+), 9 deletions(-) diff --git a/screeps-game-api/src/constants.rs b/screeps-game-api/src/constants.rs index 180cccaf..9b3a1e0a 100644 --- a/screeps-game-api/src/constants.rs +++ b/screeps-game-api/src/constants.rs @@ -176,7 +176,7 @@ pub mod find { enum_from_primitive! { #[repr(i32)] - #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] + #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Deserialize)] pub enum Direction { Top = 1, TopRight = 2, diff --git a/screeps-game-api/src/objects/impls/room.rs b/screeps-game-api/src/objects/impls/room.rs index 0bb5ffc6..2b90f954 100644 --- a/screeps-game-api/src/objects/impls/room.rs +++ b/screeps-game-api/src/objects/impls/room.rs @@ -1,14 +1,34 @@ -use std::ops::Range; +use std::{ + marker::PhantomData, + mem, + ops::Range, +}; -use stdweb::unstable::TryInto; +use stdweb::{ + Reference, + unstable::TryInto, +}; -use { - constants::{Color, FindConstant, LookConstant, ReturnCode, StructureType, find::Exit}, - HasPosition, - memory::MemoryReference, - objects::{Room, RoomPosition, StructureController, StructureStorage, StructureTerminal}, - positions::LocalRoomName, +use constants::{ + Color, + Direction, + find::Exit, + FindConstant, + LookConstant, + ReturnCode, + StructureType, }; +use HasPosition; +use memory::MemoryReference; +use objects::{ + Room, + RoomPosition, + StructureController, + StructureStorage, + StructureTerminal +}; +use pathfinder::CostMatrix; +use positions::LocalRoomName; simple_accessors! { Room; @@ -21,6 +41,8 @@ simple_accessors! { // todo: visual } +scoped_thread_local!(static COST_CALLBACK: Box<(Fn(String, Reference) -> ())>); + impl Room { pub fn create_construction_site(&self, at: T, ty: StructureType) -> ReturnCode where @@ -99,6 +121,86 @@ impl Room { // unimplemented!() // } + pub fn find_path( + &self, + from_pos: &O, + to_pos: &T, + opts: FindOptions, + ) -> Vec + where + O: HasPosition, + T: HasPosition, + F: Fn(String, CostMatrix) -> (), + { + let from = from_pos.pos(); + let to = to_pos.pos(); + + // This callback is the one actually passed to JavaScript. + fn callback(room_name: String, cost_matrix: Reference) -> () { + COST_CALLBACK.with(|callback| callback(room_name, cost_matrix)) + } + + // User provided callback: rust String, CostMatrix -> () + let raw_callback = opts.cost_callback; + + // Wrapped user callback: rust String, Reference -> () + let callback_boxed = move |room_name, cost_matrix_ref| { + let cmatrix = CostMatrix { + inner: cost_matrix_ref, + lifetime: PhantomData, + }; + raw_callback(room_name, cmatrix); + }; + + // Type erased and boxed callback: no longer a type specific to the closure passed in, + // now unified as Box + let callback_type_erased: Box ()> = Box::new(callback_boxed); + + // Overwrite lifetime of box inside closure so it can be stuck in scoped_thread_local storage: + // now pretending to be static data so that it can be stuck in scoped_thread_local. This should + // be entirely safe because we're only sticking it in scoped storage and we control the only use + // of it, but it's still necessary because "some lifetime above the current scope but otherwise + // unknown" is not a valid lifetime to have PF_CALLBACK have. + let callback_lifetime_erased: Box = + unsafe { mem::transmute(callback_type_erased) }; + + let FindOptions { + ignore_creeps, + ignore_destructible_structures, + max_ops, + heuristic_weight, + serialize, + max_rooms, + range, + plain_cost, + swamp_cost, + .. + } = opts; + + // Store callback_lifetime_erased in PF_CALLBACK for the duration of the PathFinder call and + // make the call to PathFinder. + // + // See https://docs.rs/scoped-tls/0.1/scoped_tls/ + COST_CALLBACK.set(&callback_lifetime_erased, || { + let v = js!{ + return @{&self.as_ref()}.search(@{from.as_ref()}, @{to.as_ref()}, { + ignoreCreeps: @{ignore_creeps}, + ignoreDestructibleStructures: @{ignore_destructible_structures} + costCallback: @{callback}, + maxOps: @{max_ops}, + heuristicWeight: @{heuristic_weight}, + serialize: @{serialize}, + maxRooms: @{max_rooms}, + range: @{range}, + plainCost: @{plain_cost}, + swampCost: @{swamp_cost} + }); + }; + + v.try_into().unwrap() + }) + } + pub fn look_for_at(&self, ty: T, target: U) -> Vec where T: LookConstant, @@ -168,3 +270,148 @@ impl PartialEq for Room { } impl Eq for Room {} + +pub struct FindOptions +where + F: Fn(String, CostMatrix) -> () +{ + ignore_creeps: bool, + ignore_destructible_structures: bool, + cost_callback: F, + max_ops: u32, + heuristic_weight: f64, + serialize: bool, + max_rooms: u32, + range: u32, + plain_cost: u8, + swamp_cost: u8, +} + +impl Default for FindOptions ()> { + fn default() -> Self { + fn cost_matrix(_: String, _: CostMatrix) {} + + // TODO: should we fall back onto the game's default values, or is + // it alright to copy them here? + FindOptions { + ignore_creeps: false, + ignore_destructible_structures: false, + cost_callback: cost_matrix, + max_ops: 2000, + heuristic_weight: 1.2, + serialize: false, + max_rooms: 16, + range: 0, + plain_cost: 1, + swamp_cost: 5, + } + } +} + +impl FindOptions ()> { + /// Creates default SearchOptions + pub fn new() -> Self { + Self::default() + } +} + +impl<'a, F> FindOptions +where + F: Fn(String, CostMatrix) -> (), +{ + /// Sets whether the algorithm considers creeps as walkable. Default: False. + pub fn ignore_creeps(mut self, ignore: bool) -> Self { + self.ignore_creeps = ignore; + self + } + + /// Sets whether the algorithm considers destructible structure as + /// walkable. Default: False. + pub fn ignore_destructible_structures(mut self, ignore: bool) -> Self { + self.ignore_destructible_structures = ignore; + self + } + + /// Sets cost callback - default `|_, _| {}`. + pub fn cost_callback(self, cost_callback: F2) -> FindOptions + where + F2: Fn(String, CostMatrix) -> (), + { + let FindOptions { + ignore_creeps, + ignore_destructible_structures, + cost_callback: _, + max_ops, + heuristic_weight, + serialize, + max_rooms, + range, + plain_cost, + swamp_cost, + } = self; + FindOptions { + ignore_creeps, + ignore_destructible_structures, + cost_callback, + max_ops, + heuristic_weight, + serialize, + max_rooms, + range, + plain_cost, + swamp_cost, + } + } + + /// Sets maximum ops - default `2000`. + pub fn max_ops(mut self, ops: u32) -> Self { + self.max_ops = ops; + self + } + + /// Sets heuristic weight - default `1.2`. + pub fn heuristic_weight(mut self, weight: f64) -> Self { + self.heuristic_weight = weight; + self + } + + /// Sets whether the returned path should be passed to `Room.serializePath`. + pub fn serialize(mut self, s: bool) -> Self { + self.serialize = s; + self + } + + /// Sets maximum rooms - default `16`, max `16`. + pub fn max_rooms(mut self, rooms: u32) -> Self { + self.max_rooms = rooms; + self + } + + pub fn range(mut self, k: u32) -> Self { + self.range = k; + self + } + + /// Sets plain cost - default `1`. + pub fn plain_cost(mut self, cost: u8) -> Self { + self.plain_cost = cost; + self + } + + /// Sets swamp cost - default `5`. + pub fn swamp_cost(mut self, cost: u8) -> Self { + self.swamp_cost = cost; + self + } +} + +#[derive(Clone, Debug, Deserialize)] +pub struct Step { + x: u32, + y: u32, + dx: i32, + dy: i32, + direction: Direction +} + +js_deserializable!{Step} From af501250dc8e38a963ea65c75ca54e57f531ec08 Mon Sep 17 00:00:00 2001 From: Adam Salvail Date: Mon, 24 Sep 2018 18:49:44 -0400 Subject: [PATCH 2/7] Fixes use path --- screeps-game-api/src/objects/impls/room.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/screeps-game-api/src/objects/impls/room.rs b/screeps-game-api/src/objects/impls/room.rs index 2b90f954..dc06b236 100644 --- a/screeps-game-api/src/objects/impls/room.rs +++ b/screeps-game-api/src/objects/impls/room.rs @@ -18,9 +18,9 @@ use constants::{ ReturnCode, StructureType, }; -use HasPosition; use memory::MemoryReference; use objects::{ + HasPosition, Room, RoomPosition, StructureController, From 8e241d5f46fcdeb8d38cde94eec1ec5a9e9e1464 Mon Sep 17 00:00:00 2001 From: Adam Salvail Date: Mon, 24 Sep 2018 19:20:23 -0400 Subject: [PATCH 3/7] Support for overwriting the CostMatrix --- screeps-game-api/src/objects/impls/room.rs | 40 ++++++++++++---------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/screeps-game-api/src/objects/impls/room.rs b/screeps-game-api/src/objects/impls/room.rs index dc06b236..91f57bba 100644 --- a/screeps-game-api/src/objects/impls/room.rs +++ b/screeps-game-api/src/objects/impls/room.rs @@ -41,7 +41,7 @@ simple_accessors! { // todo: visual } -scoped_thread_local!(static COST_CALLBACK: Box<(Fn(String, Reference) -> ())>); +scoped_thread_local!(static COST_CALLBACK: Box Option>); impl Room { pub fn create_construction_site(&self, at: T, ty: StructureType) -> ReturnCode @@ -121,47 +121,47 @@ impl Room { // unimplemented!() // } - pub fn find_path( + pub fn find_path<'a, O, T, F>( &self, from_pos: &O, to_pos: &T, - opts: FindOptions, + opts: FindOptions<'a, F>, ) -> Vec where O: HasPosition, T: HasPosition, - F: Fn(String, CostMatrix) -> (), + F: Fn(String, CostMatrix) -> Option> + 'a, { let from = from_pos.pos(); let to = to_pos.pos(); // This callback is the one actually passed to JavaScript. - fn callback(room_name: String, cost_matrix: Reference) -> () { + fn callback(room_name: String, cost_matrix: Reference) -> Option { COST_CALLBACK.with(|callback| callback(room_name, cost_matrix)) } - // User provided callback: rust String, CostMatrix -> () + // User provided callback: rust String, CostMatrix -> Option let raw_callback = opts.cost_callback; - // Wrapped user callback: rust String, Reference -> () + // Wrapped user callback: rust String, Reference -> Option let callback_boxed = move |room_name, cost_matrix_ref| { let cmatrix = CostMatrix { inner: cost_matrix_ref, lifetime: PhantomData, }; - raw_callback(room_name, cmatrix); + raw_callback(room_name, cmatrix).map(|cm| cm.inner) }; // Type erased and boxed callback: no longer a type specific to the closure passed in, // now unified as Box - let callback_type_erased: Box ()> = Box::new(callback_boxed); + let callback_type_erased: Box Option + 'a> = Box::new(callback_boxed); // Overwrite lifetime of box inside closure so it can be stuck in scoped_thread_local storage: // now pretending to be static data so that it can be stuck in scoped_thread_local. This should // be entirely safe because we're only sticking it in scoped storage and we control the only use // of it, but it's still necessary because "some lifetime above the current scope but otherwise // unknown" is not a valid lifetime to have PF_CALLBACK have. - let callback_lifetime_erased: Box = + let callback_lifetime_erased: Box Option + 'static> = unsafe { mem::transmute(callback_type_erased) }; let FindOptions { @@ -271,9 +271,9 @@ impl PartialEq for Room { impl Eq for Room {} -pub struct FindOptions +pub struct FindOptions<'a, F> where - F: Fn(String, CostMatrix) -> () + F: Fn(String, CostMatrix) -> Option> { ignore_creeps: bool, ignore_destructible_structures: bool, @@ -287,9 +287,11 @@ where swamp_cost: u8, } -impl Default for FindOptions ()> { +impl Default for FindOptions<'static, fn(String, CostMatrix) -> Option>> { fn default() -> Self { - fn cost_matrix(_: String, _: CostMatrix) {} + fn cost_matrix(_: String, _: CostMatrix) -> Option> { + None + } // TODO: should we fall back onto the game's default values, or is // it alright to copy them here? @@ -308,16 +310,16 @@ impl Default for FindOptions ()> { } } -impl FindOptions ()> { +impl FindOptions<'static, fn(String, CostMatrix) -> Option>> { /// Creates default SearchOptions pub fn new() -> Self { Self::default() } } -impl<'a, F> FindOptions +impl<'a, F> FindOptions<'a, F> where - F: Fn(String, CostMatrix) -> (), + F: Fn(String, CostMatrix) -> Option>, { /// Sets whether the algorithm considers creeps as walkable. Default: False. pub fn ignore_creeps(mut self, ignore: bool) -> Self { @@ -333,9 +335,9 @@ where } /// Sets cost callback - default `|_, _| {}`. - pub fn cost_callback(self, cost_callback: F2) -> FindOptions + pub fn cost_callback<'b, F2>(self, cost_callback: F2) -> FindOptions<'b, F2> where - F2: Fn(String, CostMatrix) -> (), + F2: Fn(String, CostMatrix) -> Option>, { let FindOptions { ignore_creeps, From 84878192ff144bc93561d752095212664e3b824b Mon Sep 17 00:00:00 2001 From: Adam Salvail Date: Mon, 24 Sep 2018 19:41:47 -0400 Subject: [PATCH 4/7] Supports return value of both serialized and vectorized path --- screeps-game-api/src/objects/impls/room.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/screeps-game-api/src/objects/impls/room.rs b/screeps-game-api/src/objects/impls/room.rs index 91f57bba..00d36318 100644 --- a/screeps-game-api/src/objects/impls/room.rs +++ b/screeps-game-api/src/objects/impls/room.rs @@ -126,7 +126,7 @@ impl Room { from_pos: &O, to_pos: &T, opts: FindOptions<'a, F>, - ) -> Vec + ) -> Path where O: HasPosition, T: HasPosition, @@ -196,8 +196,11 @@ impl Room { swampCost: @{swamp_cost} }); }; - - v.try_into().unwrap() + if serialize { + Path::SerializedPath(v.try_into().unwrap()) + } else { + Path::VectorizedPath(v.try_into().unwrap()) + } }) } @@ -417,3 +420,11 @@ pub struct Step { } js_deserializable!{Step} + +#[derive(Debug, Deserialize)] +pub enum Path { + VectorizedPath(Vec), + SerializedPath(String), +} + +js_deserializable!{Path} From a26c2876fd01b016dcd9481c9beacc9f23d5eb52 Mon Sep 17 00:00:00 2001 From: Adam Salvail Date: Mon, 24 Sep 2018 21:07:13 -0400 Subject: [PATCH 5/7] Removed Path stutter --- screeps-game-api/src/objects/impls/room.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/screeps-game-api/src/objects/impls/room.rs b/screeps-game-api/src/objects/impls/room.rs index 00d36318..b84b080b 100644 --- a/screeps-game-api/src/objects/impls/room.rs +++ b/screeps-game-api/src/objects/impls/room.rs @@ -423,8 +423,8 @@ js_deserializable!{Step} #[derive(Debug, Deserialize)] pub enum Path { - VectorizedPath(Vec), - SerializedPath(String), + Vectorized(Vec), + Serialized(String), } js_deserializable!{Path} From b9b312f1294ae28d85dff94c2dceabaffb348341 Mon Sep 17 00:00:00 2001 From: Adam Salvail Date: Mon, 24 Sep 2018 21:35:37 -0400 Subject: [PATCH 6/7] Changed variants name and made the deserialization untagged --- screeps-game-api/src/objects/impls/room.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/screeps-game-api/src/objects/impls/room.rs b/screeps-game-api/src/objects/impls/room.rs index b84b080b..41c0f459 100644 --- a/screeps-game-api/src/objects/impls/room.rs +++ b/screeps-game-api/src/objects/impls/room.rs @@ -197,9 +197,9 @@ impl Room { }); }; if serialize { - Path::SerializedPath(v.try_into().unwrap()) + Path::Serialized(v.try_into().unwrap()) } else { - Path::VectorizedPath(v.try_into().unwrap()) + Path::Vectorized(v.try_into().unwrap()) } }) } @@ -422,6 +422,7 @@ pub struct Step { js_deserializable!{Step} #[derive(Debug, Deserialize)] +#[serde(untagged)] pub enum Path { Vectorized(Vec), Serialized(String), From b2b94332a5b67dc24631fdd4143462de7b395d97 Mon Sep 17 00:00:00 2001 From: Adam Salvail Date: Mon, 24 Sep 2018 21:36:02 -0400 Subject: [PATCH 7/7] Export enums' name into the objects namespace --- screeps-game-api/src/objects/impls/mod.rs | 14 ++++++++++++-- screeps-game-api/src/objects/mod.rs | 9 +++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/screeps-game-api/src/objects/impls/mod.rs b/screeps-game-api/src/objects/impls/mod.rs index 2520f372..f8640859 100644 --- a/screeps-game-api/src/objects/impls/mod.rs +++ b/screeps-game-api/src/objects/impls/mod.rs @@ -23,5 +23,15 @@ mod structure_terminal; mod structure_tower; mod tombstone; -pub use self::structure_controller::{Reservation, Sign}; -pub use self::structure_spawn::SpawnOptions; +pub use self::{ + room::{ + FindOptions, + Path, + Step, + }, + structure_controller::{ + Reservation, + Sign, + }, + structure_spawn::SpawnOptions, +}; diff --git a/screeps-game-api/src/objects/mod.rs b/screeps-game-api/src/objects/mod.rs index 7c6530b6..0801e29c 100644 --- a/screeps-game-api/src/objects/mod.rs +++ b/screeps-game-api/src/objects/mod.rs @@ -19,8 +19,13 @@ use {ResourceType, ReturnCode, StructureType, ConversionError}; mod impls; -pub use self::impls::SpawnOptions; -pub use self::impls::{Reservation, Sign}; +pub use self::{ + impls::{ + SpawnOptions, + Reservation, + Sign, + }, +}; reference_wrappers!( ConstructionSite,