Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
2af20945cc | ||
|
ad638b5b3e | ||
|
69c2d580d5 | ||
|
c6cd08c993 | ||
|
69812f5b55 | ||
|
f172587fd5 | ||
|
e6021502d9 | ||
|
157905f140 | ||
|
72d132ae8b | ||
|
7a00c2df9e | ||
|
fe3bf63401 | ||
|
e9e58a766b | ||
|
f9be5c5a0f | ||
|
7c7595dcd3 |
13
CHANGELOG.md
13
CHANGELOG.md
@@ -1,5 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.1 (2021-11-14)
|
||||
|
||||
- Make server sleeping errors more descriptive
|
||||
- Add server quit cooldown period, intended to prevent RCON errors due to RCON
|
||||
server thread something quitting after main server
|
||||
- Rewrite `enable-status = true` in `server.properties`
|
||||
- Rewrite `prevent-proxy-connections = false` in `server.properties` if
|
||||
Minecraft server has non-loopback address (other public IP)
|
||||
- Add compile from source instructions to README
|
||||
- Add Windows instructions to README
|
||||
- Update dependencies
|
||||
- Various fixes and improvements
|
||||
|
||||
## 0.1.0 (2021-11-11)
|
||||
|
||||
- Initial release
|
||||
|
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -614,7 +614,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazymc"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -668,7 +668,7 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
[[package]]
|
||||
name = "minecraft-protocol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/timvisee/minecraft-protocol?rev=31041b8#31041b8fe2bc7e512d12476b958c1fe9e9077394"
|
||||
source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=31041b8#31041b8fe2bc7e512d12476b958c1fe9e9077394"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"minecraft-protocol-derive",
|
||||
@@ -681,7 +681,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "minecraft-protocol-derive"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/timvisee/minecraft-protocol?rev=31041b8#31041b8fe2bc7e512d12476b958c1fe9e9077394"
|
||||
source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=31041b8#31041b8fe2bc7e512d12476b958c1fe9e9077394"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1077,9 +1077,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.69"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8"
|
||||
checksum = "e277c495ac6cd1a01a58d0a0c574568b4d1ddf14f59965c6a58b8d96400b54f3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lazymc"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
authors = ["Tim Visee <3a4fb3964f@sinenomine.email>"]
|
||||
license = "GPL-3.0"
|
||||
readme = "README.md"
|
||||
@@ -31,7 +31,7 @@ derive_builder = "0.10"
|
||||
dotenv = "0.15"
|
||||
futures = { version = "0.3", default-features = false }
|
||||
log = "0.4"
|
||||
minecraft-protocol = { git = "https://github.com/timvisee/minecraft-protocol", rev = "31041b8" }
|
||||
minecraft-protocol = { git = "https://github.com/timvisee/rust-minecraft-protocol", rev = "31041b8" }
|
||||
pretty_env_logger = "0.4"
|
||||
rand = "0.8"
|
||||
serde = "1.0"
|
||||
|
83
README.md
83
README.md
@@ -18,9 +18,8 @@ lazymc functions as proxy between clients and the server. It handles all
|
||||
incoming status connections until the server is started and then transparently
|
||||
relays/proxies the rest. All without them noticing.
|
||||
|
||||
_Note: this is a prototype and may be incomplete._
|
||||
https://user-images.githubusercontent.com/856222/141378688-882082be-9efa-4cfe-81cc-5a7ab8b8e86b.mp4
|
||||
|
||||
https://user-images.githubusercontent.com/856222/140804726-ba1a8e59-85d9-413b-8229-03be84b55d51.mp4
|
||||
|
||||
<details><summary>Click to see screenshots</summary>
|
||||
<p>
|
||||
@@ -55,28 +54,27 @@ won't be able to set this up._
|
||||
|
||||
## Usage
|
||||
|
||||
_Note: these instructions are for Linux & macOS, for Windows look
|
||||
[here](./docs/usage-windows.md)._
|
||||
|
||||
Make sure you meet all [requirements](#requirements).
|
||||
|
||||
_Note: Installation options are limited at this moment. Ready-to-go binaries
|
||||
will be published later. For now we compile and install from source._
|
||||
Download the appropriate binary for your system from the [latest
|
||||
release][latest-release] page.
|
||||
|
||||
To compile and install you need Rust, install it through `rustup`: https://rustup.rs/
|
||||
|
||||
When Rust is installed, compile and install `lazymc` from this git repository:
|
||||
Place the binary in your Minecraft server directory, rename it if you like.
|
||||
Open a terminal, go to the directory, and make sure you can invoke it:
|
||||
|
||||
```bash
|
||||
# Compile and install lazymc from source
|
||||
cargo install -f --git https://github.com/timvisee/lazymc
|
||||
|
||||
# Ensure lazymc works
|
||||
lazymc --help
|
||||
chmod a+x ./lazymc
|
||||
./lazymc --help
|
||||
```
|
||||
|
||||
When `lazymc` is available, change into your server directory. Then set up the
|
||||
[configuration](./res/lazymc.toml) and start it up:
|
||||
When `lazymc` is set-up, change into your server directory if you haven't
|
||||
already. Then set up the [configuration](./res/lazymc.toml) and start it up:
|
||||
|
||||
```bash
|
||||
# Change into your server directory
|
||||
# Change into your server directory (if you haven't already)
|
||||
cd server
|
||||
|
||||
# Generate lazymc configuration
|
||||
@@ -90,9 +88,62 @@ nano lazymc.toml
|
||||
lazymc start
|
||||
```
|
||||
|
||||
Everything should now be running. Connect with your Minecraft client to wake
|
||||
Before you use this in production, please ensure starting and stopping the
|
||||
server works as expected by connecting to it once. Watch `lazymc`s output while
|
||||
it starts and stops. If stopping results in errors, fix this first to prevent
|
||||
corrupting world/user data.
|
||||
|
||||
Follow this repository with the _Watch_ button on the top right to be notified of new releases.
|
||||
|
||||
Everything should now be ready to go! Connect with your Minecraft client to wake
|
||||
your server up!
|
||||
|
||||
_Note: If a binary for your system isn't provided, please [compile from
|
||||
source](#compile-from-source)._
|
||||
|
||||
_Note: Installation options are limited at this moment. More will be added
|
||||
later._
|
||||
|
||||
[latest-release]: https://github.com/timvisee/lazymc/releases/latest
|
||||
|
||||
## Compile from source
|
||||
|
||||
Make sure you meet all [requirements](#requirements).
|
||||
|
||||
To compile from source you need Rust, install it through `rustup`: https://rustup.rs/
|
||||
|
||||
When Rust is installed, compile and install `lazymc` from this git repository
|
||||
directly:
|
||||
|
||||
```bash
|
||||
# Compile and install lazymc from source
|
||||
cargo install -f --git https://github.com/timvisee/lazymc
|
||||
|
||||
# Ensure lazymc works
|
||||
lazymc --help
|
||||
```
|
||||
|
||||
Or clone the repository and build it yourself:
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://github.com/timvisee/lazymc
|
||||
cd lazymc
|
||||
|
||||
# Compile
|
||||
cargo build --release
|
||||
|
||||
# Run lazymc
|
||||
./target/release/lazymc --help
|
||||
```
|
||||
|
||||
## Third-party usage & implementations
|
||||
|
||||
A list of third-party implementations, projects using `lazymc`, that you might
|
||||
find useful:
|
||||
|
||||
- Docker: [crbanman/papermc-lazymc](https://hub.docker.com/r/crbanman/papermc-lazymc) _(PaperMC with lazymc in Docker)_
|
||||
|
||||
## License
|
||||
|
||||
This project is released under the GNU GPL-3.0 license.
|
||||
|
22
TODO.md
Normal file
22
TODO.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# TODO
|
||||
|
||||
- Better organize code
|
||||
- Resolve TODOs in code
|
||||
- Don't drop errors, handle everywhere where needed (some were dropped while
|
||||
prototyping to speed up development)
|
||||
|
||||
## Nice to have
|
||||
|
||||
- Use server whitelist/blacklist
|
||||
- Console error if server already started on port, not through `lazymc`
|
||||
- Kick with message if proxy-to-server connection fails for new client.
|
||||
- Test configuration on start (server dir exists, command not empty)
|
||||
- Also quit `lazymc` after CTRL+C signal, after server has stopped
|
||||
- Dynamically increase/decrease server polling interval based on server state
|
||||
- Server polling through query (`enable-query` in `server.properties`, uses GameSpy4 protocol)
|
||||
|
||||
## Experiment
|
||||
|
||||
- Lobby method: let players connect with an emulated empty server (like 2b2t's
|
||||
queue), redirect them when the server started.
|
||||
- `io_uring` on Linux for efficient proxying (see `tokio-uring`)
|
48
docs/usage-windows.md
Normal file
48
docs/usage-windows.md
Normal file
@@ -0,0 +1,48 @@
|
||||
## Usage on Windows
|
||||
|
||||
Make sure you meet all [requirements](../README.md#requirements).
|
||||
|
||||
Download the `lazymc-*-windows.exe` Windows executable for your system from the
|
||||
[latest release][latest-release] page.
|
||||
|
||||
Place the binary in your Minecraft server directory, and rename it to
|
||||
`lazymc.exe`.
|
||||
|
||||
Open a terminal, go to the server directory, and make sure you can execute it:
|
||||
|
||||
```bash
|
||||
.\lazymc --help
|
||||
```
|
||||
|
||||
When `lazymc` is ready, set up the [configuration](./res/lazymc.toml) and start
|
||||
it up:
|
||||
|
||||
```bash
|
||||
# In your Minecraft server directory:
|
||||
|
||||
# Generate lazymc configuration
|
||||
.\lazymc config generate
|
||||
|
||||
# Edit configuration
|
||||
# Set the correct server address, directory and start command
|
||||
notepad lazymc.toml
|
||||
|
||||
# Start lazymc
|
||||
.\lazymc start
|
||||
```
|
||||
|
||||
Before you use this in production, please ensure starting and stopping the
|
||||
server works as expected by connecting to it once. Watch `lazymc`s output while
|
||||
it starts and stops. If stopping results in errors, fix this first to prevent
|
||||
corrupting world/user data.
|
||||
|
||||
Follow this repository with the _Watch_ button on the top right to be notified of new releases.
|
||||
|
||||
Everything should now be ready to go! Connect with your Minecraft client to wake
|
||||
your server up!
|
||||
|
||||
_Note: if you put `lazymc` in `PATH`, or if you
|
||||
[install](../README.md#compile-from-source) it through Cargo, you can invoke
|
||||
`lazymc` everywhere directly without the `.\` prefix.
|
||||
|
||||
[latest-release]: https://github.com/timvisee/lazymc/releases/latest
|
@@ -132,9 +132,15 @@ fn rewrite_server_properties(config: &Config) {
|
||||
let mut changes = HashMap::from([
|
||||
("server-ip", config.server.address.ip().to_string()),
|
||||
("server-port", config.server.address.port().to_string()),
|
||||
("enable-status", "true".into()),
|
||||
("query.port", config.server.address.port().to_string()),
|
||||
]);
|
||||
|
||||
// If connecting to server over non-loopback address, disable proxy blocking
|
||||
if !config.server.address.ip().is_loopback() {
|
||||
changes.extend([("prevent-proxy-connections", "false".into())]);
|
||||
}
|
||||
|
||||
// Add RCON configuration
|
||||
#[cfg(feature = "rcon")]
|
||||
if config.rcon.enabled {
|
||||
|
@@ -55,9 +55,7 @@ pub async fn monitor_server(config: Arc<Config>, server: Arc<Server>) {
|
||||
// Sleep server when it's bedtime
|
||||
if server.should_sleep(&config) {
|
||||
info!(target: "lazymc::montior", "Server has been idle, sleeping...");
|
||||
if !server.stop(&config).await {
|
||||
warn!(target: "lazymc", "Failed to stop server");
|
||||
}
|
||||
server.stop(&config).await;
|
||||
}
|
||||
|
||||
// Check whether we should force kill server
|
||||
|
@@ -20,7 +20,7 @@ pub unsafe fn kill_gracefully(pid: u32) -> bool {
|
||||
let result = libc::kill(pid as i32, libc::SIGTERM);
|
||||
|
||||
if result != 0 {
|
||||
trace!(target: "lazymc", "SIGTERM failed: {}", result);
|
||||
warn!(target: "lazymc", "Sending SIGTERM signal to server failed: {}", result);
|
||||
}
|
||||
|
||||
result == 0
|
||||
|
@@ -5,10 +5,15 @@ use std::time::{Duration, Instant};
|
||||
use futures::FutureExt;
|
||||
use minecraft_protocol::data::server_status::ServerStatus;
|
||||
use tokio::process::Command;
|
||||
use tokio::time;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::os;
|
||||
|
||||
/// Server cooldown after the process quit.
|
||||
/// Used to give it some more time to quit forgotten threads, such as for RCON.
|
||||
const SERVER_QUIT_COOLDOWN: Duration = Duration::from_millis(2500);
|
||||
|
||||
/// Server state.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum State {
|
||||
@@ -205,6 +210,7 @@ impl Server {
|
||||
// We must have a running process
|
||||
let has_process = self.pid.lock().unwrap().is_some();
|
||||
if !has_process {
|
||||
debug!(target: "lazymc", "Tried to stop server, while no PID is known");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -220,6 +226,7 @@ impl Server {
|
||||
return true;
|
||||
}
|
||||
|
||||
warn!(target: "lazymc", "Failed to stop server, no more suitable stopping method to use");
|
||||
false
|
||||
}
|
||||
|
||||
@@ -364,8 +371,13 @@ pub async fn invoke_server_cmd(
|
||||
}
|
||||
};
|
||||
|
||||
// Set state to stopped, update server PID
|
||||
// Forget server PID
|
||||
state.pid.lock().unwrap().take();
|
||||
|
||||
// Give server a little more time to quit forgotten threads
|
||||
time::sleep(SERVER_QUIT_COOLDOWN).await;
|
||||
|
||||
// Set server state to stopped
|
||||
state.update_state(State::Stopped, &config);
|
||||
|
||||
// Restart on crash
|
||||
@@ -384,6 +396,7 @@ async fn stop_server_rcon(config: &Config, server: &Server) -> bool {
|
||||
|
||||
// RCON must be enabled
|
||||
if !config.rcon.enabled {
|
||||
trace!(target: "lazymc", "Not using RCON to stop server, disabled in config");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -404,6 +417,7 @@ async fn stop_server_rcon(config: &Config, server: &Server) -> bool {
|
||||
// Invoke stop
|
||||
if let Err(err) = rcon.cmd("stop").await {
|
||||
error!(target: "lazymc", "Failed to invoke stop through RCON: {}", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set server to stopping state
|
||||
@@ -421,13 +435,21 @@ fn stop_server_signal(config: &Config, server: &Server) -> bool {
|
||||
// Grab PID
|
||||
let pid = match *server.pid.lock().unwrap() {
|
||||
Some(pid) => pid,
|
||||
None => return false,
|
||||
None => {
|
||||
debug!(target: "lazymc", "Could not send stop signal to server process, PID unknown");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Set stopping state, send kill signal
|
||||
// TODO: revert state on failure
|
||||
server.update_state(State::Stopping, config);
|
||||
crate::os::kill_gracefully(pid);
|
||||
// Send kill signal
|
||||
if !crate::os::kill_gracefully(pid) {
|
||||
error!(target: "lazymc", "Failed to send stop signal to server process");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update from starting/started to stopping
|
||||
server.update_state_from(Some(State::Starting), State::Stopping, config);
|
||||
server.update_state_from(Some(State::Started), State::Stopping, config);
|
||||
|
||||
true
|
||||
}
|
||||
|
@@ -177,7 +177,7 @@ pub async fn hold<'a>(
|
||||
|
||||
// If hold timeout is reached, kick client
|
||||
if since.elapsed().as_secs() >= timeout {
|
||||
warn!(target: "lazymc", "Holding client reached timeout of {}s, disconnecting", timeout);
|
||||
warn!(target: "lazymc", "Held client reached timeout of {}s, disconnecting", timeout);
|
||||
kick(&config.messages.login_starting, &mut inbound.split().1).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
Reference in New Issue
Block a user