mirror of
https://github.com/timvisee/lazymc.git
synced 2025-05-19 12:50:23 -07:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d058164aa6 | ||
|
8ed68b1ddf | ||
|
0eec1e0b55 | ||
|
485941cf81 | ||
|
a1008ad2a7 | ||
|
b22d32b951 | ||
|
18f26b00cb | ||
|
86428f4501 | ||
|
c311313ecb | ||
|
6e6d098cf1 | ||
|
efb047114e | ||
|
37fdb9c12a | ||
|
e7a3db19aa | ||
|
ca4753673d | ||
|
0124aa723d | ||
|
afbc54758c | ||
|
e54025f02f | ||
|
023e46fe64 | ||
|
6622962d5d | ||
|
da60287e10 | ||
|
eb5ee7defd | ||
|
fdeb7594c2 | ||
|
5c7e17b0ae | ||
|
be74e053f4 | ||
|
eb2cf1219e |
@ -7,11 +7,6 @@ stages:
|
|||||||
- pre-release
|
- pre-release
|
||||||
- release
|
- release
|
||||||
|
|
||||||
default:
|
|
||||||
tags:
|
|
||||||
- linux
|
|
||||||
- timvisee-linux
|
|
||||||
|
|
||||||
# Variable defaults
|
# Variable defaults
|
||||||
variables:
|
variables:
|
||||||
RUST_VERSION: stable
|
RUST_VERSION: stable
|
||||||
@ -57,7 +52,7 @@ check-stable:
|
|||||||
check-msrv:
|
check-msrv:
|
||||||
<<: *check-base
|
<<: *check-base
|
||||||
variables:
|
variables:
|
||||||
RUST_VERSION: 1.64.0
|
RUST_VERSION: 1.74.0
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,5 +1,21 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.2.11 (2024-03-16)
|
||||||
|
|
||||||
|
- Add support for Minecraft 1.20.3 and 1.20.4
|
||||||
|
- Improve error handling of parsing server favicon
|
||||||
|
- Fix typo in log message
|
||||||
|
- Update dependencies
|
||||||
|
|
||||||
|
## 0.2.10 (2023-02-20)
|
||||||
|
|
||||||
|
- Do not report an error when server exits with status code 143
|
||||||
|
|
||||||
|
## 0.2.9 (2023-02-14)
|
||||||
|
|
||||||
|
- Fix dropping all connections when `server.drop_banned_ips` was enabled
|
||||||
|
- Update dependencies
|
||||||
|
|
||||||
## 0.2.8 (2023-01-30)
|
## 0.2.8 (2023-01-30)
|
||||||
|
|
||||||
- Add `freeze_process` feature on Unix platforms to freeze a sleeping server
|
- Add `freeze_process` feature on Unix platforms to freeze a sleeping server
|
||||||
|
1469
Cargo.lock
generated
1469
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
24
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lazymc"
|
name = "lazymc"
|
||||||
version = "0.2.8"
|
version = "0.2.11"
|
||||||
authors = ["Tim Visee <3a4fb3964f@sinenomine.email>"]
|
authors = ["Tim Visee <3a4fb3964f@sinenomine.email>"]
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@ -11,6 +11,7 @@ keywords = ["minecraft", "server", "idle", "cli"]
|
|||||||
categories = ["command-line-interface", "games"]
|
categories = ["command-line-interface", "games"]
|
||||||
exclude = ["/.github", "/contrib"]
|
exclude = ["/.github", "/contrib"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
rust-version = "1.74.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
@ -23,7 +24,7 @@ default = ["rcon", "lobby"]
|
|||||||
# RCON support
|
# RCON support
|
||||||
# Allow use of RCON to manage (stop) server.
|
# Allow use of RCON to manage (stop) server.
|
||||||
# Required on Windows.
|
# Required on Windows.
|
||||||
rcon = ["rust_rcon", "async-std"]
|
rcon = ["rust_rcon"]
|
||||||
|
|
||||||
# Lobby support
|
# Lobby support
|
||||||
# Add lobby join method, keeps client in fake lobby world until server is ready.
|
# Add lobby join method, keeps client in fake lobby world until server is ready.
|
||||||
@ -31,7 +32,7 @@ lobby = ["md-5", "uuid"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
base64 = "0.21"
|
base64 = "0.22"
|
||||||
bytes = "1.1"
|
bytes = "1.1"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
clap = { version = "4.0.32", default-features = false, features = [
|
clap = { version = "4.0.32", default-features = false, features = [
|
||||||
@ -45,16 +46,16 @@ clap = { version = "4.0.32", default-features = false, features = [
|
|||||||
"unicode",
|
"unicode",
|
||||||
] }
|
] }
|
||||||
colored = "2.0"
|
colored = "2.0"
|
||||||
derive_builder = "0.12"
|
derive_builder = "0.20"
|
||||||
dotenv = "0.15"
|
dotenv = "0.15"
|
||||||
flate2 = { version = "1.0", default-features = false, features = ["default"] }
|
flate2 = { version = "1.0", default-features = false, features = ["default"] }
|
||||||
futures = { version = "0.3", default-features = false, features = ["executor"] }
|
futures = { version = "0.3", default-features = false, features = ["executor"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
minecraft-protocol = { git = "https://github.com/timvisee/rust-minecraft-protocol", rev = "edfdf87" }
|
minecraft-protocol = { git = "https://github.com/timvisee/rust-minecraft-protocol", rev = "4f93bb3" }
|
||||||
named-binary-tag = "0.6"
|
named-binary-tag = "0.6"
|
||||||
nix = "0.26"
|
nix = { version = "0.28", features = ["process", "signal"] }
|
||||||
notify = "4.0"
|
notify = "4.0"
|
||||||
pretty_env_logger = "0.4"
|
pretty_env_logger = "0.5"
|
||||||
proxy-protocol = "0.5"
|
proxy-protocol = "0.5"
|
||||||
quartz_nbt = "0.2"
|
quartz_nbt = "0.2"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
@ -73,16 +74,15 @@ tokio = { version = "1", default-features = false, features = [
|
|||||||
"sync",
|
"sync",
|
||||||
"fs",
|
"fs",
|
||||||
] }
|
] }
|
||||||
toml = "0.5"
|
toml = "0.8"
|
||||||
version-compare = "0.1"
|
version-compare = "0.2"
|
||||||
|
|
||||||
# Feature: rcon
|
# Feature: rcon
|
||||||
rust_rcon = { package = "rcon", version = "0.5.2", optional = true }
|
rust_rcon = { package = "rcon", version = "0.6", default-features = false, features = ["rt-tokio"], optional = true }
|
||||||
async-std = { version = "1.9.0", default-features = false, optional = true }
|
|
||||||
|
|
||||||
# Feature: lobby
|
# Feature: lobby
|
||||||
md-5 = { version = "0.10", optional = true }
|
md-5 = { version = "0.10", optional = true }
|
||||||
uuid = { version = "0.7", optional = true, features = ["v3"] }
|
uuid = { version = "1.7", optional = true, features = ["v3"] }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
@ -35,7 +35,7 @@ https://user-images.githubusercontent.com/856222/141378688-882082be-9efa-4cfe-81
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Very efficient, lightweight & low-profile (~3KB RAM)
|
- Very efficient, lightweight & low-profile (~3KB RAM)
|
||||||
- Supports Minecraft Java Edition 1.7.2+, supports modded (e.g. Forge, FTB)
|
- Supports Minecraft Java Edition 1.20.3+
|
||||||
- Configure joining client occupation methods:
|
- Configure joining client occupation methods:
|
||||||
- Hold: hold clients when server starts, relay when ready, without them noticing
|
- Hold: hold clients when server starts, relay when ready, without them noticing
|
||||||
- Kick: kick clients when server starts, with a starting message
|
- Kick: kick clients when server starts, with a starting message
|
||||||
@ -57,7 +57,7 @@ https://user-images.githubusercontent.com/856222/141378688-882082be-9efa-4cfe-81
|
|||||||
|
|
||||||
Build requirements:
|
Build requirements:
|
||||||
|
|
||||||
- Rust 1.64 (MSRV)
|
- Rust 1.74 (MSRV)
|
||||||
|
|
||||||
_Note: You must have access to the system to run the `lazymc` binary. If you're
|
_Note: You must have access to the system to run the `lazymc` binary. If you're
|
||||||
using a Minecraft shared hosting provider with a custom dashboard, you likely
|
using a Minecraft shared hosting provider with a custom dashboard, you likely
|
||||||
|
1
clippy.toml
Normal file
1
clippy.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
msrv = "1.64.0"
|
@ -18,8 +18,8 @@
|
|||||||
# Server version & protocol hint.
|
# Server version & protocol hint.
|
||||||
# Sent to clients until actual server version is known.
|
# Sent to clients until actual server version is known.
|
||||||
# See: https://git.io/J1Fvx
|
# See: https://git.io/J1Fvx
|
||||||
#version = "1.19.3"
|
#version = "1.20.3"
|
||||||
#protocol = 761
|
#protocol = 765
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
# Server address. Internal IP and port of server started by lazymc to proxy to.
|
# Server address. Internal IP and port of server started by lazymc to proxy to.
|
||||||
@ -187,4 +187,4 @@ command = "java -Xmx1G -Xms1G -jar server.jar --nogui"
|
|||||||
[config]
|
[config]
|
||||||
# lazymc version this configuration is for.
|
# lazymc version this configuration is for.
|
||||||
# Don't change unless you know what you're doing.
|
# Don't change unless you know what you're doing.
|
||||||
version = "0.2.8"
|
version = "0.2.11"
|
||||||
|
@ -108,8 +108,8 @@ pub struct Config {
|
|||||||
impl Config {
|
impl Config {
|
||||||
/// Load configuration from file.
|
/// Load configuration from file.
|
||||||
pub fn load(path: PathBuf) -> Result<Self, io::Error> {
|
pub fn load(path: PathBuf) -> Result<Self, io::Error> {
|
||||||
let data = fs::read(&path)?;
|
let data = fs::read_to_string(&path)?;
|
||||||
let mut config: Config = toml::from_slice(&data)?;
|
let mut config: Config = toml::from_str(&data).map_err(io::Error::other)?;
|
||||||
|
|
||||||
// Show warning if config version is problematic
|
// Show warning if config version is problematic
|
||||||
match &config.config.version {
|
match &config.config.version {
|
||||||
|
@ -652,7 +652,7 @@ async fn drain_stream(reader: &mut ReadHalf<'_>) -> Result<(), ()> {
|
|||||||
let mut drain_buf = [0; 8 * 1024];
|
let mut drain_buf = [0; 8 * 1024];
|
||||||
loop {
|
loop {
|
||||||
match reader.try_read(&mut drain_buf) {
|
match reader.try_read(&mut drain_buf) {
|
||||||
Ok(read) if read == 0 => return Ok(()),
|
Ok(0) => return Ok(()),
|
||||||
Err(err) if err.kind() == ErrorKind::WouldBlock => return Ok(()),
|
Err(err) if err.kind() == ErrorKind::WouldBlock => return Ok(()),
|
||||||
Ok(_) => continue,
|
Ok(_) => continue,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_std::net::TcpStream;
|
|
||||||
use async_std::prelude::*;
|
|
||||||
use rust_rcon::{Connection, Error as RconError};
|
use rust_rcon::{Connection, Error as RconError};
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
use tokio::net::TcpStream;
|
||||||
use tokio::time;
|
use tokio::time;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
@ -17,7 +17,7 @@ const QUIRK_RCON_GRACE_TIME: Duration = Duration::from_millis(200);
|
|||||||
|
|
||||||
/// An RCON client.
|
/// An RCON client.
|
||||||
pub struct Rcon {
|
pub struct Rcon {
|
||||||
con: Connection,
|
con: Connection<TcpStream>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rcon {
|
impl Rcon {
|
||||||
@ -39,7 +39,7 @@ impl Rcon {
|
|||||||
// Start connection
|
// Start connection
|
||||||
let con = Connection::builder()
|
let con = Connection::builder()
|
||||||
.enable_minecraft_quirks(true)
|
.enable_minecraft_quirks(true)
|
||||||
.connect_stream(stream, pass)
|
.handshake(stream, pass)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Self { con })
|
Ok(Self { con })
|
||||||
|
@ -122,7 +122,7 @@ fn rewrite_contents(contents: String, mut changes: HashMap<&str, String>) -> Opt
|
|||||||
// Take any new value, and update it
|
// Take any new value, and update it
|
||||||
if let Some((_, new)) = changes.remove_entry(key.trim().to_lowercase().as_str()) {
|
if let Some((_, new)) = changes.remove_entry(key.trim().to_lowercase().as_str()) {
|
||||||
if value != new {
|
if value != new {
|
||||||
line = format!("{}={}", key, new);
|
line = format!("{key}={new}");
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,7 +134,7 @@ fn rewrite_contents(contents: String, mut changes: HashMap<&str, String>) -> Opt
|
|||||||
|
|
||||||
// Append any missed changes
|
// Append any missed changes
|
||||||
for (key, value) in changes {
|
for (key, value) in changes {
|
||||||
new_contents += &format!("{}{}={}", EOL, key, value);
|
new_contents += &format!("{EOL}{key}={value}");
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ fn player_uuid(username: &str) -> Uuid {
|
|||||||
|
|
||||||
/// Get UUID for given offline player username.
|
/// Get UUID for given offline player username.
|
||||||
pub fn offline_player_uuid(username: &str) -> Uuid {
|
pub fn offline_player_uuid(username: &str) -> Uuid {
|
||||||
player_uuid(&format!("{}{}", OFFLINE_PLAYER_NAMESPACE, username))
|
player_uuid(&format!("{OFFLINE_PLAYER_NAMESPACE}{username}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Java's `UUID.nameUUIDFromBytes`
|
/// Java's `UUID.nameUUIDFromBytes`
|
||||||
|
@ -3,11 +3,10 @@ use std::sync::Arc;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use minecraft_protocol::data::server_status::ServerStatus;
|
|
||||||
use minecraft_protocol::decoder::Decoder;
|
use minecraft_protocol::decoder::Decoder;
|
||||||
use minecraft_protocol::version::v1_14_4::handshake::Handshake;
|
use minecraft_protocol::version::v1_14_4::handshake::Handshake;
|
||||||
use minecraft_protocol::version::v1_14_4::status::{
|
use minecraft_protocol::version::v1_20_3::status::{
|
||||||
PingRequest, PingResponse, StatusRequest, StatusResponse,
|
PingRequest, PingResponse, ServerStatus, StatusRequest, StatusResponse,
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
@ -57,13 +56,13 @@ pub async fn monitor_server(config: Arc<Config>, server: Arc<Server>) {
|
|||||||
|
|
||||||
// Sleep server when it's bedtime
|
// Sleep server when it's bedtime
|
||||||
if server.should_sleep(&config).await {
|
if server.should_sleep(&config).await {
|
||||||
info!(target: "lazymc::montior", "Server has been idle, sleeping...");
|
info!(target: "lazymc::monitor", "Server has been idle, sleeping...");
|
||||||
server.stop(&config).await;
|
server.stop(&config).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether we should force kill server
|
// Check whether we should force kill server
|
||||||
if server.should_kill().await {
|
if server.should_kill().await {
|
||||||
error!(target: "lazymc::montior", "Force killing server, took too long to start or stop");
|
error!(target: "lazymc::monitor", "Force killing server, took too long to start or stop");
|
||||||
if !server.force_kill().await {
|
if !server.force_kill().await {
|
||||||
warn!(target: "lazymc", "Failed to force kill server");
|
warn!(target: "lazymc", "Failed to force kill server");
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
pub mod windows;
|
pub mod windows;
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use nix::{sys::signal, unistd::Pid};
|
use nix::{
|
||||||
|
sys::signal::{self, Signal},
|
||||||
|
unistd::Pid,
|
||||||
|
};
|
||||||
|
|
||||||
/// Force kill process.
|
/// Force kill process.
|
||||||
///
|
///
|
||||||
@ -10,7 +13,7 @@ use nix::{sys::signal, unistd::Pid};
|
|||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
pub fn force_kill(pid: u32) -> bool {
|
pub fn force_kill(pid: u32) -> bool {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
return unix_signal(pid, signal::SIGKILL);
|
return unix_signal(pid, Signal::SIGKILL);
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -28,7 +31,7 @@ pub fn force_kill(pid: u32) -> bool {
|
|||||||
#[allow(unreachable_code, dead_code, unused_variables)]
|
#[allow(unreachable_code, dead_code, unused_variables)]
|
||||||
pub fn kill_gracefully(pid: u32) -> bool {
|
pub fn kill_gracefully(pid: u32) -> bool {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
return unix_signal(pid, signal::SIGTERM);
|
return unix_signal(pid, Signal::SIGTERM);
|
||||||
|
|
||||||
unimplemented!(
|
unimplemented!(
|
||||||
"gracefully killing Minecraft server process not implemented on non-Unix platforms"
|
"gracefully killing Minecraft server process not implemented on non-Unix platforms"
|
||||||
@ -43,7 +46,7 @@ pub fn kill_gracefully(pid: u32) -> bool {
|
|||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
pub fn freeze(pid: u32) -> bool {
|
pub fn freeze(pid: u32) -> bool {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
return unix_signal(pid, signal::SIGSTOP);
|
return unix_signal(pid, Signal::SIGSTOP);
|
||||||
|
|
||||||
unimplemented!(
|
unimplemented!(
|
||||||
"freezing the Minecraft server process is not implemented on non-Unix platforms"
|
"freezing the Minecraft server process is not implemented on non-Unix platforms"
|
||||||
@ -58,7 +61,7 @@ pub fn freeze(pid: u32) -> bool {
|
|||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
pub fn unfreeze(pid: u32) -> bool {
|
pub fn unfreeze(pid: u32) -> bool {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
return unix_signal(pid, signal::SIGCONT);
|
return unix_signal(pid, Signal::SIGCONT);
|
||||||
|
|
||||||
unimplemented!(
|
unimplemented!(
|
||||||
"unfreezing the Minecraft server process is not implemented on non-Unix platforms"
|
"unfreezing the Minecraft server process is not implemented on non-Unix platforms"
|
||||||
@ -66,12 +69,12 @@ pub fn unfreeze(pid: u32) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub fn unix_signal(pid: u32, signal: signal::Signal) -> bool {
|
pub fn unix_signal(pid: u32, signal: Signal) -> bool {
|
||||||
return match signal::kill(Pid::from_raw(pid as i32), signal) {
|
match signal::kill(Pid::from_raw(pid as i32), signal) {
|
||||||
Ok(()) => true,
|
Ok(()) => true,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!(target: "lazymc", "Sending {signal} signal to server failed: {err}");
|
warn!(target: "lazymc", "Sending {signal} signal to server failed: {err}");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ pub mod packets;
|
|||||||
/// in the configuration.
|
/// in the configuration.
|
||||||
///
|
///
|
||||||
/// Should be kept up-to-date with latest supported Minecraft version by lazymc.
|
/// Should be kept up-to-date with latest supported Minecraft version by lazymc.
|
||||||
pub const PROTO_DEFAULT_VERSION: &str = "1.19.3";
|
pub const PROTO_DEFAULT_VERSION: &str = "1.20.3";
|
||||||
|
|
||||||
/// Default minecraft protocol version.
|
/// Default minecraft protocol version.
|
||||||
///
|
///
|
||||||
@ -17,7 +17,7 @@ pub const PROTO_DEFAULT_VERSION: &str = "1.19.3";
|
|||||||
/// in the configuration.
|
/// in the configuration.
|
||||||
///
|
///
|
||||||
/// Should be kept up-to-date with latest supported Minecraft version by lazymc.
|
/// Should be kept up-to-date with latest supported Minecraft version by lazymc.
|
||||||
pub const PROTO_DEFAULT_PROTOCOL: u32 = 761;
|
pub const PROTO_DEFAULT_PROTOCOL: u32 = 765;
|
||||||
|
|
||||||
/// Compression threshold to use.
|
/// Compression threshold to use.
|
||||||
// TODO: read this from server.properties instead
|
// TODO: read this from server.properties instead
|
||||||
|
@ -70,7 +70,7 @@ async fn send_v1_16_3(
|
|||||||
packet::write_packet(
|
packet::write_packet(
|
||||||
Title {
|
Title {
|
||||||
action: if title.is_empty() && subtitle.is_empty() {
|
action: if title.is_empty() && subtitle.is_empty() {
|
||||||
// Defaults: https://minecraft.fandom.com/wiki/Commands/title#Detail
|
// Defaults: https://minecraft.wiki/w/Commands/title#Detail
|
||||||
TitleAction::SetTimesAndDisplay {
|
TitleAction::SetTimesAndDisplay {
|
||||||
fade_in: 10,
|
fade_in: 10,
|
||||||
stay: 70,
|
stay: 70,
|
||||||
@ -121,7 +121,7 @@ async fn send_v1_17(
|
|||||||
// Set title times
|
// Set title times
|
||||||
packet::write_packet(
|
packet::write_packet(
|
||||||
if title.is_empty() && subtitle.is_empty() {
|
if title.is_empty() && subtitle.is_empty() {
|
||||||
// Defaults: https://minecraft.fandom.com/wiki/Commands/title#Detail
|
// Defaults: https://minecraft.wiki/w/Commands/title#Detail
|
||||||
SetTitleTimes {
|
SetTitleTimes {
|
||||||
fade_in: 10,
|
fade_in: 10,
|
||||||
stay: 70,
|
stay: 70,
|
||||||
|
@ -4,7 +4,7 @@ use std::sync::Arc;
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use minecraft_protocol::data::server_status::ServerStatus;
|
use minecraft_protocol::version::v1_20_3::status::ServerStatus;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
#[cfg(feature = "rcon")]
|
#[cfg(feature = "rcon")]
|
||||||
@ -29,9 +29,11 @@ const SERVER_QUIT_COOLDOWN: Duration = Duration::from_millis(2500);
|
|||||||
#[cfg(feature = "rcon")]
|
#[cfg(feature = "rcon")]
|
||||||
const RCON_COOLDOWN: Duration = Duration::from_secs(15);
|
const RCON_COOLDOWN: Duration = Duration::from_secs(15);
|
||||||
|
|
||||||
/// Exit code when SIGTERM is received on Unix.
|
/// Exit codes that are allowed.
|
||||||
#[cfg(unix)]
|
///
|
||||||
const UNIX_EXIT_SIGTERM: i32 = 130;
|
/// - 143: https://github.com/timvisee/lazymc/issues/26#issuecomment-1435670029
|
||||||
|
/// - 130: https://unix.stackexchange.com/q/386836/61092
|
||||||
|
const ALLOWED_EXIT_CODES: [i32; 2] = [130, 143];
|
||||||
|
|
||||||
/// Shared server state.
|
/// Shared server state.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -496,8 +498,12 @@ pub async fn invoke_server_cmd(
|
|||||||
debug!(target: "lazymc", "Server process stopped successfully ({})", status);
|
debug!(target: "lazymc", "Server process stopped successfully ({})", status);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
#[cfg(unix)]
|
Ok(status)
|
||||||
Ok(status) if status.code() == Some(UNIX_EXIT_SIGTERM) => {
|
if status
|
||||||
|
.code()
|
||||||
|
.map(|ref code| ALLOWED_EXIT_CODES.contains(code))
|
||||||
|
.unwrap_or(false) =>
|
||||||
|
{
|
||||||
debug!(target: "lazymc", "Server process stopped successfully by SIGTERM ({})", status);
|
debug!(target: "lazymc", "Server process stopped successfully by SIGTERM ({})", status);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ pub fn service(config: Arc<Config>, server: Arc<Server>) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Keep watching
|
// Keep watching
|
||||||
#[allow(clippy::blocks_in_if_conditions)]
|
#[allow(clippy::blocks_in_conditions)]
|
||||||
while {
|
while {
|
||||||
// Update all files once
|
// Update all files once
|
||||||
reload_bans(&config, &server, &dir.join(ban::FILE));
|
reload_bans(&config, &server, &dir.join(ban::FILE));
|
||||||
|
@ -84,7 +84,7 @@ fn route(inbound: TcpStream, config: Arc<Config>, server: Arc<Server>) {
|
|||||||
|
|
||||||
// Check ban state, just drop connection if enabled
|
// Check ban state, just drop connection if enabled
|
||||||
let banned = server.is_banned_ip_blocking(&peer.ip());
|
let banned = server.is_banned_ip_blocking(&peer.ip());
|
||||||
if config.server.drop_banned_ips {
|
if banned && config.server.drop_banned_ips {
|
||||||
info!(target: "lazymc", "Connection from banned IP {}, dropping", peer.ip());
|
info!(target: "lazymc", "Connection from banned IP {}, dropping", peer.ip());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use minecraft_protocol::data::chat::{Message, Payload};
|
use minecraft_protocol::data::server_status::{OnlinePlayers, ServerVersion};
|
||||||
use minecraft_protocol::data::server_status::*;
|
|
||||||
use minecraft_protocol::decoder::Decoder;
|
use minecraft_protocol::decoder::Decoder;
|
||||||
use minecraft_protocol::encoder::Encoder;
|
use minecraft_protocol::encoder::Encoder;
|
||||||
use minecraft_protocol::version::v1_14_4::handshake::Handshake;
|
use minecraft_protocol::version::v1_14_4::handshake::Handshake;
|
||||||
use minecraft_protocol::version::v1_14_4::login::LoginStart;
|
use minecraft_protocol::version::v1_14_4::login::LoginStart;
|
||||||
use minecraft_protocol::version::v1_14_4::status::StatusResponse;
|
use minecraft_protocol::version::v1_20_3::status::{ServerStatus, StatusResponse};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
@ -152,12 +151,8 @@ pub async fn serve(
|
|||||||
info!(target: "lazymc", "Login from banned IP {}, disconnecting", client.peer.ip());
|
info!(target: "lazymc", "Login from banned IP {}, disconnecting", client.peer.ip());
|
||||||
DEFAULT_BAN_REASON.to_string()
|
DEFAULT_BAN_REASON.to_string()
|
||||||
};
|
};
|
||||||
action::kick(
|
action::kick(&client, &format!("{BAN_MESSAGE_PREFIX}{msg}"), &mut writer)
|
||||||
&client,
|
.await?;
|
||||||
&format!("{}{}", BAN_MESSAGE_PREFIX, msg),
|
|
||||||
&mut writer,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,11 +231,11 @@ async fn server_status(client_info: &ClientInfo, config: &Config, server: &Serve
|
|||||||
if config.motd.from_server && status.is_some() {
|
if config.motd.from_server && status.is_some() {
|
||||||
status.as_ref().unwrap().description.clone()
|
status.as_ref().unwrap().description.clone()
|
||||||
} else {
|
} else {
|
||||||
Message::new(Payload::text(match server_state {
|
match server_state {
|
||||||
server::State::Stopped | server::State::Started => &config.motd.sleeping,
|
server::State::Stopped | server::State::Started => config.motd.sleeping.clone(),
|
||||||
server::State::Starting => &config.motd.starting,
|
server::State::Starting => config.motd.starting.clone(),
|
||||||
server::State::Stopping => &config.motd.stopping,
|
server::State::Stopping => config.motd.stopping.clone(),
|
||||||
}))
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -285,15 +280,10 @@ async fn server_favicon(config: &Config) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read icon data
|
// Read icon data
|
||||||
let data = match fs::read(path).await.map_err(|err| {
|
let data = fs::read(path).await.unwrap_or_else(|err| {
|
||||||
error!(target: "lazymc", "Failed to read favicon from {}: {}", SERVER_ICON_FILE, err);
|
error!(target: "lazymc::status", "Failed to read favicon from {}, using default: {err}", SERVER_ICON_FILE);
|
||||||
}) {
|
favicon::default_favicon().into_bytes()
|
||||||
Ok(data) => data,
|
});
|
||||||
Err(err) => {
|
|
||||||
error!(target: "lazymc::status", "Failed to load server icon from disk, using default: {:?}", err);
|
|
||||||
return favicon::default_favicon();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
favicon::encode_favicon(&data)
|
favicon::encode_favicon(&data)
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ use crate::util::error::{quit_error, ErrorHints};
|
|||||||
/// excluding the `:` suffix.
|
/// excluding the `:` suffix.
|
||||||
pub fn prompt(msg: &str) -> String {
|
pub fn prompt(msg: &str) -> String {
|
||||||
// Show the prompt
|
// Show the prompt
|
||||||
eprint!("{}: ", msg);
|
eprint!("{msg}: ");
|
||||||
let _ = stderr().flush();
|
let _ = stderr().flush();
|
||||||
|
|
||||||
// Get the input
|
// Get the input
|
||||||
@ -49,7 +49,7 @@ pub fn prompt_yes(msg: &str, def: Option<bool>) -> bool {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Get the user input
|
// Get the user input
|
||||||
let answer = prompt(&format!("{} {}", msg, options));
|
let answer = prompt(&format!("{msg} {options}"));
|
||||||
|
|
||||||
// Assume the default if the answer is empty
|
// Assume the default if the answer is empty
|
||||||
if answer.is_empty() {
|
if answer.is_empty() {
|
||||||
|
@ -16,7 +16,7 @@ pub fn print_error(err: anyhow::Error) {
|
|||||||
// Report each printable error, count them
|
// Report each printable error, count them
|
||||||
let count = err
|
let count = err
|
||||||
.chain()
|
.chain()
|
||||||
.map(|err| format!("{}", err))
|
.map(|err| err.to_string())
|
||||||
.filter(|err| !err.is_empty())
|
.filter(|err| !err.is_empty())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, err)| {
|
.map(|(i, err)| {
|
||||||
@ -126,7 +126,7 @@ impl ErrorHints {
|
|||||||
if self.config_generate {
|
if self.config_generate {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Use '{}' to generate a new config file",
|
"Use '{}' to generate a new config file",
|
||||||
highlight(&format!("{} config generate", bin))
|
highlight(&format!("{bin} config generate"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if self.config {
|
if self.config {
|
||||||
@ -138,7 +138,7 @@ impl ErrorHints {
|
|||||||
if self.config_test {
|
if self.config_test {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"Use '{}' to test a config file",
|
"Use '{}' to test a config file",
|
||||||
highlight(&format!("{} config test -c FILE", bin))
|
highlight(&format!("{bin} config test -c FILE"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if self.verbose {
|
if self.verbose {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user