1.0.0 release

This commit is contained in:
june 2025-12-24 21:03:43 -08:00
commit 22767341f3
6 changed files with 1631 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

1483
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

11
Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "craftpinger"
version = "1.0.0"
edition = "2024"
[dependencies]
clap = {version = "4.5.53", features = ["derive"]}
confy = "2.0.0"
mc-server-status = "1.0.0"
serde = {version = "1.0.228", features = ["derive"]}
tokio = "1.48.0"

7
LICENSE.txt Normal file
View file

@ -0,0 +1,7 @@
Copyright (c) 2025 jhalfsharp
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

21
README.md Normal file
View file

@ -0,0 +1,21 @@
# CraftPinger
CraftPinger is a simple, command-line program to see if any minecraft servers have friends online, without having to open the game. It's primarily useful when you have multiple servers across multiple instances of the game, especially large modpacks that can take several minutes to load. I was primarily motivated to make this because most other tools for this purpose are only hosted as web tools, and thus cannot be configured at all, while also having no guarantees against tracking.
## USAGE
The config file is located at $HOME/.config/craftpinger/config.toml. Servers are added to the list to scan by adding to the list `[[servers]]`, as demonstrated by the two examples in the file.
To use, simply run `craftpinger` from the terminal. By default, servers with no players online will be hidden from the list; to change this, set `show_empty_servers` to `true` in the config file.
By default, the timeout for the request is 5 seconds; to change this, use `-t` or `--timeout`. Run `craftpinger --help` for details.
For scripts, consider spawning a new thread ahead of time to avoid blocking on network responses.
## DEPENDENCIES
To run, no dependencies are needed except a stable internet connection. Cargo is required in order to build from source.
## LICENSE
CraftPinger is licensed under the MIT license.

108
src/main.rs Normal file
View file

@ -0,0 +1,108 @@
use clap::Parser;
use mc_server_status::{McClient, ServerEdition, ServerInfo};
use serde::{Deserialize, Serialize};
use std::{time::Duration, vec};
/// Pings a list of minecraft servers and prints overviews to standard output.
///
/// On Linux, config is stored in $HOME/.config/scan-mc.toml.
#[derive(Parser)]
#[command(version, long_about)]
struct Cli {
/// How long to wait for a response from servers, in seconds.
#[arg(short, long)]
timeout: Option<u64>,
}
#[derive(Serialize, Deserialize)]
struct Config {
show_empty_servers: bool,
server: Vec<Server>,
}
impl ::std::default::Default for Config {
fn default() -> Self {
Self {
show_empty_servers: false,
server: vec![
Server {
ip: "mc.hypixel.net".into(),
name: "Hypixel".into(),
},
Server {
ip: "play.wynncraft.com".into(),
name: "Wynncraft".into(),
},
],
}
}
}
#[derive(Serialize, Deserialize)]
struct Server {
ip: String,
name: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let cli = Cli::parse();
let cfg: Config = confy::load("craftpinger", Some("config"))?;
let client = McClient::new()
.with_timeout(Duration::from_secs(cli.timeout.unwrap_or(5)))
.with_max_parallel(100);
// Batch check servers
let mut servers = vec![];
for server in cfg.server.iter() {
servers.push(ServerInfo {
address: server.ip.clone(),
edition: ServerEdition::Java,
});
}
let results = client.ping_many(&servers).await;
let data: Vec<_> = cfg
.server
.iter()
.filter_map(|server| {
Some((
server.name.clone(),
results.iter().find(|(i, _status)| i.address == server.ip)?,
))
})
.filter_map(|(name, (info, status))| {
Some((
name,
info.address.clone(),
(match &status.as_ref().ok()?.data {
mc_server_status::ServerData::Java(j) => Some(j),
_default => None,
})?,
))
})
.map(|(name, addr, data)| (name, addr, data.players.online, data.players.max))
.collect();
let min_spacing = 3;
let pad_length = data
.iter()
.map(|(name, addr, _, _)| name.len() + addr.len())
.max();
for (name, addr, online, max) in data {
if online == 0 && !cfg.show_empty_servers {
continue;
}
let pad_len =
pad_length.expect("data must be non-empty") + min_spacing - name.len() - addr.len();
let pad = format!("{:1$}", "", pad_len);
println!("{}{}{} - {} / {}", name, pad, addr, online, max);
}
Ok(())
}