Extend status collector

Information about the currently loaded/ticked chunks have been added.
This commit is contained in:
Malte Tammena 2021-12-10 13:39:34 +01:00
parent 461d66a3d9
commit 4d60e14d43
6 changed files with 206 additions and 4 deletions

2
Cargo.lock generated
View file

@ -613,7 +613,7 @@ dependencies = [
[[package]]
name = "glados"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"dotenv",
"influx_db_client",

View file

@ -1,6 +1,6 @@
[package]
name = "glados"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
license = "MIT"

View file

@ -11,7 +11,7 @@ lazy_static! {
#[derive(Debug, StructOpt)]
pub struct Args {
#[structopt(long, env = "DATA_COLLECTOR_ENABLE", takes_value = false)]
#[structopt(long, env = "DATA_COLLECTOR_ENABLE", takes_value = false, requires_all = &["influx-host", "influx-db", "influx-user", "influx-password"])]
pub enable_data_collector: bool,
#[structopt(long = "token",

View file

@ -24,6 +24,8 @@ pub enum Error {
RconListCmd(#[source] nom::Err<nom::error::Error<String>>),
#[error("Parsing results of `whois` command: {_0}")]
RconWhoIsCmd(#[source] nom::Err<nom::error::Error<String>>),
#[error("Parsing results of `paper chunkinfo` command: {_0}")]
RconWorldInfoCmd(#[source] nom::Err<nom::error::Error<String>>),
#[error("Parsing member filter of `~filter` command: {_0}")]
ParseMemberFilter(#[source] nom::Err<nom::error::Error<String>>),
#[error("Unknown role {_0:?} in filter")]

View file

@ -3,6 +3,7 @@ use serenity::client::Context;
use std::collections::HashMap;
mod world_info;
mod player_info;
mod tps;
@ -14,6 +15,7 @@ use crate::{
};
use self::{
world_info::{WorldInfo, WorldInfoCmd},
player_info::{ListCmd, PlayerInfo},
tps::{Tps, TpsCmd},
};
@ -23,6 +25,7 @@ pub struct Status {
pub amount_online: u32,
/// List of players associated with their online status.
pub players: HashMap<MinecraftPlayer, bool>,
pub world_info: WorldInfo,
}
impl Status {
@ -45,11 +48,13 @@ impl Status {
for (_ign, uuid) in online_players {
players.insert(MinecraftPlayer::from_uuid(ctx, uuid).await?, true);
}
let world_info = RconHandle::cmd(&WorldInfoCmd, ctx).await?;
Ok(Status {
tps,
amount_online,
players,
world_info,
})
}
@ -58,7 +63,84 @@ impl Status {
let status = Point::new("status")
.add_field("tps", self.tps.min1)
// Safe, since we started with u32
.add_field("player_count", self.amount_online as i64);
.add_field("player_count", self.amount_online as i64)
.add_field(
"world_total_chunks",
self.world_info.overworld.total_chunks as i64,
)
.add_field(
"world_inactive_chunks",
self.world_info.overworld.inactive_chunks as i64,
)
.add_field(
"world_border_chunks",
self.world_info.overworld.border_chunks as i64,
)
.add_field(
"world_ticking_chunks",
self.world_info.overworld.ticking_chunks as i64,
)
.add_field(
"world_entity_chunks",
self.world_info.overworld.entity_chunks as i64,
)
.add_field(
"nether_total_chunks",
self.world_info.nether.total_chunks as i64,
)
.add_field(
"nether_inactive_chunks",
self.world_info.nether.inactive_chunks as i64,
)
.add_field(
"nether_border_chunks",
self.world_info.nether.border_chunks as i64,
)
.add_field(
"nether_ticking_chunks",
self.world_info.nether.ticking_chunks as i64,
)
.add_field(
"nether_entity_chunks",
self.world_info.nether.entity_chunks as i64,
)
.add_field(
"the_end_total_chunks",
self.world_info.the_end.total_chunks as i64,
)
.add_field(
"the_end_inactive_chunks",
self.world_info.the_end.inactive_chunks as i64,
)
.add_field(
"the_end_border_chunks",
self.world_info.the_end.border_chunks as i64,
)
.add_field(
"the_end_ticking_chunks",
self.world_info.the_end.ticking_chunks as i64,
)
.add_field(
"the_end_entity_chunks",
self.world_info.the_end.entity_chunks as i64,
)
.add_field("all_total_chunks", self.world_info.all.total_chunks as i64)
.add_field(
"all_inactive_chunks",
self.world_info.all.inactive_chunks as i64,
)
.add_field(
"all_border_chunks",
self.world_info.all.border_chunks as i64,
)
.add_field(
"all_ticking_chunks",
self.world_info.all.ticking_chunks as i64,
)
.add_field(
"all_entity_chunks",
self.world_info.all.entity_chunks as i64,
);
points.push(status);
let mut activity = Point::new("activity");
for (player, is_online) in &self.players {

View file

@ -0,0 +1,118 @@
use std::marker::PhantomData;
use nom::{
bytes::complete::tag,
character::complete::char,
combinator::{map, opt, recognize},
sequence::{pair, preceded, tuple},
IResult, Parser,
};
use crate::{
error::{Error, Result},
rcon::RconCommand,
};
pub struct WorldInfoCmd;
pub struct WorldInfo {
pub overworld: WorldInfoPart<Overworld>,
pub nether: WorldInfoPart<Nether>,
pub the_end: WorldInfoPart<TheEnd>,
pub all: WorldInfoPart<AllWorlds>,
}
pub trait World {
const NAME: &'static str;
}
macro_rules! world {
($ident:ident: $name:literal) => {
pub struct $ident;
impl World for $ident {
const NAME: &'static str = $name;
}
};
}
world!(Overworld: "world");
world!(Nether: "world_nether");
world!(TheEnd: "world_the_end");
world!(AllWorlds: "all listed worlds");
pub struct WorldInfoPart<W: World> {
pub total_chunks: u32,
pub inactive_chunks: u32,
pub border_chunks: u32,
pub ticking_chunks: u32,
pub entity_chunks: u32,
_world: PhantomData<W>,
}
impl RconCommand for WorldInfoCmd {
type Output = WorldInfo;
fn as_string(&self) -> String {
String::from("paper chunkinfo")
}
fn parse(&self, raw: String) -> Result<Self::Output> {
parse_chunkinfo_result(&raw)
.map(|(_, world_info)| world_info)
.map_err(|why| Error::RconWorldInfoCmd(why.to_owned()))
}
}
fn parse_chunkinfo_result(inp: &str) -> IResult<&str, WorldInfo> {
let world = WorldInfoPart::<Overworld>::parse;
let world_nether = WorldInfoPart::<Nether>::parse;
let world_the_end = WorldInfoPart::<TheEnd>::parse;
let all_worlds = WorldInfoPart::<AllWorlds>::parse;
map(
tuple((world, world_nether, world_the_end, all_worlds)),
|(overworld, nether, the_end, all)| WorldInfo {
overworld,
nether,
the_end,
all,
},
)(inp)
}
impl<W: World> WorldInfoPart<W> {
pub fn parse(inp: &str) -> IResult<&str, Self> {
preceded(Self::parse_headline, Self::parse_info)(inp)
}
fn parse_headline(inp: &str) -> IResult<&str, &str> {
recognize(tuple((tag("§9Chunks in §a"), tag(W::NAME), tag("§3:\n"))))(inp)
}
fn parse_info<'i>(inp: &'i str) -> IResult<&'i str, Self> {
let total = Self::parse_entry("Total");
let inactive = Self::parse_entry("Inactive");
let border = Self::parse_entry("Border");
let ticking = Self::parse_entry("Ticking");
let entity = Self::parse_entry("Entity");
map(
tuple((total, inactive, border, ticking, entity, char('\n'))),
|(total, inactive, border, ticking, entity, _)| Self {
total_chunks: total,
inactive_chunks: inactive,
border_chunks: border,
ticking_chunks: ticking,
entity_chunks: entity,
_world: PhantomData,
},
)(inp)
}
fn parse_entry<'i, E>(key: &'static str) -> impl Parser<&'i str, u32, E>
where
E: nom::error::ParseError<&'i str>,
{
use nom::character::complete::u32;
let key = preceded(pair(tag("§9"), opt(char(' '))), tag(key));
map(tuple((key, tag(": §3"), u32)), |(_, _, num)| num)
}
}