Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

September 2018 game update #71

Merged
merged 9 commits into from
Oct 6, 2018
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions screeps-game-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ log = "0.4"
num-traits = "0.2"
serde = "1"
serde_derive = "1"
serde_json = "1"
scoped-tls = "0.1"
stdweb = "0.4"
stdweb-derive = "0.5"
Expand Down
9 changes: 0 additions & 9 deletions screeps-game-api/javascript/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,6 @@ function __resource_type_str_to_num(str) {
}
}

function __terrain_type_str_to_num(str) {
switch (str) {
case "plain": return 0;
case "wall": return 1;
case "swamp": return 2;
default: throw new Error("unknown terrain type " + str);
}
}

function _hasActiveBodypart(body, type) {
for (var i = body.length - 1; i >= 0; i--) {
if (body[i].hits <= 0) {
Expand Down
18 changes: 5 additions & 13 deletions screeps-game-api/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ pub enum Terrain {
Plain = 0,
Wall = 1,
Swamp = 2,
SwampWall = 3,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it is right now I don't see a reason to include this separately from Wall.

It makes no gameplay difference whether a wall has a swamp underneath it or not, and forcing consumers of Terrain to consider that there is a difference seems a bit wrong?

o4kapuk has also mentioned on slack that the official server has gone through and replaced all terrain 3 tiles with terrain 1, and that the screeps team is working on a patch for the private server to do a similar thing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't know that. Fair enough, I'll remove it.

Lava = 4,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be necessary? I'm against including if if it doesn't occur just because it forces any consumers of Terrain to insert an extra case Terrain::Lava => unreachable!(), whenever matching on it which will never be triggered.

The screeps team has mentioned on slack that TERRAIN_MASK_LAVA has never been used, and that there are no current plans for using it. It's also missing from Room.Terrain.get's listed output.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a question of whether you prefer to implement 1:1 the constants or clean-up the API on our end. I'm all for making the usage easier rather than going for 1:1 translation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case I would go for "cleanup", but I think 1:1 would agree here too. We're only ever returning the terrain, never passing it into the game, and the only place it's returned is documented to not return 4. Even though the constant exists, it's documented to never be used?

// Todo: Implement the other masks when lava becomes a thing
}

impl TryFrom<Value> for Terrain {
Expand All @@ -267,26 +270,15 @@ impl TryFrom<Value> for Terrain {
0 => Terrain::Plain,
1 => Terrain::Wall,
2 => Terrain::Swamp,
// might not need this, but just in case we try
// to decode a game-encoded number and '3' represents swamp + wall
3 => Terrain::Wall,
3 => Terrain::SwampWall,
4 => Terrain::Lava,
x => panic!("unknown terrain encoded integer {}", x),
},
};
Ok(v)
}
}

impl AsRef<str> for Terrain {
fn as_ref(&self) -> &str {
match *self {
Terrain::Plain => "plain",
Terrain::Wall => "wall",
Terrain::Swamp => "swamp",
}
}
}

/// Internal enum representing each LOOK_* constant.
///
/// It's recommended to use the constants in the `look` module instead for type safety.
Expand Down
11 changes: 4 additions & 7 deletions screeps-game-api/src/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ pub mod map {
use std::collections;

use {
constants::{find::Exit, Direction, ReturnCode, Terrain},
objects::{Room, RoomPosition},
constants::{find::Exit, Direction, ReturnCode},
objects::{Room, RoomTerrain},
traits::{TryFrom, TryInto},
};

Expand Down Expand Up @@ -164,11 +164,8 @@ pub mod map {
js_unwrap!(Game.map.getRoomLinearDistance(@{room1}, @{room2}, @{continuous}))
}

/// See [http://docs.screeps.com/api/#Game.map.getTerrainAt]
///
/// [http://docs.screeps.com/api/#Game.map.getTerrainAt]: http://docs.screeps.com/api/#Game.map.getTerrainAt
pub fn get_terrain_at(pos: &RoomPosition) -> Terrain {
js_unwrap!(__terrain_type_str_to_num(Game.map.getTerrainAt(@{pos.as_ref()})))
pub fn get_room_terrain(room_name: &str) -> RoomTerrain {
js_unwrap!(Game.map.getRoomTerrain(@{room_name}))
}

/// See [http://docs.screeps.com/api/#Game.map.getWorldSize]
Expand Down
1 change: 1 addition & 0 deletions screeps-game-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ extern crate scoped_tls;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate stdweb;
#[macro_use]
Expand Down
7 changes: 6 additions & 1 deletion screeps-game-api/src/objects/impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod nuke;
mod resource;
mod room;
mod room_position;
mod room_terrain;
mod source;
mod structure_controller;
mod structure_keeper_lair;
Expand All @@ -24,7 +25,11 @@ mod structure_tower;
mod tombstone;

pub use self::{
room::{FindOptions, Path, Step},
room::{
AttackEvent, AttackType, BuildEvent, Event, EventType, ExitEvent, FindOptions,
HarvestEvent, HealEvent, HealType, ObjectDestroyedEvent, Path, RepairEvent,
ReserveControllerEvent, Step, UpgradeControllerEvent,
},
structure_controller::{Reservation, Sign},
structure_spawn::SpawnOptions,
};
130 changes: 129 additions & 1 deletion screeps-game-api/src/objects/impls/room.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{marker::PhantomData, mem, ops::Range};

use serde_json;
use stdweb::Reference;

use {
Expand All @@ -8,7 +9,8 @@ use {
},
memory::MemoryReference,
objects::{
HasPosition, Room, RoomPosition, StructureController, StructureStorage, StructureTerminal,
HasPosition, Room, RoomPosition, RoomTerrain, StructureController, StructureStorage,
StructureTerminal,
},
pathfinder::CostMatrix,
positions::LocalRoomName,
Expand Down Expand Up @@ -102,10 +104,19 @@ impl Room {
}
}

pub fn get_event_log(&self) -> Vec<Event> {
let raw_event_log: String = js_unwrap!{@{self.as_ref()}.getEventLog(true)};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To fully fix #59 it'd be nice to have a get_event_log_raw function returning this String directly as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't see the usage, but sure.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe storing the event log in a segment for further inspection? I could imagine doing this when in a "low cpu available" mode if under attack and an exact replay of events is wanted. There's the room history, but that's not entirely reliable, and the AI itself cannot analyze it.

Or an AI could want to use something besides serde_json to deserialize events, possibly skipping irrelevant ones to save CPU.

I'm not sure I would do any of these things, but since the JS API does give us this option and it is much cheaper than parsing it, I'd prefer giving that option as well.

serde_json::from_str(&raw_event_log).expect("Malformed Event Log")
}

pub fn get_position_at(&self, x: u32, y: u32) -> Option<RoomPosition> {
js_unwrap!{@{self.as_ref()}.get_position_at(@{x}, @{y})}
}

pub fn get_terrain(&self) -> RoomTerrain {
js_unwrap!(@{self.as_ref()}.getTerrain())
}

// pub fn look_at(&self, x: u32, y: u32) -> ! {
// unimplemented!()
// }
Expand Down Expand Up @@ -420,3 +431,120 @@ pub enum Path {
}

js_deserializable!{Path}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct Event {
#[serde(flatten)]
pub event: EventType,
#[serde(rename = "objectId")]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this work as #[serde(rename_all = "camelCase")] on the type? Just to match the other structures and to not single this one out.

pub object_id: String,
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(tag = "event", content = "data")]
pub enum EventType {
#[serde(rename = "1")]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work?

I have not tested whether serde equates the string "1" with the JSON integer 1 for tags like this. If you have, it's all good.

I'd almost want us to include a small unit test to ensure EventType deserializes correctly in the code, but for now if we know it works that's enough.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not tested it. My problem is working on getting those to actually make sense without, but I might have to write a deserializer manually for this one. I had forgotten that integers were a thing in JSON.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is actually being worked on right now:
serde-rs/serde#1392

I might wait a few days and see whether this goes anywhere. Still, I'm learning how to manually write a deserializer in the meantime.

Attack(AttackEvent),
#[serde(rename = "2")]
ObjectDestroyed(ObjectDestroyedEvent),
#[serde(rename = "3")]
AttackController,
#[serde(rename = "4")]
Build(BuildEvent),
#[serde(rename = "5")]
Harvest(HarvestEvent),
#[serde(rename = "6")]
Heal(HealEvent),
#[serde(rename = "7")]
Repair(RepairEvent),
#[serde(rename = "8")]
ReserveController(ReserveControllerEvent),
#[serde(rename = "9")]
UpgradeController(UpgradeControllerEvent),
#[serde(rename = "10")]
Exit(ExitEvent),
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AttackEvent {
pub target_id: String,
pub damage: u32,
pub attack_type: AttackType,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[repr(u32)]
pub enum AttackType {
Melee = 1,
Ranged = 2,
RangedMass = 3,
Dismantle = 4,
HitBack = 5,
Nuke = 6,
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct ObjectDestroyedEvent {
#[serde(rename = "type")]
pub object_type: String,
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BuildEvent {
pub target_id: String,
pub amount: u32,
pub energy_spent: u32,
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HarvestEvent {
pub target_id: String,
pub amount: u32,
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HealEvent {
pub target_id: String,
pub amount: u32,
pub heal_type: HealType,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[repr(u32)]
pub enum HealType {
Melee = 1,
Ranged = 2,
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RepairEvent {
pub target_id: String,
pub amount: u32,
pub energy_spent: u32,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReserveControllerEvent {
pub amount: u32,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpgradeControllerEvent {
pub amount: u32,
pub energy_spent: u32,
}

#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExitEvent {
pub room: String,
pub x: u32,
pub y: u32,
}
7 changes: 7 additions & 0 deletions screeps-game-api/src/objects/impls/room_position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ impl RoomPosition {
)
}

pub fn create_named_construction_site(&self, ty: StructureType, name: &str) -> ReturnCode {
js_unwrap!(
@{self.as_ref()}.createConstructionSite(__structure_type_num_to_str(@{ty as i32}),
@{name})
)
}

pub fn create_flag(&self, name: &str, main_color: Color, secondary_color: Color) -> ReturnCode {
// TODO: determine if ERR_NOT_IN_RANGE is the best choice here
(js! {
Expand Down
15 changes: 15 additions & 0 deletions screeps-game-api/src/objects/impls/room_terrain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use {constants::Terrain, objects::RoomTerrain};

impl RoomTerrain {
pub fn constructor(room_name: &str) -> Self {
js_unwrap!{new Room.Terrain(@{room_name})}
}

pub fn get(&self, x: u32, y: u32) -> Terrain {
js_unwrap!{@{self.as_ref()}.get(@{x}, @{y})}
}

pub fn get_raw_buffer(&self) -> Vec<u8> {
js_unwrap!(@{self.as_ref()}.getRawBuffer())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in my comment, I think it should be pretty simple to create the Vec<u8> ourselves and ask the game to populate it rather than having the game create it and then copy it into WASM.

}
}
4 changes: 4 additions & 0 deletions screeps-game-api/src/objects/impls/structure_lab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ impl StructureLab {
pub fn run_reaction(&self, lab1: &StructureLab, lab2: &StructureLab) -> ReturnCode {
js_unwrap!{@{self.as_ref()}.runReaction(@{lab1.as_ref()}, @{lab2.as_ref()})}
}

pub fn unboost_creep(&self, creep: &Creep) -> ReturnCode {
js_unwrap!(@{self.as_ref()}.unboostCreep(@{creep.as_ref()}))
}
}
8 changes: 7 additions & 1 deletion screeps-game-api/src/objects/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ mod impls;
mod structure;

pub use self::{
impls::{FindOptions, Path, Reservation, Sign, SpawnOptions, Step},
impls::{
AttackEvent, AttackType, BuildEvent, Event, EventType, ExitEvent, FindOptions,
HarvestEvent, HealEvent, HealType, ObjectDestroyedEvent, Path, RepairEvent, Reservation,
ReserveControllerEvent, Sign, SpawnOptions, Step, UpgradeControllerEvent,
},
structure::Structure,
};

Expand All @@ -49,6 +53,8 @@ reference_wrappers!(
RoomObject,
#[reference(instance_of = "RoomPosition")]
RoomPosition,
#[reference(instance_of = "Room.Terrain")]
RoomTerrain,
#[reference(instance_of = "Source")]
Source,
#[reference(instance_of = "StructureContainer")]
Expand Down