diff --git a/src/constants.rs b/src/constants.rs index 4da412b1..dff18b30 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -52,7 +52,6 @@ pub(crate) mod macros { } } impl std::str::FromStr for $ty { - // add a new error type for this - or something type Err = InvalidConstantString; fn from_str(s: &str) -> Result { diff --git a/src/constants/numbers.rs b/src/constants/numbers.rs index 5429a8f6..bbf0579d 100644 --- a/src/constants/numbers.rs +++ b/src/constants/numbers.rs @@ -504,16 +504,8 @@ pub const LAB_UNBOOST_MINERAL: u32 = 15; /// Exponential growth rate of control points needed per global control level /// (GCL). -/// -/// See [`control_points_for_gcl`] function to calculate for each level -/// -/// [`control_points_for_gcl`]: crate::constants::math::control_points_for_gcl pub const GCL_POW: f64 = 2.4; /// Base growth rate of control points needed per global control level (GCL). -/// -/// See [`control_points_for_gcl`] function to calculate for each level -/// -/// [`control_points_for_gcl`]: crate::constants::math::control_points_for_gcl pub const GCL_MULTIPLY: u32 = 1_000_000; /// Maximum GCL for players allowed to spawn in a Novice area. pub const GCL_NOVICE: u32 = 3; @@ -740,17 +732,9 @@ pub const SIGN_PLANNED_AREA: &str = "A new Novice or Respawn Area is being plann // EVENT_* constants in src/objects/impls/room.rs /// Base growth rate of processed power needed per global power level (GPL). -/// -/// See [`power_for_gpl`] function to calculate for each level -/// -/// [`power_for_gpl`]: crate::constants::math::power_for_gpl pub const POWER_LEVEL_MULTIPLY: u32 = 1000; /// Exponential growth rate of processed power needed per global power level /// (GPL). -/// -/// See [`power_for_gpl`] function to calculate for each level -/// -/// [`power_for_gpl`]: crate::constants::math::power_for_gpl pub const POWER_LEVEL_POW: u32 = 2; /// Time, in milliseconds, that a power creep must wait to respawn after dying. pub const POWER_CREEP_SPAWN_COOLDOWN: u32 = 8 * 3600 * 1000; diff --git a/src/game.rs b/src/game.rs index e9bf8bfb..4be905bf 100644 --- a/src/game.rs +++ b/src/game.rs @@ -117,7 +117,7 @@ pub fn resources() -> JsHashMap { Game::resources().into() } -/// Get an [`JsHashMap`] with the rooms visible for the current +/// Get a [`JsHashMap`] with the rooms visible for the current /// tick. /// /// [Screeps documentation](https://docs.screeps.com/api/#Game.rooms) @@ -125,7 +125,7 @@ pub fn rooms() -> JsHashMap { Game::rooms().into() } -/// Get an [`JsHashMap`] with all of your spawns, which +/// Get a [`JsHashMap`] with all of your spawns, which /// has spawn names as keys. /// /// [Screeps documentation](https://docs.screeps.com/api/#Game.spawns) @@ -133,7 +133,7 @@ pub fn spawns() -> JsHashMap { Game::spawns().into() } -/// Get an [`JsHashMap`] with all of your owned +/// Get a [`JsHashMap`] with all of your owned /// structures. /// /// [Screeps documentation](https://docs.screeps.com/api/#Game.spawns) @@ -205,8 +205,10 @@ pub fn get_object_by_id_erased(id: &RawObjectId) -> Option { Game::get_object_by_id(&js_str) } -/// Send an email message to yourself with a given message. Set a group -/// interval to only send messages every `group_interval` minutes. +/// Send an email message to yourself with a given message. +/// +/// Set a `group_interval` with a limit, in minutes, on how frequently emails +/// are allowed to be sent. /// /// [Screeps documentation](https://docs.screeps.com/api/#Game.notify) pub fn notify(message: &str, group_interval: Option) { diff --git a/src/js_collections.rs b/src/js_collections.rs index d0adeecf..554335b4 100644 --- a/src/js_collections.rs +++ b/src/js_collections.rs @@ -35,6 +35,8 @@ pub trait JsCollectionFromValue { fn from_value(val: JsValue) -> Self; } +/// Container holding a reference to an [`Object`] in JavaScript as well as +/// expected types for both the keys and values. pub struct JsHashMap { map: Object, _phantom: PhantomData<(K, V)>, @@ -161,16 +163,19 @@ impl std::iter::FusedIterator for OwnedArrayIter where T: JsCollectionFrom impl std::iter::ExactSizeIterator for OwnedArrayIter where T: JsCollectionFromValue {} -/// Represents a reference to an Object ID string held on the javascript heap -/// and a type that the ID points to. +/// Represents a reference to an Object ID string in JavaScript memory, typed +/// according to the object type Rust expects for the object after resolving. /// -/// This representation is less useful on the Rust side due to lack of -/// visibility on the underlying string and lack of most trait implementations, -/// and consumes more memory, but is faster to resolve and may be useful with -/// objects you plan to resolve frequently. +/// Use [`ObjectId`] if a value stored in Rust memory is preferred; the +/// JavaScript representation can be harder to work with in Rust code due to +/// lack of visibility on the underlying string and lack of most trait +/// implementations, and consumes more memory, but is faster to resolve and may +/// be useful with objects you plan to resolve frequently. /// /// This object ID is typed, but not strictly, and can be converted into /// referring into another type of object with [`JsObjectId::into_type`]. +/// +/// [`ObjectId`]: crate::local::ObjectId // Copy, Clone, Debug, PartialEq, Eq, Hash, PartialEq, Eq implemented manually // below pub struct JsObjectId { diff --git a/src/lib.rs b/src/lib.rs index 2efe35d8..188b00de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,52 @@ -//! Typed bindings to the Screeps in-game API for WASM Rust AIs. +//! Typed bindings to the Screeps: World in-game API for WASM Rust AIs. +//! +//! # Performance +//! +//! Due to the limited CPU available to the player in Screeps: World, the +//! performance implications of running WebAssembly are good to be aware of. +//! +//! WebAssembly instances have a dedicated linear memory, and data being passed +//! in or out must be copied. Additionally, Rust uses UTF-8 strings while +//! JavaScript uses UTF-16 strings, so any strings being copied must also be +//! converted between encodings. +//! +//! Additionally, compile time for the `WebAssembly.Module` can be considerable, +//! requiring a lot of CPU time on each initial startup tick (and potentially +//! requiring skipped ticks for larger bots). However, global resets are very +//! infrequent in most environments, so the impact of this isn't severe. +//! +//! After compilation, the WebAssembly environment has near-native performance, +//! and no Javascript garbage collection happening for its memory space, +//! allowing for faster execution of some types of workloads. Overall, the +//! advantages and disadvantages of WebAssembly in Screeps are relatively small, +//! especially when compared to the relatively high 0.2ms cost of game actions. +//! +//! # Data Persistence +//! +//! In the Screeps: World JavaScript environment, the `Memory` object is the +//! typical way to store data in a way that persists through the environment +//! resets that happen occasionally, either triggered by deploying a new version +//! of your code or due to natural expiration in the server. It provides a +//! wrapper that automatically deserializes the contents of `RawMemory` via the +//! `JSON.parse()` JavaScript function when accessed for the first time each +//! tick, then gets serialized by `JSON.stringify()` at the end of the tick. +//! +//! Using this untyped `Memory` object (or the reference to a part of it, which +//! can be obtained from the `memory` function on various game objects) from +//! within WebAssembly can be awkward, but is recommended if you need to +//! maintain compatibility with the default `Memory` object. +//! +//! An alternative that you may prefer is to use `RawMemory` instead, fetching +//! the stored data in string format using [`raw_memory::get`] and deserializing +//! within WebAssembly using [`serde`] or another serializion approach, then +//! serializing and using [`raw_memory::set`] to store the data. +//! +//! If you choose the `RawMemory` approach, be aware that some game methods +//! (notably [`StructureSpawn::spawn_creep`] and [`Creep::move_to`]) directly +//! store data in the `Memory` object; replacing the 'special' `Memory` object +//! with one that doesn't attempt to deserialize the contents of `RawMemory` may +//! be advisable if you're using it directly (note that this needs to be done +//! each tick to be effective). //! //! # Cargo Features //! diff --git a/src/local/cost_matrix.rs b/src/local/cost_matrix.rs index 32c6268c..14311e60 100644 --- a/src/local/cost_matrix.rs +++ b/src/local/cost_matrix.rs @@ -9,6 +9,10 @@ use crate::{ use super::{linear_index_to_xy, xy_to_linear_index, Position, RoomXY, ROOM_AREA}; +/// A matrix of pathing costs for a room, stored in Rust memory. +/// +/// Use [`CostMatrix`] if a reference to data stored in JavaScript memory is +/// preferred. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(transparent)] pub struct LocalCostMatrix { diff --git a/src/local/object_id.rs b/src/local/object_id.rs index 514371e6..e08014d4 100644 --- a/src/local/object_id.rs +++ b/src/local/object_id.rs @@ -18,18 +18,16 @@ mod raw; pub use errors::*; pub use raw::*; -/// Represents an Object ID and a type that the ID points to. +/// Represents an Object ID and a type that the ID points to, stored in Rust +/// memory. /// -/// Each object id in screeps is represented by a Mongo GUID, which, -/// while not guaranteed, is unlikely to change. This takes advantage of that by -/// storing a packed representation of 12 bytes. +/// Use [`JsObjectId`] if a reference stored in JavaScript memory is preferred. /// -/// This object ID is typed, but not strictly. It's completely safe to create an -/// ObjectId with an incorrect type, and all operations which use the type will -/// double-check at runtime. -/// -/// With that said, using this can provide nice type inference, and should have -/// few disadvantages to the lower-level alternative, [`RawObjectId`]. +/// Each object ID in Screeps: World is represented by an ID of up to 24 +/// hexidemical characters, which cannot change. This implementation takes +/// advantage of that by storing a packed representation in a `u128`, using 96 +/// bits for the ID and 32 bits for tracking the length of the ID string for +/// reconstruction in JS. /// /// # Conversion /// @@ -55,6 +53,7 @@ pub use raw::*; /// accuracy, these ids will be sorted by object creation time. /// /// [`BTreeMap`]: std::collections::BTreeMap +/// [`JsObjectId`]: crate::js_collections::JsObjectId /// [1]: https://docs.mongodb.com/manual/reference/method/ObjectId/ // Copy, Clone, Debug, PartialEq, Eq, Hash, PartialEq, Eq implemented manually below #[derive(Serialize, Deserialize)] diff --git a/src/local/position.rs b/src/local/position.rs index 466beaf1..a14a885e 100644 --- a/src/local/position.rs +++ b/src/local/position.rs @@ -20,13 +20,11 @@ mod game_methods; mod pair_utils; mod world_utils; -/// Represents a position in a particular room in Screeps. +/// Represents a position in a particular room in Screeps, stored in Rust +/// memory. /// -/// **Note:** This is analogous to the `RoomPosition` JavaScript type. -/// -/// We've renamed this type to `Position` in `screeps-game-api` to reflect the -/// fact that it's implemented entirely as a local type, and does represent a -/// position located within an entire shard, not only within a single room. +/// Use [`RoomPosition`] if a reference to an object stored in JavaScript memory +/// is preferred. /// /// This should be a very efficient type to use in most if not all situations. /// It's represented by a single `u32`, all math operations are implemented in @@ -112,7 +110,7 @@ mod world_utils; /// /// # Method Behavior /// -/// While this corresponds with the JavaScript `RoomPosition` type, it is not +/// While this corresponds with the JavaScript [`RoomPosition`] type, it is not /// identical. In particular, all "calculation" methods which take in another /// position are re-implemented in pure Rust code, and some behave slightly /// different. diff --git a/src/local/room_coordinate.rs b/src/local/room_coordinate.rs index f32cc4f8..e9d54e46 100644 --- a/src/local/room_coordinate.rs +++ b/src/local/room_coordinate.rs @@ -19,6 +19,9 @@ impl Error for OutOfBoundsError {} /// Converts a [`RoomXY`] coordinate pair to a linear index appropriate for use /// with the internal representation of a [`CostMatrix`] or [`LocalCostMatrix`]. +/// +/// [`CostMatrix`]: crate::objects::CostMatrix +/// [`LocalCostMatrix`]: crate::local::LocalCostMatrix #[inline] pub const fn xy_to_linear_index(xy: RoomXY) -> usize { xy.x.u8() as usize * ROOM_SIZE as usize + xy.y.u8() as usize @@ -27,6 +30,9 @@ pub const fn xy_to_linear_index(xy: RoomXY) -> usize { /// Converts a linear index from the internal representation of a [`CostMatrix`] /// or [`LocalCostMatrix`] to a [`RoomXY`] coordinate pair for the position the /// index represents. +/// +/// [`CostMatrix`]: crate::objects::CostMatrix +/// [`LocalCostMatrix`]: crate::local::LocalCostMatrix #[inline] pub fn linear_index_to_xy(idx: usize) -> RoomXY { assert!(idx < ROOM_AREA, "Out of bounds index: {idx}"); @@ -39,6 +45,9 @@ pub fn linear_index_to_xy(idx: usize) -> RoomXY { /// Converts a [`RoomXY`] coordinate pair to a terrain index appropriate for use /// with the internal representation of [`RoomTerrain`] or [`LocalRoomTerrain`]. +/// +/// [`RoomTerrain`]: crate::objects::RoomTerrain +/// [`LocalRoomTerrain`]: crate::local::LocalRoomTerrain #[inline] pub const fn xy_to_terrain_index(xy: RoomXY) -> usize { xy.y.u8() as usize * ROOM_SIZE as usize + xy.x.u8() as usize @@ -47,6 +56,9 @@ pub const fn xy_to_terrain_index(xy: RoomXY) -> usize { /// Converts a terrain index from the internal representation of a /// [`RoomTerrain`] or [`LocalRoomTerrain`] to a [`RoomXY`] coordinate pair for /// the position the index represents. +/// +/// [`RoomTerrain`]: crate::objects::RoomTerrain +/// [`LocalRoomTerrain`]: crate::local::LocalRoomTerrain #[inline] pub fn terrain_index_to_xy(idx: usize) -> RoomXY { assert!(idx < ROOM_AREA, "Out of bounds index: {idx}"); @@ -57,6 +69,8 @@ pub fn terrain_index_to_xy(idx: usize) -> RoomXY { } } +/// An X or Y coordinate in a room, restricted to the valid range of +/// coordinates. #[derive( Debug, Hash, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, )] @@ -161,6 +175,7 @@ impl fmt::Display for RoomCoordinate { } } +/// An X/Y pair representing a given coordinate relative to any room. #[derive(Debug, Default, Hash, Clone, Copy, PartialEq, Eq)] pub struct RoomXY { pub x: RoomCoordinate, diff --git a/src/local/terrain.rs b/src/local/terrain.rs index c742a368..23a81ef8 100644 --- a/src/local/terrain.rs +++ b/src/local/terrain.rs @@ -11,6 +11,9 @@ pub struct LocalRoomTerrain { bits: Box<[u8; ROOM_AREA]>, } +/// A matrix representing the terrain of a room, stored in Rust memory. +/// +/// Use [`RoomTerrain`] if data stored in JavaScript memory is preferred. impl LocalRoomTerrain { /// Gets the terrain at the specified position in this room. pub fn get(&self, xy: RoomXY) -> Terrain { diff --git a/src/objects/impls/cost_matrix.rs b/src/objects/impls/cost_matrix.rs index 41f219b5..eb7d74fa 100644 --- a/src/objects/impls/cost_matrix.rs +++ b/src/objects/impls/cost_matrix.rs @@ -9,7 +9,11 @@ use crate::{ #[wasm_bindgen] extern "C" { - /// An object representing a [`CostMatrix`] held in the javascript heap. + /// A reference to a matrix of pathing costs for a room, stored in + /// JavaScript memory. + /// + /// Use [`LocalCostMatrix`] to store and access the same data in Rust + /// memory. /// /// [Screeps documentation](https://docs.screeps.com/api/#PathFinder-CostMatrix) #[wasm_bindgen(js_namespace = PathFinder)] diff --git a/src/objects/impls/room_position.rs b/src/objects/impls/room_position.rs index 7607e103..974b9bfc 100644 --- a/src/objects/impls/room_position.rs +++ b/src/objects/impls/room_position.rs @@ -15,7 +15,10 @@ use crate::{ #[wasm_bindgen] extern "C" { - /// An object representing a position in a room. + /// An object representing a position in a room, stored in JavaScript + /// memory. + /// + /// Use [`Position`] to store and access the same data in Rust memory. /// /// [Screeps documentation](https://docs.screeps.com/api/#RoomPosition) pub type RoomPosition; diff --git a/src/objects/impls/room_terrain.rs b/src/objects/impls/room_terrain.rs index 6b61cccc..c1fc7363 100644 --- a/src/objects/impls/room_terrain.rs +++ b/src/objects/impls/room_terrain.rs @@ -9,7 +9,11 @@ use crate::{ #[wasm_bindgen] extern "C" { - /// An object representing a room's terrain held in the javascript heap. + /// A matrix representing the terrain of a room, held in the JavaScript + /// heap. + /// + /// Use [`LocalRoomTerrain`] to store and access the same data in Rust + /// memory. /// /// [Screeps documentation](https://docs.screeps.com/api/#Room-Terrain) #[wasm_bindgen(js_namespace = Room, js_name = Terrain)]