Extend status collector
Information about the currently loaded/ticked chunks have been added.
This commit is contained in:
parent
461d66a3d9
commit
4d60e14d43
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -613,7 +613,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glados"
|
name = "glados"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"influx_db_client",
|
"influx_db_client",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "glados"
|
name = "glados"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ lazy_static! {
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
pub struct Args {
|
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,
|
pub enable_data_collector: bool,
|
||||||
|
|
||||||
#[structopt(long = "token",
|
#[structopt(long = "token",
|
||||||
|
|
|
@ -24,6 +24,8 @@ pub enum Error {
|
||||||
RconListCmd(#[source] nom::Err<nom::error::Error<String>>),
|
RconListCmd(#[source] nom::Err<nom::error::Error<String>>),
|
||||||
#[error("Parsing results of `whois` command: {_0}")]
|
#[error("Parsing results of `whois` command: {_0}")]
|
||||||
RconWhoIsCmd(#[source] nom::Err<nom::error::Error<String>>),
|
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}")]
|
#[error("Parsing member filter of `~filter` command: {_0}")]
|
||||||
ParseMemberFilter(#[source] nom::Err<nom::error::Error<String>>),
|
ParseMemberFilter(#[source] nom::Err<nom::error::Error<String>>),
|
||||||
#[error("Unknown role {_0:?} in filter")]
|
#[error("Unknown role {_0:?} in filter")]
|
||||||
|
|
|
@ -3,6 +3,7 @@ use serenity::client::Context;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
mod world_info;
|
||||||
mod player_info;
|
mod player_info;
|
||||||
mod tps;
|
mod tps;
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
|
world_info::{WorldInfo, WorldInfoCmd},
|
||||||
player_info::{ListCmd, PlayerInfo},
|
player_info::{ListCmd, PlayerInfo},
|
||||||
tps::{Tps, TpsCmd},
|
tps::{Tps, TpsCmd},
|
||||||
};
|
};
|
||||||
|
@ -23,6 +25,7 @@ pub struct Status {
|
||||||
pub amount_online: u32,
|
pub amount_online: u32,
|
||||||
/// List of players associated with their online status.
|
/// List of players associated with their online status.
|
||||||
pub players: HashMap<MinecraftPlayer, bool>,
|
pub players: HashMap<MinecraftPlayer, bool>,
|
||||||
|
pub world_info: WorldInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Status {
|
impl Status {
|
||||||
|
@ -45,11 +48,13 @@ impl Status {
|
||||||
for (_ign, uuid) in online_players {
|
for (_ign, uuid) in online_players {
|
||||||
players.insert(MinecraftPlayer::from_uuid(ctx, uuid).await?, true);
|
players.insert(MinecraftPlayer::from_uuid(ctx, uuid).await?, true);
|
||||||
}
|
}
|
||||||
|
let world_info = RconHandle::cmd(&WorldInfoCmd, ctx).await?;
|
||||||
|
|
||||||
Ok(Status {
|
Ok(Status {
|
||||||
tps,
|
tps,
|
||||||
amount_online,
|
amount_online,
|
||||||
players,
|
players,
|
||||||
|
world_info,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +63,84 @@ impl Status {
|
||||||
let status = Point::new("status")
|
let status = Point::new("status")
|
||||||
.add_field("tps", self.tps.min1)
|
.add_field("tps", self.tps.min1)
|
||||||
// Safe, since we started with u32
|
// 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);
|
points.push(status);
|
||||||
let mut activity = Point::new("activity");
|
let mut activity = Point::new("activity");
|
||||||
for (player, is_online) in &self.players {
|
for (player, is_online) in &self.players {
|
||||||
|
|
118
src/tracking/status/world_info.rs
Normal file
118
src/tracking/status/world_info.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue