1.0.0 release
This commit is contained in:
commit
22767341f3
6 changed files with 1631 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/target
|
||||
1483
Cargo.lock
generated
Normal file
1483
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
11
Cargo.toml
Normal file
11
Cargo.toml
Normal 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
7
LICENSE.txt
Normal 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
21
README.md
Normal 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
108
src/main.rs
Normal 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(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue