From 7ae70638f5cbeb0a3f5846f428e935112f229e49 Mon Sep 17 00:00:00 2001 From: danielvschoor <43602193+danielvschoor@users.noreply.github.com> Date: Sat, 26 Jun 2021 00:35:59 +0200 Subject: [PATCH] Add tags (#26) * Add tags * Fix clippy lints --- Cargo.toml | 1 - rust_ac/result.py | 8 +++++++ src/controller.rs | 12 +++++++--- src/handler/game.rs | 13 ++++++---- src/handler/lobby.rs | 4 ++-- src/handler/messaging.rs | 10 ++++++-- src/handler/player.rs | 51 ++++++++++++++++++++++++++++++---------- src/result.rs | 4 ++++ 8 files changed, 77 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8a947fd..29a3faf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,6 @@ pyo3-log = {version="^0.3.0", optional=true} [dependencies.websocket] git = "https://github.com/aiarena/rust-websocket" default-features = false -features = ["sync"] [dependencies.pyo3] version = "^0.13" diff --git a/rust_ac/result.py b/rust_ac/result.py index df22aeb..9521394 100644 --- a/rust_ac/result.py +++ b/rust_ac/result.py @@ -26,6 +26,8 @@ def __init__(self, match_config, cfg=None): self.bot1_avg_frame = 0 self.bot2_avg_frame = 0 self.winner = None + self.bot1_tags = None + self.bot2_tags = None def __repr__(self): return f""" @@ -53,6 +55,8 @@ def to_json(self): "Bot1AvgFrame": self.bot1_avg_frame, "Bot2AvgFrame": self.bot2_avg_frame, 'ReplayPath': self.replay_path, + 'Bot1Tags': self.bot1_tags, + 'Bot2Tags': self.bot2_tags } def has_result(self): @@ -129,6 +133,10 @@ def parse_result(self, result=None, error=False): if result.get("TimeStamp", None): self.time_stamp = result["TimeStamp"] + if result.get("Tags", None): + self.bot1_tags = result['Tags'].get(self.bot1, []) + self.bot2_tags = result['Tags'].get(self.bot2, []) + if self.replay_path is None: self.replay_path = os.path.join( self._config.REPLAYS_DIRECTORY, f'{self.match_id}_{self.bot1}_vs_{self.bot2}.SC2Replay') diff --git a/src/controller.rs b/src/controller.rs index b952450..69c45be 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -409,11 +409,16 @@ impl Controller { Ok((result, players)) => { let average_frame_time: Option>; let mut avg_hash: HashMap = HashMap::with_capacity(2); - for p in players.into_iter() { - avg_hash - .insert(p.player_name().as_ref().unwrap().to_string(), p.frame_time); + let tags: Option>>; + let mut tags_hash: HashMap> = HashMap::with_capacity(2); + for p in players.iter() { + let player_name = p.player_name().as_ref().unwrap().to_string(); + avg_hash.insert(player_name.clone(), p.frame_time); + tags_hash.insert(player_name.clone(), p.tags.iter().cloned().collect()); } + tags = Some(tags_hash); average_frame_time = Some(avg_hash); + let player_results = result.player_results; let p1 = self.config.as_ref().unwrap().player1().to_string(); @@ -439,6 +444,7 @@ impl Controller { self.config.as_ref().map(|x| x.map.clone()), self.config.as_ref().map(|x| x.replay_name.clone()), self.config.as_ref().map(|x| x.match_id), + tags, ); self.send_message(j_result.serialize().as_ref()); diff --git a/src/handler/game.rs b/src/handler/game.rs index a2c56b3..24991f2 100644 --- a/src/handler/game.rs +++ b/src/handler/game.rs @@ -44,20 +44,22 @@ impl Game { player_results: &mut Vec>, game_loops: &mut u32, frame_times: &mut [f32; 2], + tags: &mut [Vec; 2], ) { let ToGame { player_index, content, } = msg; match content { - ToGameContent::GameOver(results) => { + ToGameContent::GameOver(game_over) => { for (i, item) in player_results.iter_mut().enumerate() { if item.is_none() { - *item = Some(results.0[i]) + *item = Some(game_over.results[i]) } } - *game_loops = results.1; - frame_times[player_index] = results.2; + *game_loops = game_over.game_loops; + frame_times[player_index] = game_over.frame_time; + tags[player_index] = game_over.tags; } ToGameContent::LeftGame => { info!("Player left handler before it was over"); @@ -98,6 +100,7 @@ impl Game { let mut handles: Vec>> = Vec::new(); let mut game_loops = 0_u32; let mut frame_times: [f32; 2] = [0_f32, 0_f32]; + let mut tags: [Vec; 2] = [vec![], vec![]]; let (rx, mut _to_player_channels, player_channels) = create_channels(self.players.len()); let mut player_results: Vec> = vec![None; self.players.len()]; @@ -113,7 +116,7 @@ impl Game { // A client ended the handler recv(rx) -> r => match r { Ok(msg) => { - Self::process_msg(msg, &mut player_results, &mut game_loops, &mut frame_times); + Self::process_msg(msg, &mut player_results, &mut game_loops, &mut frame_times, &mut tags); }, Err(e) => panic!("Player channel closed without sending results {:?}",e), }, diff --git a/src/handler/lobby.rs b/src/handler/lobby.rs index 9b6160f..46d375b 100644 --- a/src/handler/lobby.rs +++ b/src/handler/lobby.rs @@ -86,7 +86,7 @@ impl GameLobby { r_create_game.set_local_map(r_local_map); r_create_game.set_realtime(self.config.realtime()); - let p_cfgs: Vec<_> = players.iter().map(CreateGamePlayer::to_proto).collect(); + let p_cfgs: Vec<_> = players.iter().map(CreateGamePlayer::as_proto).collect(); r_create_game.set_player_setup(RepeatedField::from_vec(p_cfgs)); let mut request = Request::new(); @@ -216,7 +216,7 @@ enum CreateGamePlayer { Observer, } impl CreateGamePlayer { - fn to_proto(&self) -> sc2_proto::sc2api::PlayerSetup { + fn as_proto(&self) -> sc2_proto::sc2api::PlayerSetup { use sc2_proto::sc2api::{PlayerSetup, PlayerType}; let mut ps = PlayerSetup::new(); match self { diff --git a/src/handler/messaging.rs b/src/handler/messaging.rs index 22d5c66..25a9489 100644 --- a/src/handler/messaging.rs +++ b/src/handler/messaging.rs @@ -74,7 +74,7 @@ pub struct ToGame { #[derive(Debug, Clone)] pub enum ToGameContent { /// Game ended normally - GameOver((Vec, u32, f32)), + GameOver(GameOver), /// SC2 responded to `leave_game` request LeftGame, /// SC2 responded to `quit` request without the client leaving the handler @@ -85,7 +85,13 @@ pub enum ToGameContent { /// Client unexpectedly closed connection UnexpectedConnectionClose, } - +#[derive(Debug, Clone)] +pub struct GameOver { + pub(crate) results: Vec, + pub(crate) game_loops: u32, + pub(crate) frame_time: f32, + pub(crate) tags: Vec, +} /// Channel from the handler to a player #[derive(Clone)] pub struct ChannelToPlayer { diff --git a/src/handler/player.rs b/src/handler/player.rs index 0494079..5757fc9 100644 --- a/src/handler/player.rs +++ b/src/handler/player.rs @@ -14,9 +14,11 @@ use websocket::OwnedMessage; use super::messaging::{ChannelToGame, ToGameContent}; use crate::config::Config; +use crate::handler::messaging::GameOver; use crate::proxy::Client; use crate::sc2::{PlayerResult, Race}; use crate::sc2process::Process; +use std::collections::HashSet; use std::fs::File; use std::io::Write; use std::thread; @@ -40,6 +42,8 @@ pub struct Player { pub frame_time: f32, /// Player id pub player_id: Option, + /// Tags + pub tags: HashSet, } impl Player { @@ -57,6 +61,7 @@ impl Player { data, frame_time: 0_f32, player_id: None, + tags: Default::default(), } }) } @@ -72,6 +77,7 @@ impl Player { data, frame_time: 0_f32, player_id: None, + tags: Default::default(), } } pub fn player_name(&self) -> &Option { @@ -292,6 +298,22 @@ impl Player { surrender = true; break; } + for tag in req + .get_action() + .actions + .iter() + .filter(|a| a.has_action_chat() && a.get_action_chat().has_message()) + .filter_map(|x| { + let msg = x.get_action_chat().get_message(); + if msg.contains("Tag:") { + msg.strip_prefix("Tag:").map(String::from) + } else { + None + } + }) + { + self.tags.insert(tag); + } // Send request to SC2 and get response response_raw = match self.sc2_query_raw(req_raw) { @@ -352,11 +374,12 @@ impl Player { .collect(); results_by_id.sort(); let results: Vec<_> = results_by_id.into_iter().map(|(_, v)| v).collect(); - gamec.send(ToGameContent::GameOver(( + gamec.send(ToGameContent::GameOver(GameOver { results, - self.game_loops, - self.frame_time, - ))); + game_loops: self.game_loops, + frame_time: self.frame_time, + tags: self.tags.iter().cloned().collect(), + })); self.save_replay(replay_path); self.process.kill(); return Some(self); @@ -370,11 +393,12 @@ impl Player { self.frame_time }; debug!("Max time reached"); - gamec.send(ToGameContent::GameOver(( - vec![PlayerResult::Tie, PlayerResult::Tie], - self.game_loops, - self.frame_time, - ))); + gamec.send(ToGameContent::GameOver(GameOver { + results: vec![PlayerResult::Tie, PlayerResult::Tie], + game_loops: self.game_loops, + frame_time: self.frame_time, + tags: self.tags.iter().cloned().collect(), + })); self.process.kill(); return Some(self); } @@ -421,11 +445,12 @@ impl Player { if surrender { let mut results: Vec = vec![PlayerResult::Victory; 2]; results[(self.player_id.unwrap() - 1) as usize] = PlayerResult::Defeat; - gamec.send(ToGameContent::GameOver(( + gamec.send(ToGameContent::GameOver(GameOver { results, - self.game_loops, - self.frame_time, - ))); + game_loops: self.game_loops, + frame_time: self.frame_time, + tags: self.tags.iter().cloned().collect(), + })); self.process.kill(); return Some(self); } diff --git a/src/result.rs b/src/result.rs index 2c236ff..0000a5b 100644 --- a/src/result.rs +++ b/src/result.rs @@ -23,6 +23,8 @@ pub(crate) struct JsonResult { map: String, #[serde(default, rename = "ReplayPath")] replay_path: String, + #[serde(default, rename = "Tags")] + tags: HashMap>, } impl JsonResult { #[allow(clippy::too_many_arguments)] @@ -37,6 +39,7 @@ impl JsonResult { map: Option, replay_path: Option, match_id: Option, + tags: Option>>, ) -> Self { Self { result: result.unwrap_or_default(), @@ -49,6 +52,7 @@ impl JsonResult { map: map.unwrap_or_default(), replay_path: replay_path.unwrap_or_default(), match_id: match_id.unwrap_or_default(), + tags: tags.unwrap_or_default(), } } pub(crate) fn serialize(&self) -> String {