Skip to content

Commit

Permalink
Merge pull request #34 from Kiiyya/merge-yeet-diesel
Browse files Browse the repository at this point in the history
Merge yeet diesel
  • Loading branch information
Razer2015 authored Dec 6, 2022
2 parents 0041a0a + 0ba710c commit 45b1fda
Show file tree
Hide file tree
Showing 11 changed files with 268 additions and 28 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ jobs:
# OS: ubuntu-latest
- TARGET: aarch64-unknown-linux-gnu # tested on aws t4g.nano
OS: ubuntu-latest
- TARGET: aarch64-unknown-linux-musl # tested on aws t4g.nano in alpine container
OS: ubuntu-latest
# - TARGET: aarch64-unknown-linux-musl # tested on aws t4g.nano in alpine container
# OS: ubuntu-latest
# - TARGET: armv7-unknown-linux-gnueabihf # raspberry pi 2-3-4, not tested
# OS: ubuntu-latest
# - TARGET: armv7-unknown-linux-musleabihf # raspberry pi 2-3-4, not tested
Expand Down
24 changes: 24 additions & 0 deletions battlefield_rcon/src/bf4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use error::Bf4Error;
use futures_core::Stream;
use player_info_block::{parse_pib, PlayerInfo};
use server_info::{parse_serverinfo, ServerInfo};
use team_scores::{parse_team_scores};
use tokio::{
net::ToSocketAddrs,
sync::{broadcast, mpsc, oneshot},
Expand All @@ -22,6 +23,7 @@ pub mod map_list;
pub(crate) mod player_cache;
pub mod player_info_block;
pub mod server_info;
pub mod team_scores;
mod util;
pub mod ban_list;

Expand Down Expand Up @@ -350,6 +352,28 @@ impl Bf4Client {
winning_team: Team::rcon_decode(&packet.words[1])?,
})
}
"server.onRoundOverTeamScores" => {
if packet.words.len() < 5 {
return Err(Bf4Error::Rcon(RconError::malformed_packet(
packet.words.clone(),
format!("{} packet must have at least {} words", &packet.words[0], 5),
)));
}

let team_scores = parse_team_scores(&packet.words)?;

Ok(Event::RoundOverTeamScores {
number_of_entries: team_scores.number_of_entries,
scores: team_scores.scores,
target_score: team_scores.target_score,
})
}
"server.onRoundOverPlayers" => {
let pib = parse_pib(&packet.words[1..])?;
Ok(Event::RoundOverPlayers {
players: pib,
})
}
"punkBuster.onMessage" => {
assert_len(&packet, 2)?;
Ok(Event::PunkBusterMessage(packet.words[1].to_string()))
Expand Down
8 changes: 8 additions & 0 deletions battlefield_rcon/src/bf4/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,14 @@ pub enum Event {
RoundOver {
winning_team: Team,
},
RoundOverTeamScores {
number_of_entries: i32,
scores: Vec<i32>,
target_score: i32,
},
RoundOverPlayers {
players: Vec<PlayerInfo>
},
Join {
player: Player,
},
Expand Down
36 changes: 36 additions & 0 deletions battlefield_rcon/src/bf4/team_scores.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::bf4::util::{parse_int};
use crate::rcon::{RconError, RconResult};
use ascii::AsciiString;
use serde::{Deserialize, Serialize};

///
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TeamScores {
pub number_of_entries: i32,
pub scores: Vec<i32>,
pub target_score: i32,
}

/// Expects the TeamScores, without any leading "OK".
pub fn parse_team_scores(words: &[AsciiString]) -> RconResult<TeamScores> {
if words.is_empty() {
return Err(RconError::protocol_msg(
"Failed to parse TeamScores: Zero length?",
));
}

let teams_count = parse_int(&words[1])? as usize;
let mut offset = 2;
let mut teamscores: Vec<i32> = Vec::new();
for _ in 0..teams_count {
teamscores.push(parse_int(&words[offset]).unwrap());

offset += 1;
}

Ok(TeamScores {
number_of_entries: teams_count as i32,
scores: teamscores,
target_score: parse_int(&words[offset]).unwrap(),
})
}
4 changes: 2 additions & 2 deletions battlefox/Dockerfile.cross-platform
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ RUN if [ "$TARGETPLATFORM" = "linux/amd64" ]; then export ARCHITECTURE=x86_64-un

RUN tar -xf battlefox.tar.gz

FROM debian:bullseye-slim
FROM frolvlad/alpine-glibc:glibc-2.34

RUN apt-get update && apt-get install -y ca-certificates
# RUN apt-get update && apt-get install -y ca-certificates

WORKDIR /app
COPY --from=downloader /home/curl_user/battlefox .
Expand Down
1 change: 1 addition & 0 deletions battlefox/configs/mapvote.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ enabled: true
n_options: 4
max_options: 6
max_noms_per_vip: 1
vote_start_interval: 30
spammer_interval: 180
endscreen_votetime: 15
endscreen_post_votetime: 5
Expand Down
33 changes: 33 additions & 0 deletions battlefox/src/mapmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ pub struct PopState {
pub pool: MapPool,
/// At `min_players` or more players, activate this pool. Unless a pool with even higher `min_players` exists.
pub min_players: usize,
/// The amount of map history to be excluded when generating a new vote
pub excluded_maps_count: Option<usize>,
}

impl PopState {
Expand Down Expand Up @@ -128,6 +130,9 @@ impl Plugin for MapManager {
// Err(MapListError::Rcon(r)) => return Err(r),
Err(mle) => error!("While starting up MapManager: {:?}. MapManager is *not* starting now!", mle),
}

// Populate the current map in the history
let _ = self.current_map(&bf4).await;
}

async fn event(self: Arc<Self>, bf4: Arc<Bf4Client>, event: Event) -> RconResult<()> {
Expand Down Expand Up @@ -393,6 +398,34 @@ impl MapManager {
Some(hist[0])
}
}

pub fn recent_maps(&self) -> Vec<Map> {
let hist = {
let inner = self.inner.lock().unwrap();
inner.map_history.clone()
};

let popstate = {
let lock = self.inner.lock().unwrap();
lock.pop_state.clone()
};

hist.iter()
.take(popstate.excluded_maps_count.unwrap_or(1))
.cloned()
.collect()
}

pub fn is_recently_played(&self, map: &Map) -> bool {
let recent_maps = self.recent_maps();

for m in recent_maps.iter() {
if map.eq(&m) {
return true;
}
};
false
}
}

impl std::fmt::Debug for MapManager {
Expand Down
12 changes: 12 additions & 0 deletions battlefox/src/mapmanager/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,18 @@ impl MapPool {
.collect(),
}
}

/// Returns a new map pool which contains the same items, except with any with `map` removed.
pub fn without_many_vec(&self, maps: &Vec<Map>) -> Self {
Self {
pool: self
.pool
.iter()
.filter(|&mip| !maps.contains(&mip.map))
.cloned()
.collect(),
}
}
}

#[cfg(test)]
Expand Down
89 changes: 65 additions & 24 deletions battlefox/src/mapvote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use futures::{future::join_all, StreamExt};
use itertools::Itertools;
use matching::AltMatcher;
use multimap::MultiMap;
use num_bigint::BigInt;
use rand::{RngCore, thread_rng};
use std::cell::Cell;
use std::fmt::Write;
Expand All @@ -43,7 +44,7 @@ use tokio::{
time::{sleep, Interval},
};

use num_rational::BigRational as Rat;
use num_rational::{BigRational as Rat, Ratio};
use num_traits::{One, ToPrimitive};

pub mod config;
Expand Down Expand Up @@ -216,9 +217,9 @@ impl Inner {
}
}

fn set_up_new_vote(&mut self, n_options: usize, without: Option<Map>) {
fn set_up_new_vote(&mut self, n_options: usize, without: Option<Vec<Map>>) {
let pool = if let Some(without) = without {
self.popstate.pool.without(without)
self.popstate.pool.without_many_vec(&without)
} else {
self.popstate.pool.clone()
};
Expand Down Expand Up @@ -322,11 +323,13 @@ impl Plugin for Mapvote {
async fn event(self: Arc<Self>, bf4: Arc<Bf4Client>, event: Event) -> RconResult<()> {
match event {
Event::Chat { vis, player, msg } => {
if msg.as_str().starts_with("/bfox endvote") && self.admins.is_admin(&player.name) {
self.handle_round_over(&bf4).await;
} else {
let _ = self.handle_chat_msg(bf4, vis, player, msg).await;
}
let _ = self.handle_chat_msg(bf4, vis, player, msg).await;
}
Event::LevelLoaded { level_name, game_mode, rounds_played, rounds_total } => {
tokio::spawn(async move {
tokio::time::sleep(self.config.vote_start_interval).await;
self.start_new_vote().await;
});
}
Event::RoundOver { .. } => {
self.handle_round_over(&bf4).await;
Expand Down Expand Up @@ -621,7 +624,7 @@ impl Mapvote {
prefs.retain(|MapInPool { map, mode, vehicles}| inner.alternatives.contains_map(*map));

let weight = match player.clone().cases() {
Left(yesvip) => Rat::one() + Rat::one(), // 2
Left(yesvip) => self.vip_vote_weight(),
Right(novip) => Rat::one(),
};

Expand Down Expand Up @@ -760,22 +763,31 @@ impl Mapvote {
if inner.popstate.pool.contains_map(map) {
// make sure this VIP hasn't exceeded their nomination limit this round.
if inner.vip_n_noms(&player) < self.config.max_noms_per_vip {
// phew, that was a lot of ifs...
info!("Player {} has nominated {} (vehicles: {:?})", player.name, map.Pretty(), vehicles);
inner.vip_nom(&player, map, vehicles);
info!("The new alternatives are {:?}.", inner.alternatives);

let announce = self.config.announce_nominator.unwrap_or(true);
if announce {
futures.push(bf4.say_lines(vec![
format!("Our beloved VIP {} has nominated {}!", &*player, map.Pretty()),
format!("{} has been added to the options, everyone can vote on it now <3", map.Pretty()),
], Visibility::All));
// Check if the nominated map has recently been played
if !self.mapman.is_recently_played(&map) {
// phew, that was a lot of ifs...
info!("Player {} has nominated {} (vehicles: {:?})", player.name, map.Pretty(), vehicles);
inner.vip_nom(&player, map, vehicles);
info!("The new alternatives are {:?}.", inner.alternatives);

let announce = self.config.announce_nominator.unwrap_or(true);
if announce {
futures.push(bf4.say_lines(vec![
format!("Our beloved VIP {} has nominated {}!", &*player, map.Pretty()),
format!("{} has been added to the options, everyone can vote on it now <3", map.Pretty()),
], Visibility::All));
}
else {
futures.push(bf4.say_lines(vec![
format!("{} has been added to the options, everyone can vote on it now <3", map.Pretty()),
], Visibility::All));
}
}
else {
futures.push(bf4.say_lines(vec![
format!("{} has been added to the options, everyone can vote on it now <3", map.Pretty()),
], Visibility::All));
format!("Apologies, {}, that map was just recently played.", &*player),
"Try nominating some other map".to_string(),
], player.get().into()));
}
} else {
futures.push(bf4.say_lines(vec![
Expand Down Expand Up @@ -899,6 +911,14 @@ impl Mapvote {
let _ = bf4.say("You are not admin (according to bfox config).", player).await;
}
},
"startvote" => {
if self.admins.is_admin(&player.name) {
let _ = bf4.say("Starting new vote.", player).await;
self.start_new_vote().await;
} else {
let _ = bf4.say("You are not admin (according to bfox config).", player).await;
}
},
_ => (),
}
}
Expand Down Expand Up @@ -997,8 +1017,18 @@ impl Mapvote {
Ok(())
}

async fn start_new_vote(&self) {
let recent_maps = self.mapman.recent_maps();

let mut lock = self.inner.lock().await;
if let Some(inner) = &mut *lock {
info!("Starting a new vote: {:#?}", &inner.votes);
inner.set_up_new_vote(self.config.n_options, Some(recent_maps));
}
}

async fn handle_round_over(&self, bf4: &Arc<Bf4Client>) {
let current_map = self.mapman.current_map(bf4).await;
let recent_maps = self.mapman.recent_maps();
self.broadcast_status(bf4).await; // send everyone the voting options.
// let's wait like 10 seconds because people might still vote in the end screen.
let _ = bf4.say(format!("Mapvote is still going for {}s! Hurry!", self.config.endscreen_votetime.as_secs()), Visibility::All).await;
Expand All @@ -1018,7 +1048,7 @@ impl Mapvote {
// get each player's votes, so we can simulate how the votes go later.
let assignment = inner.to_assignment();

inner.set_up_new_vote(self.config.n_options, current_map);
// inner.set_up_new_vote(self.config.n_options, Some(recent_maps));
Some((profile, assignment, inner.anim_override_override.clone()))
} else {
None
Expand Down Expand Up @@ -1078,6 +1108,17 @@ impl Mapvote {
}
}
}

/// Returns the configured (or default 2) VIP vote weight
/// - Can't be less than 1
fn vip_vote_weight(&self) -> Ratio<BigInt> {
let mut weight = Rat::one();
let config_weight = self.config.vip_vote_weight.unwrap_or(2);
for n in 1..config_weight {
weight += Rat::one();
}
weight
}
}

pub enum ParseMapsResult {
Expand Down
Loading

0 comments on commit 45b1fda

Please sign in to comment.