add command works ,fix warnings, proxy repaired, docker start arguments defined
This commit is contained in:
@ -1,27 +1,69 @@
|
||||
use clap::{ArgMatches, Command, arg};
|
||||
use crate::cli::env_settings;
|
||||
use crate::utils::validate_name;
|
||||
use crate::status::ServerStatus;
|
||||
use crate::utils::{ validate_name, extract_archive, move_all_elements};
|
||||
|
||||
use diesel::{insert_into, prelude::*};
|
||||
use crate::{DbPool, models};
|
||||
use crate::schema;
|
||||
|
||||
use tokio::{fs::{File, create_dir}, io::AsyncWriteExt};
|
||||
|
||||
use std::path::Path;
|
||||
use std::time::SystemTime;
|
||||
|
||||
pub fn create_subcommand() -> Command {
|
||||
let mut arguments = vec![
|
||||
arg!(<NAME> "Name of the server"),
|
||||
arg!([SERVER_ZIP] "initial server zip"),
|
||||
arg!([SERVER_ARCHIVE] "initial server archive (supported : prism zip)"),
|
||||
];
|
||||
|
||||
arguments.extend(env_settings::get_args());
|
||||
|
||||
Command::new("add")
|
||||
.about("add a server")
|
||||
.about("Add a server")
|
||||
.args(arguments.as_slice())
|
||||
}
|
||||
|
||||
|
||||
pub fn subcommand(arg: &ArgMatches) -> Result<(), String>{
|
||||
if !validate_name(arg.get_one::<String>("NAME").unwrap()) {
|
||||
pub async fn subcommand(arg: &ArgMatches, pool: DbPool) -> Result<(), String>{
|
||||
let config = crate::Config::load();
|
||||
let name = arg.get_one::<String>("NAME").unwrap();
|
||||
|
||||
|
||||
if !validate_name(name) {
|
||||
return Err("Invalid name provided".to_string());
|
||||
}
|
||||
|
||||
|
||||
println!("{}", env_settings::args_to_env(arg));
|
||||
let conn = &mut pool.get().unwrap();
|
||||
|
||||
if let Ok(_) = schema::servers::table.filter(schema::servers::name.eq(name)).select(models::Servers::as_select()).first(conn) {
|
||||
return Err("Server with this name already exists".to_string())
|
||||
}
|
||||
|
||||
let path = config.config_path + "/" + name + "/";
|
||||
|
||||
create_dir(&path).await.map_err(|e| format!("failed to create the config folder, reason : {}", e))?;
|
||||
|
||||
let mut file = File::create(path.to_string() + "env").await.map_err(|e| format!("failed to create .env file, reason {}", e))?;
|
||||
file.write_all(env_settings::args_to_env(arg).as_bytes()).await.map_err(|e| format!("Failed to write to env file, reason : {}", e))?;
|
||||
|
||||
create_dir(path.to_string() + "server").await.map_err(|e| format!("failed to create server subfolder, reason: {}", e))?;
|
||||
|
||||
if let Some(archive) = arg.get_one::<String>("SERVER_ARCHIVE") {
|
||||
extract_archive(archive.to_owned(), path.to_string() + "tmp").await?;
|
||||
move_all_elements(Path::new(&(path.to_string() + "tmp/minecraft")), Path::new(&(path.to_string() + "server"))).map_err(|e| format!("failed to move the elements, reason: {}", e))?;
|
||||
std::fs::remove_dir_all(path.to_string() + "tmp").map_err(|e| format!("failed to remove the tmp dir, reason : {}", e))?;
|
||||
}
|
||||
|
||||
let new_server = models::CreateServer {
|
||||
name: name,
|
||||
last_login: Some(SystemTime::now()),
|
||||
container_id: None,
|
||||
status: ServerStatus::Stopped,
|
||||
redirect_ip: None
|
||||
};
|
||||
|
||||
insert_into(schema::servers::dsl::servers).values(&new_server).execute(conn).map_err(|e| format!("Failed to insert in db, error : {:?}", e))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use clap::{Command, arg};
|
||||
use clap::Command;
|
||||
|
||||
pub fn create_subcommand() -> Command {
|
||||
Command::new("edit")
|
||||
|
||||
@ -8,13 +8,13 @@ pub fn get_args() -> [Arg; 22] {
|
||||
Arg::new("SPAWN_PROTECTION").short('s').long("spawn-protection").help("spawn protection size").default_value("0"),
|
||||
Arg::new("ONLINE_MODE").short('o').long("online-mode").help("false to allow cracked accounts to join").default_value("true"),
|
||||
Arg::new("TYPE").short('t').long("type").help("Server type (VANILLA, FORGE, NEOFORGE, PAPER)").default_value("VANILLA"),
|
||||
Arg::new("FORGE_VERSION").long("forge-version").help("forge version if set type is FORGE or NEOFORGE").default_value("latest"), //TODO diff handle
|
||||
Arg::new("FORGE_VERSION").long("forge-version").help("forge version if set type is FORGE or NEOFORGE").default_value("latest"),
|
||||
Arg::new("MOTD").short('M').long("motd").help("MOTD of the server (only shown when the server is running)"),
|
||||
Arg::new("DIFFICULTY").short('d').long("difficulty").help("Difficulty of the server").default_value("easy"),
|
||||
Arg::new("WHITELIST").short('w').long("whitelist").help("comma separated list of the playsrs to whitelist (disabled if not provied)"), //TODO diff handle
|
||||
Arg::new("WHITELIST").short('w').long("whitelist").help("comma separated list of the playsrs to whitelist (disabled if not provied)"),
|
||||
Arg::new("OPS").short('O').long("ops").help("comma separated list of the players to give operator permissions to"),
|
||||
Arg::new("MODE").long("gamemode").default_value("survival"),
|
||||
Arg::new("CUSTOM_SERVER_PROPERTIES").long("custom-property").help("add a custom property").action(ArgAction::Append), //TODO diff handle
|
||||
Arg::new("CUSTOM_SERVER_PROPERTIES").long("custom-property").help("add a custom property").action(ArgAction::Append),
|
||||
Arg::new("ALLOW_FLIGHT").short('f').long("alllow-flight").help("Allow flight in suvival mode").default_value("false"),
|
||||
Arg::new("ALLOW_NETHER").short('n').long("allow-nether").help("Enable the nether dimension").default_value("true"),
|
||||
Arg::new("HARDCORE").short('H').long("hardcore").help("Enable hardcore mode").default_value("false"),
|
||||
@ -30,7 +30,10 @@ pub fn get_args() -> [Arg; 22] {
|
||||
pub fn args_to_env(arguments: &ArgMatches) -> 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"];
|
||||
let mut res = vec![];
|
||||
|
||||
let mut res = vec![
|
||||
("EULA".to_string(), "TRUE".to_string()),
|
||||
];
|
||||
|
||||
for setting in default_handle {
|
||||
if let Some(val) = arguments.get_one::<String>(setting) {
|
||||
@ -55,6 +58,8 @@ pub fn args_to_env(arguments: &ArgMatches) -> String {
|
||||
|
||||
// println!("\n\n\narguments : {:?}\nignored:{:?}", arguments, ignored);
|
||||
res.iter()
|
||||
.map(|x| format!("{}=\"{}\"", x.0, x.1))
|
||||
.map(|x| format!("{}={}", x.0, x.1))
|
||||
.collect::<Vec<String>>().join("\n")
|
||||
}
|
||||
// START COMMAND :
|
||||
// docker run --rm --env-file ./env -u 1000:100 -it --volume $PWD/server:/data itzg/minecraft-server
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use clap::{Command, arg};
|
||||
use clap::Command;
|
||||
|
||||
pub fn create_subcommand() -> Command {
|
||||
Command::new("ls")
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use clap::{Command, arg};
|
||||
use clap::Command;
|
||||
|
||||
pub fn create_subcommand() -> Command {
|
||||
Command::new("remove")
|
||||
|
||||
@ -1,3 +1,88 @@
|
||||
use std::fs;
|
||||
use std::io;
|
||||
|
||||
use zip::{ZipArchive, result::ZipError};
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
pub fn validate_name(name: &str) -> bool{
|
||||
name.chars().all(|c| c.is_alphanumeric() || c == '_' || c == '-')
|
||||
}
|
||||
|
||||
pub async fn extract_archive(archive_path: String, output: String) -> Result<(), String> {
|
||||
println!("starting extraction of {}", archive_path);
|
||||
fs::create_dir(&output).map_err(|e| format!("Failed to create archive output folder, reason : {}", e))?;
|
||||
|
||||
let mut archive = match fs::File::open(archive_path)
|
||||
.map_err(ZipError::from)
|
||||
.and_then(ZipArchive::new)
|
||||
{
|
||||
Ok(archive) => archive,
|
||||
Err(e) => {
|
||||
return Err(format!("failed to open archive, reason : {:?}", e));
|
||||
}
|
||||
};
|
||||
|
||||
for i in 0..archive.len() {
|
||||
let mut file = match archive.by_index(i) {
|
||||
Ok(file) => file,
|
||||
Err(e) => {
|
||||
return Err(format!("extraction failed, reason : {}", e));
|
||||
}
|
||||
};
|
||||
let mut out_path = match file.enclosed_name() {
|
||||
Some(path) => path,
|
||||
None => {
|
||||
return Err("Invalid file path".to_string());
|
||||
}
|
||||
};
|
||||
|
||||
out_path = Path::new(&output).join(out_path);
|
||||
|
||||
|
||||
if file.is_dir() {
|
||||
if let Err(e) = fs::create_dir_all(&out_path) {
|
||||
return Err(format!("extraction failed, reason : {}", e));
|
||||
}
|
||||
} else {
|
||||
if let Some(p) = out_path.parent()
|
||||
&& !p.exists()
|
||||
&& let Err(e) = fs::create_dir_all(p)
|
||||
{
|
||||
return Err(format!("extraction failed, reason : {}", e));
|
||||
}
|
||||
|
||||
let _ = fs::File::create(&out_path)
|
||||
.and_then(|mut outfile| io::copy(&mut file, &mut outfile))
|
||||
.map_err(|e| format!("extraction failed, reason: {}", e))?;
|
||||
}
|
||||
}
|
||||
println!("extraction done");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn move_all_elements(src: &Path, dst: &Path) -> io::Result<()> {
|
||||
// 1. Ensure the destination directory exists; if not, create it
|
||||
if !dst.exists() {
|
||||
fs::create_dir_all(dst)?;
|
||||
println!("Created destination directory: {:?}", dst);
|
||||
}
|
||||
|
||||
// 2. Read the contents of the source directory
|
||||
for entry in fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let file_name = entry.file_name();
|
||||
|
||||
// 3. Construct the new path (destination + filename)
|
||||
let old_path = entry.path();
|
||||
let new_path = dst.join(file_name);
|
||||
|
||||
// 4. Perform the move
|
||||
// fs::rename works for both files and directories
|
||||
fs::rename(&old_path, &new_path)?;
|
||||
println!("Moved: {:?} -> {:?}", old_path, new_path);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
11
src/config.rs
Normal file
11
src/config.rs
Normal file
@ -0,0 +1,11 @@
|
||||
pub struct Config {
|
||||
pub config_path: String
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/lib.rs
10
src/lib.rs
@ -1,16 +1,6 @@
|
||||
use bollard::Docker;
|
||||
|
||||
|
||||
use std::env;
|
||||
|
||||
use std::sync::LazyLock;
|
||||
|
||||
|
||||
use diesel::PgConnection;
|
||||
use diesel::r2d2::{
|
||||
Pool,
|
||||
ConnectionManager
|
||||
};
|
||||
|
||||
pub static mut DOCKER: LazyLock<Docker> = LazyLock::new(|| Docker::connect_with_local_defaults().expect("Failed to connect to the docker socket") );
|
||||
|
||||
|
||||
91
src/main.rs
91
src/main.rs
@ -6,25 +6,10 @@
|
||||
/* By: tomoron <tomoron@student.42angouleme.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/05/29 21:22:17 by tomoron #+# #+# */
|
||||
/* Updated: 2026/06/05 00:11:54 by tomoron ### ########.fr */
|
||||
/* Updated: 2026/06/05 17:48:58 by tomoron ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
use tokio::io;
|
||||
//use bollard::{
|
||||
// query_parameters::{
|
||||
// ListImagesOptionsBuilder,
|
||||
// ListContainersOptionsBuilder,
|
||||
// CreateContainerOptionsBuilder
|
||||
// },
|
||||
// models::{
|
||||
// ContainerCreateBody
|
||||
// },
|
||||
// Docker
|
||||
//};
|
||||
|
||||
|
||||
|
||||
mod minecraft;
|
||||
use minecraft::mc_socket_listen;
|
||||
|
||||
@ -38,9 +23,10 @@ pub mod models;
|
||||
pub mod status;
|
||||
|
||||
mod cli;
|
||||
|
||||
use cli::*;
|
||||
|
||||
mod config;
|
||||
pub use config::Config;
|
||||
|
||||
pub type DbPool = Pool<ConnectionManager<PgConnection>>;
|
||||
|
||||
@ -54,8 +40,7 @@ fn get_connection_pool() -> DbPool {
|
||||
}
|
||||
|
||||
|
||||
use clap::{Command, arg};
|
||||
|
||||
use clap::Command;
|
||||
|
||||
fn cli() -> Command {
|
||||
Command::new("dmm")
|
||||
@ -64,7 +49,7 @@ fn cli() -> Command {
|
||||
.arg_required_else_help(true)
|
||||
.subcommand(
|
||||
Command::new("serverMode")
|
||||
.about("start the server manager")
|
||||
.about("Start the server manager")
|
||||
|
||||
)
|
||||
.subcommand(add::create_subcommand())
|
||||
@ -80,72 +65,8 @@ async fn main() -> Result<(), String> {
|
||||
|
||||
match arg.subcommand() {
|
||||
Some(("serverMode", _)) => { mc_socket_listen(pool).await.map_err(|_| "socket error".to_string())?; },
|
||||
Some(("add", sub_matches)) => { add::subcommand(sub_matches)?; },
|
||||
Some(("add", sub_matches)) => { add::subcommand(sub_matches, pool).await?; },
|
||||
_ => {panic!("subcommand not implemented")}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
println!("{:?}", docker);
|
||||
let options = ListImagesOptionsBuilder::default()
|
||||
.all(true)
|
||||
.build();
|
||||
let images = &docker.list_images(Some(options)).await.unwrap();
|
||||
println!("\n\nimages : {:?}", images);
|
||||
|
||||
let options = ListContainersOptionsBuilder::default()
|
||||
.all(true)
|
||||
.build();
|
||||
|
||||
let containers = docker.list_containers(Some(options)).await.unwrap();
|
||||
println!("\n\ncontainers : {:?}", containers);
|
||||
|
||||
let options = CreateContainerOptionsBuilder::default()
|
||||
.name("rust-created-container")
|
||||
.build();
|
||||
|
||||
let config = ContainerCreateBody {
|
||||
image: Some("hello-world".to_string()),
|
||||
// cmd: Some(vc pec!["/hello".to_string()]),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let container = docker.create_container(Some(options), config).await.unwrap();
|
||||
|
||||
let start_res = docker.start_container("rust-created-container", None).await;
|
||||
*/
|
||||
/*
|
||||
*/
|
||||
//}
|
||||
|
||||
@ -6,13 +6,12 @@
|
||||
/* By: tomoron <tomoron@student.42angouleme.fr> +#+ +:+ +#+ */
|
||||
/* +#+#+#+#+#+ +#+ */
|
||||
/* Created: 2026/05/07 17:23:09 by tomoron #+# #+# */
|
||||
/* Updated: 2026/05/31 23:23:05 by tomoron ### ########.fr */
|
||||
/* Updated: 2026/06/05 12:36:46 by tomoron ### ########.fr */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
|
||||
use tokio::net::TcpStream;
|
||||
use crate::minecraft::handshake::Handshake;
|
||||
use crate::minecraft::varint::varint_write;
|
||||
use std::fmt;
|
||||
|
||||
use crate::models::Servers;
|
||||
@ -29,7 +28,7 @@ pub struct Client {
|
||||
|
||||
pub server: Option<Result<Servers,diesel::result::Error>>,
|
||||
|
||||
pub dbPool: DbPool
|
||||
pub db_pool: DbPool
|
||||
}
|
||||
|
||||
use crate::DbPool;
|
||||
@ -54,7 +53,7 @@ impl Client {
|
||||
|
||||
server: None,
|
||||
|
||||
dbPool: pool
|
||||
db_pool: pool
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +65,7 @@ impl Client {
|
||||
}
|
||||
|
||||
|
||||
let conn = &mut self.dbPool.get().unwrap();
|
||||
let conn = &mut self.db_pool.get().unwrap();
|
||||
|
||||
let reg = Regex::new(r"^(?:([a-zA-Z-_]*)\.)?mc\.tmoron\.fr$").unwrap();
|
||||
let mut found: Option<String> = None;
|
||||
|
||||
@ -3,7 +3,6 @@ use crate::minecraft::client::client::Client;
|
||||
use json::object;
|
||||
use crate::minecraft::varint::varint_write;
|
||||
use std::collections::VecDeque;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
impl Client {
|
||||
pub async fn login_intent_handle(&mut self, mut _packet: &mut VecDeque<u8>, _packet_id: i32) -> Result<(),String> {
|
||||
|
||||
@ -2,11 +2,9 @@ use crate::minecraft::client::client::Client;
|
||||
|
||||
use crate::minecraft::varint::{varint_read, varint_write};
|
||||
use crate::minecraft::handshake::Handshake;
|
||||
use crate::models;
|
||||
use crate::status::ServerStatus;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use diesel::result::Error::NotFound;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
impl Client {
|
||||
@ -20,9 +18,11 @@ impl Client {
|
||||
}
|
||||
self.handshake = Some(Handshake::from_packet(&mut packet)?);
|
||||
self.server = Some(self.get_server().await);
|
||||
// if self.server.as_ref().unwrap().status == ServerStatus::Running {
|
||||
// return self.proxy_start().await;
|
||||
// }
|
||||
if let Some(Ok(server)) = &self.server {
|
||||
if server.status == ServerStatus::Running {
|
||||
return self.proxy_start().await;
|
||||
}
|
||||
}
|
||||
|
||||
if self.handshake.as_ref().unwrap().intent == 2 {
|
||||
self.login_intent_handle(&mut packet, packet_id).await?;
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
use crate::minecraft::client::client::Client;
|
||||
|
||||
use diesel::prelude::*;
|
||||
use diesel::result::Error::NotFound;
|
||||
use crate::schema;
|
||||
use crate::models;
|
||||
|
||||
use json::object;
|
||||
|
||||
|
||||
@ -14,7 +14,6 @@ use crate::schema;
|
||||
pub struct Servers {
|
||||
pub id: i64,
|
||||
pub name: String,
|
||||
pub volume_path: String,
|
||||
pub last_login: Option<SystemTime>,
|
||||
pub container_id: Option<String>,
|
||||
pub status: ServerStatus,
|
||||
@ -26,7 +25,6 @@ pub struct Servers {
|
||||
#[diesel(table_name = schema::servers)]
|
||||
pub struct CreateServer<'a> {
|
||||
pub name: &'a str,
|
||||
pub volume_path: &'a str,
|
||||
pub last_login: Option<SystemTime>,
|
||||
pub container_id: Option<&'a str>,
|
||||
pub status: ServerStatus,
|
||||
|
||||
@ -5,8 +5,6 @@ diesel::table! {
|
||||
id -> Int8,
|
||||
#[max_length = 255]
|
||||
name -> Varchar,
|
||||
#[max_length = 255]
|
||||
volume_path -> Varchar,
|
||||
last_login -> Nullable<Timestamp>,
|
||||
#[max_length = 30]
|
||||
container_id -> Nullable<Varchar>,
|
||||
|
||||
@ -4,7 +4,9 @@ use diesel::expression::AsExpression;
|
||||
use diesel::pg::Pg;
|
||||
use diesel::serialize::{self, ToSql, Output};
|
||||
use diesel::sql_types::SmallInt;
|
||||
use std::fmt::{self, write};
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, AsExpression, FromSqlRow, serde::Serialize, serde::Deserialize)]
|
||||
|
||||
Reference in New Issue
Block a user