diff --git a/javascript/main.js b/javascript/main.js index 532a6ec..87c2229 100644 --- a/javascript/main.js +++ b/javascript/main.js @@ -36,8 +36,10 @@ module.exports.loop = function () { Game.cpu.halt(); } else { try { + // Capture current list of creeps + const creeps = Object.values(Game.creeps); if (wasm_module) { - wasm_module.loop(); + wasm_module.loop(creeps); } else { // attempt to load the wasm only if there's enough bucket to do a bunch of work this tick if (Game.cpu.bucket < 750) { @@ -49,7 +51,7 @@ module.exports.loop = function () { // load the wasm instance! wasm_module.initialize_instance(); // go ahead and run the loop for its first tick - wasm_module.loop(); + wasm_module.loop(creeps); } } catch (error) { console.error("caught exception:", error); diff --git a/src/lib.rs b/src/lib.rs index 3417402..c1f2a8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use std::{ collections::{hash_map::Entry, HashMap, HashSet}, }; +use crate::types::Creep; use js_sys::{JsString, Object, Reflect}; use log::*; use screeps::{ @@ -10,12 +11,13 @@ use screeps::{ enums::StructureObject, find, game, local::ObjectId, - objects::{Creep, Source, StructureController}, + objects::{Source, StructureController}, prelude::*, }; use wasm_bindgen::{prelude::*, JsCast}; mod logging; +mod types; // this is one way to persist data between ticks within Rust's memory, as opposed to // keeping state in memory on game objects - but will be lost on global resets! @@ -35,9 +37,12 @@ enum CreepTarget { } // add wasm_bindgen to any function you would like to expose for call from js -// to use a reserved name as a function name, use `js_name`: +// to use a reserved name as a function name, use `js_name`. +// You can pass arguments from JS. For example, here we are passing in the list of creeps +// as of the start of the tick and since we know we haven't spawned any creeps yet this tick +// we can use our custom Creep type instead of screeps::Creep. #[wasm_bindgen(js_name = loop)] -pub fn game_loop() { +pub fn game_loop(creeps: Vec) { INIT_LOGGING.call_once(|| { // show all output of Info level, adjust as needed logging::setup_logging(logging::Info); @@ -50,7 +55,7 @@ pub fn game_loop() { CREEP_TARGETS.with(|creep_targets_refcell| { let mut creep_targets = creep_targets_refcell.borrow_mut(); debug!("running creeps"); - for creep in game::creeps().values() { + for creep in creeps { run_creep(&creep, &mut creep_targets); } }); diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..d249479 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,29 @@ +use js_sys::JsString; +use screeps::HasId; +use wasm_bindgen::prelude::wasm_bindgen; + +// The Rust mappings of the JS Types can be extended to modify their functionality. +// Here, we create a custom Creep type we can use when we know what a Creep definitely has an ID. +// Each tick, JS will pass in a list of Creeps which we will convert to this custom type since +// the only way a creep could *not* have an ID is if it was just spawned this tick, and by passing +// in the creeps from JS before our bot code runs, we know that we have not spawned any new creeps yet +#[wasm_bindgen] +extern "C" { + // By specifying `extends`, wasm_bindgen will implement Deref for the base type, + // thus anything a screeps::Creep can do, so can our custom type + #[wasm_bindgen(extends = screeps::Creep)] + pub type Creep; + + // Define our own id_internal method instead of screeps::Creep::id_internal since we + // know that Creeps represented by this type always have an ID + #[wasm_bindgen(method, getter = id)] + fn id_internal(this: &Creep) -> JsString; +} + +// Implement HasId for our custom creep type (instead of MaybeHasId) since this type is used +// for creeps that are known to have an ID. +impl HasId for Creep { + fn js_raw_id(&self) -> JsString { + self.id_internal() + } +}