M Cargo.lock => Cargo.lock +81 -6
@@ 428,7 428,7 @@ dependencies = [
"log",
"parking",
"polling",
- "rustix",
+ "rustix 0.37.20",
"slab",
"socket2",
"waker-fn",
@@ 1774,6 1774,12 @@ checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
[[package]]
name = "linux-raw-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
+
+[[package]]
+name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
@@ 2194,7 2200,7 @@ dependencies = [
"redox_syscall 0.3.5",
"smallvec",
"thread-id",
- "windows-targets",
+ "windows-targets 0.48.0",
]
[[package]]
@@ 2415,6 2421,36 @@ dependencies = [
]
[[package]]
+name = "procfs"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "hex",
+ "lazy_static",
+ "rustix 0.36.15",
+]
+
+[[package]]
+name = "prometheus"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c"
+dependencies = [
+ "cfg-if",
+ "fnv",
+ "lazy_static",
+ "libc",
+ "memchr",
+ "parking_lot 0.12.1",
+ "procfs",
+ "protobuf 2.28.0",
+ "thiserror",
+]
+
+[[package]]
name = "protobuf"
version = "2.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 2791,6 2827,20 @@ dependencies = [
[[package]]
name = "rustix"
+version = "0.36.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941"
+dependencies = [
+ "bitflags",
+ "errno",
+ "io-lifetimes",
+ "libc",
+ "linux-raw-sys 0.1.4",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "rustix"
version = "0.37.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0"
@@ 2799,7 2849,7 @@ dependencies = [
"errno",
"io-lifetimes",
"libc",
- "linux-raw-sys",
+ "linux-raw-sys 0.3.8",
"windows-sys 0.48.0",
]
@@ 3436,6 3486,7 @@ dependencies = [
"log",
"nalgebra",
"parking_lot 0.12.1",
+ "prometheus",
"protobuf 3.2.0",
"rand",
"rapier2d-f64",
@@ 3525,7 3576,7 @@ dependencies = [
"cfg-if",
"fastrand",
"redox_syscall 0.3.5",
- "rustix",
+ "rustix 0.37.20",
"windows-sys 0.48.0",
]
@@ 4207,7 4258,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
- "windows-targets",
+ "windows-targets 0.48.0",
]
[[package]]
@@ 4227,11 4278,35 @@ dependencies = [
[[package]]
name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets",
+ "windows-targets 0.48.0",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
]
[[package]]
M assets/dist/spritesheet-125.json => assets/dist/spritesheet-125.json +4 -4
@@ 63,7 63,7 @@
"pivot": { "x": 32, "y": 32 },
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
- "autoplr_error.png": {
+ "hearty.png": {
"frame": { "x": 0, "y": 1216, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
@@ 81,7 81,7 @@
"pivot": { "x": 32, "y": 32 },
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
- "hearty.png": {
+ "autoplr_error.png": {
"frame": { "x": 0, "y": 1344, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
@@ 144,7 144,7 @@
"pivot": { "x": 32, "y": 32 },
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
- "autoplr_on.png": {
+ "powerhub_on.png": {
"frame": { "x": 0, "y": 1792, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
@@ 153,7 153,7 @@
"pivot": { "x": 32, "y": 32 },
"9slicedFrame": { "x": 0, "y": 0, "w": 64, "h": 64 }
},
- "powerhub_on.png": {
+ "autoplr_on.png": {
"frame": { "x": 0, "y": 1856, "w": 64, "h": 64 },
"rotated": false,
"trimmed": false,
M assets/dist/spritesheet-125.png => assets/dist/spritesheet-125.png +0 -0
M assets/dist/spritesheet-375.json => assets/dist/spritesheet-375.json +5 -5
@@ 63,7 63,7 @@
"pivot": { "x": 96, "y": 96 },
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
- "autoplr_cfg.png": {
+ "hearty.png": {
"frame": { "x": 0, "y": 3648, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
@@ 72,7 72,7 @@
"pivot": { "x": 96, "y": 96 },
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
- "autoplr_error.png": {
+ "autoplr_cfg.png": {
"frame": { "x": 0, "y": 3840, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
@@ 81,7 81,7 @@
"pivot": { "x": 96, "y": 96 },
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
- "hearty.png": {
+ "autoplr_error.png": {
"frame": { "x": 0, "y": 4032, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
@@ 135,7 135,7 @@
"pivot": { "x": 96, "y": 96 },
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
- "powerhub_on.png": {
+ "hub_on.png": {
"frame": { "x": 0, "y": 5184, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
@@ 144,7 144,7 @@
"pivot": { "x": 96, "y": 96 },
"9slicedFrame": { "x": 0, "y": 0, "w": 192, "h": 192 }
},
- "hub_on.png": {
+ "powerhub_on.png": {
"frame": { "x": 0, "y": 5376, "w": 192, "h": 192 },
"rotated": false,
"trimmed": false,
M assets/dist/spritesheet-375.png => assets/dist/spritesheet-375.png +0 -0
M assets/dist/spritesheet-full.json => assets/dist/spritesheet-full.json +3 -3
@@ 63,7 63,7 @@
"pivot": { "x": 256, "y": 256 },
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
- "autoplr_error.png": {
+ "hearty.png": {
"frame": { "x": 512, "y": 6144, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
@@ 72,7 72,7 @@
"pivot": { "x": 256, "y": 256 },
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
- "hearty.png": {
+ "autoplr_cfg.png": {
"frame": { "x": 512, "y": 6656, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
@@ 81,7 81,7 @@
"pivot": { "x": 256, "y": 256 },
"9slicedFrame": { "x": 0, "y": 0, "w": 512, "h": 512 }
},
- "autoplr_cfg.png": {
+ "autoplr_error.png": {
"frame": { "x": 512, "y": 7168, "w": 512, "h": 512 },
"rotated": false,
"trimmed": false,
M assets/dist/spritesheet-full.png => assets/dist/spritesheet-full.png +0 -0
M server/Cargo.toml => server/Cargo.toml +2 -0
@@ 32,5 32,7 @@ protobuf = "3.2.0"
parking_lot = { version = "0.12.1", features = ["deadlock_detection"] }
+prometheus = { version = "0.13", features = ["process"] }
+
[build-dependencies]
cargo_metadata = "0.15"
M server/src/entity.rs => server/src/entity.rs +9 -1
@@ 13,7 13,8 @@ use crate::{
pub type EntityId = u32;
pub type Entities = HashMap<EntityId, Entity>;
-static mut ENTITY_ID_COUNT: AtomicU32 = AtomicU32::new(0);
+pub static mut ENTITY_ID_COUNT: AtomicU32 = AtomicU32::new(0);
+
pub fn get_entity_id() -> EntityId {
let last_entity_id = unsafe { &ENTITY_ID_COUNT };
let id = last_entity_id.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
@@ 21,6 22,13 @@ pub fn get_entity_id() -> EntityId {
id
}
+pub fn get_last_entity_id() -> EntityId {
+ let last_entity_id = unsafe { &ENTITY_ID_COUNT };
+ let id = last_entity_id.fetch_add(0, std::sync::atomic::Ordering::AcqRel);
+ assert!(id <= 4_294_967_290, "No remaining entity ids");
+ id
+}
+
#[derive(Default)]
pub struct EntityHandler {
pub entities: Entities,
M server/src/main.rs => server/src/main.rs +59 -1
@@ 35,6 35,7 @@ use std::collections::HashMap;
use std::net::SocketAddr;
use std::thread;
use std::time::Duration;
+use prometheus::{Gauge, register_gauge, opts, labels, TextEncoder, Counter, register_counter};
use crate::tcp_handler::handle_request;
pub mod handler;
@@ 75,6 76,59 @@ lazy_static! {
ccd_solver: CCDSolver::new(),
}));
static ref ENTITIES: Arc<RwLock<EntityHandler>> = Arc::new(RwLock::new(EntityHandler::new()));
+
+ // Prometheus stuff //
+
+ static ref ENCODER: TextEncoder = TextEncoder::new();
+
+ static ref BUILD_INFO_GAGUE: Gauge = register_gauge!(
+ opts!("starkingdoms_build_info",
+ "A constant gague with value 1 containing build information in the labels.",
+ labels! {
+ "version_name" => env!("STK_VERSION_NAME"),
+ "version" => env!("STK_VERSION"),
+ "protocol" => stringify!(PROTOCOL_VERSION),
+ "channel" => env!("STK_CHANNEL"),
+ "build" => env!("STK_BUILD"),
+ "server_description" => env!("STK_SLP_DESCRIPTION")
+ }
+ )
+ ).unwrap();
+
+ static ref ONLINE_PLAYERS_GAGUE: Gauge = register_gauge!(
+ opts!(
+ "starkingdoms_online_players",
+ "Number of players currently online"
+ )
+ ).unwrap();
+
+ static ref CMGR_HANDLERS_GAUGE: Gauge = register_gauge!(
+ opts!(
+ "starkingdoms_cmgr_handlers",
+ "Number of active handlers in the client manager"
+ )
+ ).unwrap();
+
+ static ref CMGR_USERNAMES_GAUGE: Gauge = register_gauge!(
+ opts!(
+ "starkingdoms_cmgr_usernames",
+ "Number of active usernames in the client manager"
+ )
+ ).unwrap();
+
+ static ref ENTITIES_GAUGE: Gauge = register_gauge!(
+ opts!(
+ "starkingdoms_entities",
+ "Number of entities in the entity manager"
+ )
+ ).unwrap();
+
+ static ref ENTITY_ID_COUNTER: Counter = register_counter!(
+ opts!(
+ "starkingdoms_entity_id",
+ "Number of entities ever created in the entity manager"
+ )
+ ).unwrap();
}
pub const PANIC_ON_DEADLOCK: bool = true;
@@ 93,7 147,11 @@ async fn main() {
env!("STK_BUILD")
);
- info!("{}", std::mem::size_of::<Entity>());
+ // Initialize metrics //
+
+ BUILD_INFO_GAGUE.inc(); // Create the build info metric
+
+ // All done with metrics //
if std::env::var("STK_API_KEY").is_err() {
error!(
M server/src/tcp_handler.rs => server/src/tcp_handler.rs +43 -3
@@ 6,11 6,11 @@ use async_std::net::TcpStream;
use async_std::sync::RwLock;
use log::{error, info, warn};
use starkingdoms_protocol::PROTOCOL_VERSION;
-use crate::entity::{Entity, EntityHandler};
+use crate::entity::{Entity, ENTITY_ID_COUNT, EntityHandler, get_last_entity_id};
use crate::manager::{ClientHandler, ClientManager, PhysicsData};
-use crate::{CMGR, ServerPingResponse, ServerPingResponseVersion};
+use crate::{CMGR, CMGR_HANDLERS_GAUGE, CMGR_USERNAMES_GAUGE, ENCODER, ENTITIES, ENTITIES_GAUGE, ENTITY_ID_COUNTER, ONLINE_PLAYERS_GAGUE, ServerPingResponse, ServerPingResponseVersion};
use crate::handler::handle_client;
-use futures::StreamExt;
+use futures::{StreamExt};
pub async fn handle_request(
conn: TcpStream,
@@ 71,6 71,46 @@ async fn _handle_request(
ping_resp.len()
);
return Ok(());
+ } else if peek_buf == *b"GET /stat" {
+ info!("[{}] incoming metrics request", remote_addr);
+
+ // These blocks are separated on purpose to prevent deadlocks.
+ // DO NOT COMBINE THEM.
+
+ #[allow(clippy::cast_precision_loss)] {
+ ONLINE_PLAYERS_GAGUE.set(CMGR.usernames.read().await.len() as f64);
+ }
+ #[allow(clippy::cast_precision_loss)] {
+ CMGR_USERNAMES_GAUGE.set(CMGR.usernames.read().await.len() as f64);
+ CMGR_HANDLERS_GAUGE.set(CMGR.handlers.read().await.len() as f64);
+ ENTITIES_GAUGE.set(ENTITIES.read().await.entities.len() as f64);
+ }
+ #[allow(clippy::cast_precision_loss)] {
+ let current = ENTITY_ID_COUNTER.get();
+ let target = f64::from(get_last_entity_id());
+ let diff = target - current;
+ ENTITY_ID_COUNTER.inc_by(diff);
+ }
+
+ let stats = prometheus::gather();
+
+ let text = ENCODER.encode_to_string(&stats)?;
+
+ let resp_str = format!(
+ "HTTP/1.0 200 OK\nAccess-Control-Allow-Origin: *\nContent-Length: {}\n\n{}",
+ text.len(),
+ text
+ );
+
+ conn.write_all(resp_str.as_bytes()).await?;
+
+ info!(
+ "[{}] sent stats response (200 OK {} bytes)",
+ remote_addr,
+ text.len()
+ );
+
+ return Ok(());
}
info!("[{}] incoming websocket connection", remote_addr);