diff --git a/Cargo.toml b/Cargo.toml index c843138..4637c73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,11 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] +fern = "0.6" +getrandom = { version = "0.2", features = ["custom"] } js-sys = "0.3" log = "0.4" -fern = "0.6" +rand = "0.8" screeps-game-api = "0.10" wasm-bindgen = "0.2" web-sys = { version = "0.3", features = ["console"] } diff --git a/README.md b/README.md index 481023e..82d758b 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ cd screeps-starter-rust cp example-screeps.toml screeps.toml nano screeps.toml # configure credentials (API key) if you'd like to upload directly, -# or a directory to copy to if you'd prepfer to use the game client to deploy +# or a directory to copy to if you'd prefer to use the game client to deploy # build tool: cargo screeps --help diff --git a/src/lib.rs b/src/lib.rs index c906583..f8ff735 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,9 @@ use std::cell::RefCell; -use std::collections::{HashMap, hash_map::Entry}; +use std::collections::{hash_map::Entry, HashMap}; +use getrandom::register_custom_getrandom; use log::*; +use rand::prelude::*; use screeps::{ find, game, prelude::*, Creep, ObjectId, Part, ResourceType, ReturnCode, RoomObjectProperties, Source, StructureController, StructureObject, @@ -10,6 +12,16 @@ use wasm_bindgen::prelude::*; mod logging; +// implement a custom randomness generator for the getrandom crate, +// because the `js` feature expects the Node.js WebCrypto API to be available +// (it's not available in the Screeps Node.js environment) +fn custom_getrandom(buf: &mut [u8]) -> Result<(), getrandom::Error> { + let mut rng = StdRng::seed_from_u64(js_sys::Math::random().to_bits()); + rng.fill_bytes(buf); + Ok(()) +} +register_custom_getrandom!(custom_getrandom); + // add wasm_bindgen to any function you would like to expose for call from js #[wasm_bindgen] pub fn setup() { @@ -22,7 +34,10 @@ thread_local! { static CREEP_TARGETS: RefCell> = RefCell::new(HashMap::new()); } -// this enum will represent a creep's lock on a specific target object, storing a js reference to the object id so that we can grab a fresh reference to the object each successive tick, since screeps game objects become 'stale' and shouldn't be used beyond the tick they were fetched +// this enum will represent a creep's lock on a specific target object, storing a js +// reference to the object id so that we can grab a fresh reference to the object each +// successive tick, since screeps game objects become 'stale' and shouldn't be used beyond +// the tick they were fetched #[derive(Clone)] enum CreepTarget { Upgrade(ObjectId), @@ -38,22 +53,12 @@ pub fn game_loop() { CREEP_TARGETS.with(|creep_targets_refcell| { let mut creep_targets = creep_targets_refcell.borrow_mut(); debug!("running creeps"); - // same type conversion (and type assumption) as the spawn loop for creep in game::creeps().values() { run_creep(&creep, &mut creep_targets); } }); debug!("running spawns"); - // Game::spawns returns a `js_sys::Object`, which is a light reference to an - // object of any kind which is held on the javascript heap. - // - // Object::values returns a `js_sys::Array`, which contains the member spawn objects - // representing all the spawns you control. - // - // They are returned as wasm_bindgen::JsValue references, which we can safely - // assume are StructureSpawn objects as returned from js without checking first - let mut additional = 0; for spawn in game::spawns().values() { debug!("running spawn {}", String::from(spawn.name())); @@ -61,17 +66,14 @@ pub fn game_loop() { if spawn.room().unwrap().energy_available() >= body.iter().map(|p| p.cost()).sum() { // create a unique name, spawn. let name_base = game::time(); - let name = format!("{}-{}", name_base, additional); + let name = format!("{}-{}", name_base, random::()); // note that this bot has a fatal flaw; spawning a creep // creates Memory.creeps[creep_name] which will build up forever; // these memory entries should be prevented (todo doc link on how) or cleaned up let res = spawn.spawn_creep(&body, &name); - // todo once fixed in branch this should be ReturnCode::Ok instead of this i8 grumble grumble if res != ReturnCode::Ok { warn!("couldn't spawn: {:?}", res); - } else { - additional += 1; } } } @@ -88,10 +90,12 @@ fn run_creep(creep: &Creep, creep_targets: &mut HashMap) { let target = creep_targets.entry(name); match target { - Entry::Occupied(entry) => { + Entry::Occupied(entry) => { let creep_target = entry.get(); match creep_target { - CreepTarget::Upgrade(controller_id) if creep.store().get_used_capacity(Some(ResourceType::Energy)) > 0 => { + CreepTarget::Upgrade(controller_id) + if creep.store().get_used_capacity(Some(ResourceType::Energy)) > 0 => + { if let Some(controller) = controller_id.resolve() { let r = creep.upgrade_controller(&controller); if r == ReturnCode::NotInRange { @@ -104,7 +108,9 @@ fn run_creep(creep: &Creep, creep_targets: &mut HashMap) { entry.remove(); } } - CreepTarget::Harvest(source_id) if creep.store().get_free_capacity(Some(ResourceType::Energy)) > 0 => { + CreepTarget::Harvest(source_id) + if creep.store().get_free_capacity(Some(ResourceType::Energy)) > 0 => + { if let Some(source) = source_id.resolve() { if creep.pos().is_near_to(source.pos()) { let r = creep.harvest(&source); @@ -118,8 +124,10 @@ fn run_creep(creep: &Creep, creep_targets: &mut HashMap) { } else { entry.remove(); } - }, - _ => { entry.remove(); } + } + _ => { + entry.remove(); + } }; } Entry::Vacant(entry) => {