From 887a228cf28fc27d6791831c97a2652301694bd2 Mon Sep 17 00:00:00 2001 From: tomoron Date: Thu, 11 Jun 2026 15:22:29 +0200 Subject: [PATCH] nix disable check phase, fix incomplete/failed write in proxy mode, add FML support, rename executable to dmm, image tag specific to servers, config folder from env, --- Cargo.lock | 2 +- Cargo.toml | 8 +++--- flake.nix | 7 +++-- src/cli/env_settings.rs | 5 ++-- src/config.rs | 6 +++-- src/dockermgr/start.rs | 14 +++++++--- src/dockermgr/utils.rs | 36 ++++++++++++++++++++++--- src/minecraft/client/buffer.rs | 48 +++++++++++++++++++++++++--------- src/minecraft/client/client.rs | 6 +++-- src/minecraft/client/packet.rs | 15 ++++++++--- src/models.rs | 3 ++- 11 files changed, 114 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e385c2a..233b93b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -607,7 +607,7 @@ dependencies = [ [[package]] name = "dockermcmgr" -version = "0.0.2" +version = "0.0.3" dependencies = [ "async-std", "bollard", diff --git a/Cargo.toml b/Cargo.toml index 5c7a630..28c107e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "dockermcmgr" -version = "0.0.2" +version = "0.0.3" edition = "2024" -#[[bin]] -#name = "dmm" -#path = "src/main.rs" +[[bin]] +name = "dmm" +path = "src/main.rs" [dependencies] async-std = "1.13.2" diff --git a/flake.nix b/flake.nix index 6feb0be..b65a7c3 100644 --- a/flake.nix +++ b/flake.nix @@ -37,16 +37,19 @@ in final: { - pname = "dockermcmgr"; - version = "0.0.2"; + pname = "dmm"; + version = "0.0.3"; inherit src; cargoLock.lockFile = src + "/Cargo.lock"; nativeBuildInputs = tools; + doCheck = false; + buildInputs = with pkgs; [ sqlite ]; } + // specialArgs ); in diff --git a/src/cli/env_settings.rs b/src/cli/env_settings.rs index 0c0b349..157ef87 100644 --- a/src/cli/env_settings.rs +++ b/src/cli/env_settings.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use clap::{ Arg, ArgMatches, parser::ValueSource}; -pub fn get_args() -> [Arg; 21] { +pub fn get_args() -> [Arg; 22] { [ Arg::new("VERSION").short('v').long("version").help("Minecraft version of the server").default_value("LATEST"), Arg::new("MEMORY").short('m').long("memory").help("How much ram does this server have").default_value("4G"), @@ -26,12 +26,13 @@ pub fn get_args() -> [Arg; 21] { Arg::new("SPAWN_ANIMALS").long("spawn-animals").help("spawn animals").default_value("true"), Arg::new("SPAWN_MONSTERS").long("spawn-monsters").help("spawn monsters").default_value("true"), Arg::new("SPAWN_NPCS").long("spawn-npcs").help("spawn npcs").default_value("true"), + Arg::new("IMAGE_TAG").long("image-tag").help("set the tag of the docker image").default_value("latest") ] } pub fn args_to_settings(arguments: &ArgMatches, default: bool) -> Vec<(String, String)> { let default_handle = vec!["VERSION", "MEMORY", "MAX_PLAYERS", "SPAWN_PROTECTION", "ONLINE_MODE", "TYPE", "MOTD", "DIFFICULTY", "OPS", "MODE", "ALLOW_FLIGHT", - "ALLOW_NETHER", "HARDCORE", "MAX_TICK_TIME", "PAUSE_WHEN_EMPTY_SECONDS", "PVP", "SPAWN_ANIMALS", "SPAWN_MONSTERS", "SPAWN_NPCS"]; + "ALLOW_NETHER", "HARDCORE", "MAX_TICK_TIME", "PAUSE_WHEN_EMPTY_SECONDS", "PVP", "SPAWN_ANIMALS", "SPAWN_MONSTERS", "SPAWN_NPCS", "IMAGE_TAG"]; let mut res = vec![ ("EULA".to_string(), "TRUE".to_string()), diff --git a/src/config.rs b/src/config.rs index e2ca39d..da2ceeb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,5 @@ +use std::env; + pub struct Config { pub config_path: String, pub docker_image: String, @@ -6,8 +8,8 @@ pub struct Config { impl Config { pub fn load() -> Self { Self { - config_path: "/home/tom/desktop/rust/yo_mama/servers".to_string(), // that's my program I do what I want - docker_image: "itzg/minecraft-server".to_string() + config_path: env::var("DMM_CONFIG_PATH").unwrap_or_else(|_| "/raid/mc_servers".to_string()), + docker_image: "itzg/minecraft-server".to_string(), } } } diff --git a/src/dockermgr/start.rs b/src/dockermgr/start.rs index 0de0ff0..4b2e25c 100644 --- a/src/dockermgr/start.rs +++ b/src/dockermgr/start.rs @@ -1,12 +1,11 @@ use bollard::{Docker, plugin::{ContainerCreateBody, HostConfig, Mount, MountType}, query_parameters::CreateContainerOptionsBuilder}; use diesel::{prelude::*, result::Error::NotFound}; -use crate::{schema, models, status::ServerStatus}; +use crate::{dockermgr::utils::get_image_tag, models, schema, status::ServerStatus}; use crate::{DbPool, dockermgr::utils::{self, get_container_ip}}; async fn start_docker_container(docker: &Docker, name: &String) -> Result { - utils::check_image(&docker).await?; let container_name = format!("minecraft-{}", name); @@ -20,6 +19,13 @@ async fn start_docker_container(docker: &Docker, name: &String) -> Result {tag}, + None => { return Err("env missing IMAGE_TAG".to_string()); } + }; + + utils::check_image(&docker, &image_tag).await?; + let mut mount = Mount::default(); mount.typ = Some(MountType::BIND); mount.target = Some("/data".to_string()); @@ -30,13 +36,15 @@ async fn start_docker_container(docker: &Docker, name: &String) -> Result Result<(), String> { +pub async fn check_image(docker: &Docker, tag: &String) -> Result<(), String> { let mut filters = HashMap::new(); - let container_name = crate::Config::load().docker_image; + let config = crate::Config::load(); + let container_name = config.docker_image + ":" + tag; filters.insert("reference", vec![&container_name]); let options = ListImagesOptionsBuilder::default() .all(true) @@ -23,9 +24,10 @@ pub async fn check_image(docker: &Docker) -> Result<(), String> { return Ok(()); } + let options = CreateImageOptionsBuilder::default() .from_image(&container_name) - .tag("latest") + .tag(tag) .build(); @@ -69,3 +71,31 @@ pub async fn get_container_ip(docker: &Docker, name: &String) -> Result) -> Option { + for var in env { + match var.split_once("=") { + Some(split) => { if split.0 == "IMAGE_TAG" { return Some(split.1.to_string()); }}, + None => { }, + } + } + None +} + + + + + + + + + + + + + + + + + + diff --git a/src/minecraft/client/buffer.rs b/src/minecraft/client/buffer.rs index 50d91db..0b96e4d 100644 --- a/src/minecraft/client/buffer.rs +++ b/src/minecraft/client/buffer.rs @@ -1,18 +1,24 @@ +use tokio::net::TcpStream; + use crate::minecraft::client::client::Client; use crate::minecraft::varint::varint_read; +use std::collections::VecDeque; use std::io; +use tokio::io::AsyncWriteExt; + + impl Client { pub async fn in_read(&mut self) -> Result { loop { match self.in_stream.try_read(&mut self.read_buf) { Ok(n) => { - if let Some(out_stream) = &self.out_stream { - let _ = out_stream.writable().await; - let _ = out_stream.try_write(&self.read_buf[..n]); + if let Some(out_stream) = &mut self.out_stream { + + out_stream.write_all(&self.read_buf[..n]).await.map_err(|e| format!("Failed to write to server stream :{}", e))?; } else { self.buffer_append((&self.read_buf[..n]).to_vec()).await?; } @@ -23,8 +29,8 @@ impl Client { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { return Ok(1); } - Err(_e) => { - return Err("read failed".to_string()) + Err(e) => { + return Err(format!("in read failed: {}", e)) } } } @@ -46,8 +52,7 @@ impl Client { match self.out_stream.as_mut().unwrap().try_read(&mut self.read_buf) { Ok(n) => { - let _ = self.in_stream.writable().await; - let _ = self.in_stream.try_write(&self.read_buf[..n]); + self.in_stream.write_all(&self.read_buf[..n]).await.map_err(|e| format!("Failed to write to client stream :{}", e))?; if n == 0 { Ok(0) } else { Ok(1) } } Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { @@ -60,10 +65,13 @@ impl Client { } } + pub async fn buffer_append(&mut self, data: Vec) -> Result<(),String> { if data.len() == 0 { return Ok(()); } + + println!("[{}] adding {} bytes to the buffer",self, data.len()); if self.buffer.len() + data.len() > 65536 { @@ -71,21 +79,35 @@ impl Client { } let _ = self.buffer.extend(data); + + let mut varint_buffer: VecDeque = self.buffer[..5].to_vec().into(); + println!("varint_buffer : {:?}", varint_buffer); - while self.buffer.len() != 0 && self.buffer.len() - 1 >= self.buffer[0] as usize { + let expected_len = varint_read(&mut varint_buffer)?; + let varint_len = 5 - varint_buffer.len(); + + println!("expected len : {}, current len : {}", expected_len as usize, self.buffer.len()); + println!("buffer : {:?}", self.buffer); + + while self.buffer.len() != 0 && self.buffer.len() - 1 >= expected_len as usize { if self.buffer[0] == 0 { return Err("invalid packet".to_string()); } + println!("valid packet found, handling"); + println!("varint len : {}", varint_len); + println!("len : {}", expected_len); - let len = varint_read(&mut self.buffer.clone().into())? as usize; - self.handle_packet(self.buffer[1..=len].to_vec().into()).await?; - self.buffer.drain(..len + 1); + self.handle_packet(self.buffer[varint_len..=(expected_len as usize)].to_vec().into()).await?; + self.buffer.drain(..(expected_len as usize + varint_len)); + + println!("buffer after handle : {:?}", self.buffer); if let Some(out_stream) = &self.out_stream { + println!("out stream present , sending the {} remaining bytes", self.buffer.len()); if self.buffer.len() != 0 { - let _ = out_stream.writable().await; - let _ = out_stream.try_write(self.buffer.as_slice()); + let _ = out_stream.writable().await.map_err(|e| format!("out stream writable call failed : {}", e))?; + let _ = out_stream.try_write(self.buffer.as_slice()).map_err(|e| format!("Failed to send remaining buffer to out stream : {}", e))?; self.buffer.drain(..); } } diff --git a/src/minecraft/client/client.rs b/src/minecraft/client/client.rs index daed318..f91297d 100644 --- a/src/minecraft/client/client.rs +++ b/src/minecraft/client/client.rs @@ -6,7 +6,7 @@ /* By: tomoron +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/07 17:23:09 by tomoron #+# #+# */ -/* Updated: 2026/06/10 12:29:08 by tomoron ### ########.fr */ +/* Updated: 2026/06/11 12:21:51 by tomoron ### ########.fr */ /* */ /* ************************************************************************** */ @@ -72,10 +72,12 @@ impl Client { let conn = &mut self.db_pool.get().unwrap(); - let reg = Regex::new(r"^(?:([a-zA-Z-_]*)\.)?mc\.tmoron\.fr$").unwrap(); + let reg = Regex::new(r"^(?:([a-zA-Z-_]*)\.)?mc\.tmoron\.fr").unwrap(); let mut found: Option = None; if let Some(capture) = reg.captures(&self.handshake.as_ref().unwrap().server_address) { + println!("capture : {:?}", capture); if let Some(captured_str) = capture.get(1) { + println!("found valid capture : {}", captured_str.as_str()); found = Some(captured_str.as_str().to_string()); } } diff --git a/src/minecraft/client/packet.rs b/src/minecraft/client/packet.rs index df1ec50..0d87b0d 100644 --- a/src/minecraft/client/packet.rs +++ b/src/minecraft/client/packet.rs @@ -9,6 +9,7 @@ use tokio::net::TcpStream; impl Client { pub async fn handle_packet(&mut self, mut packet: VecDeque) -> Result<(), String> { + println!("packet len : {}", packet.len()); let packet_id = varint_read(&mut packet)?; @@ -56,7 +57,8 @@ impl Client { async fn proxy_start(&mut self) -> Result<(),String> { println!("[{}] starting proxy mode", self); - let redirect_ip = &self.server.as_ref().unwrap().as_ref().unwrap().redirect_ip.as_ref().unwrap(); + let server = &self.server.as_ref().unwrap().as_ref().unwrap(); + let redirect_ip = server.redirect_ip.as_ref().unwrap(); let split_ip = redirect_ip.split_once(":"); let domain : String = split_ip.unwrap_or_else(|| (redirect_ip, "")).0.to_string(); let port : u16 = split_ip.unwrap_or_else(|| ("", "25565")).1.parse().map_err(|e| format!("Failed to parse redirect_ip : {}", e))?; @@ -64,7 +66,12 @@ impl Client { .map_err(|_| "failed to connect to remote host".to_string())?; let mut new_handshake = self.handshake.as_ref().unwrap().clone(); - new_handshake.server_address = domain; + if server.simple_redirect { + new_handshake.server_address = domain; + } else { + new_handshake.server_address = self.handshake.as_ref().unwrap().server_address.clone(); + } + new_handshake.server_port = port; let handshake_packet = new_handshake.to_packet(); @@ -76,8 +83,10 @@ impl Client { let _ = self.out_stream.as_ref().unwrap().writable().await; let _ = self.out_stream.as_ref().unwrap().try_write(packet.as_slice()); - println!("[{}] sent handshake to server", self); + + println!("proxy mode started, buffer len : {}", self.buffer.len()); + Ok(()) } } diff --git a/src/models.rs b/src/models.rs index b139693..63ea1c6 100644 --- a/src/models.rs +++ b/src/models.rs @@ -14,7 +14,8 @@ pub struct Servers { pub name: String, pub last_login: Option, pub status: ServerStatus, - pub redirect_ip: Option + pub redirect_ip: Option, + pub simple_redirect: bool, }