1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#![allow(
    clippy::needless_pass_by_ref_mut //until we find a better way for specs
)]

use clap::{builder::ValueParser, Parser};
use common::comp;
use server::persistence::SqlLogMode;
use std::{str::FromStr, sync::mpsc::Sender};
use tracing::error;

// Custom value parser for case-insensitive parsing of AdminRole
fn admin_role_value_parser() -> ValueParser {
    ValueParser::new(move |s: &str| -> Result<comp::AdminRole, String> {
        comp::AdminRole::from_str(&s.to_lowercase()).map_err(|err| err.to_string())
    })
}

#[derive(Clone, Debug, Parser)]
pub enum Admin {
    /// Adds an admin
    Add {
        /// Name of the admin to whom to assign a role
        username: String,
        /// Role to assign to the admin
        #[arg(value_parser = admin_role_value_parser())]
        role: comp::AdminRole,
    },
    Remove {
        /// Name of the admin from whom to remove any existing roles
        username: String,
    },
}

#[derive(Clone, Debug, Parser)]
pub enum Shutdown {
    /// Closes the server immediately
    Immediate,
    /// Shuts down the server gracefully
    Graceful {
        /// Number of seconds to wait before shutting down
        seconds: u64,
        #[arg(short, long, default_value = "The server is shutting down")]
        /// Shutdown reason
        reason: String,
    },
    /// Cancel any pending graceful shutdown.
    Cancel,
}

#[derive(Clone, Debug, Parser)]
pub enum SharedCommand {
    /// Perform operations on the admin list
    Admin {
        #[command(subcommand)]
        command: Admin,
    },
}

#[derive(Debug, Clone, Parser)]
pub enum Message {
    #[command(flatten)]
    Shared(SharedCommand),
    /// Shut down the server (or cancel a shut down)
    Shutdown {
        #[command(subcommand)]
        command: Shutdown,
    },
    /// Loads up the chunks at map center and adds a entity that mimics a
    /// player to keep them from despawning
    #[cfg(feature = "worldgen")]
    LoadArea {
        /// View distance of the loaded area
        view_distance: u32,
    },
    /// Enable or disable sql logging
    SqlLogMode {
        #[arg(default_value_t, value_parser = clap::value_parser!(SqlLogMode))]
        mode: SqlLogMode,
    },
    /// Disconnects all connected clients
    DisconnectAllClients,
    /// returns active player names
    ListPlayers,
    ListLogs,
    /// sends a msg to everyone on the server
    SendGlobalMsg {
        msg: String,
    },
}

#[derive(Debug, Clone)]
pub enum MessageReturn {
    Players(Vec<String>),
    Logs(Vec<String>),
}

#[derive(Parser)]
#[command(
    name = "Veloren server TUI",
    version = common::util::DISPLAY_VERSION_LONG.as_str(),
    about = "The veloren server tui allows sending commands directly to the running server.",
    author = "The veloren devs <https://gitlab.com/veloren/veloren>",
)]
#[clap(no_binary_name = true)]
pub struct TuiApp {
    #[command(subcommand)]
    command: Message,
}

#[derive(Debug, Clone, Copy, Parser)]
pub struct BenchParams {
    /// View distance of the loaded area (in chunks)
    #[arg(long)]
    pub view_distance: u32,
    /// Duration to run after loading completes (in seconds).
    #[arg(long)]
    pub duration: u32,
}

#[derive(Parser)]
pub enum ArgvCommand {
    #[command(flatten)]
    Shared(SharedCommand),
    /// Load an area, run the server for some time, and then exit (useful for
    /// profiling).
    Bench(BenchParams),
}

#[derive(Parser)]
#[command(
    name = "Veloren server CLI",
    version = common::util::DISPLAY_VERSION_LONG.as_str(),
    about = "The veloren server cli provides an easy to use interface to start a veloren server.",
    author = "The veloren devs <https://gitlab.com/veloren/veloren>",
)]
pub struct ArgvApp {
    #[arg(long, short)]
    /// Enables the tui
    pub tui: bool,
    #[arg(long, short)]
    /// Doesn't listen on STDIN
    ///
    /// Useful if you want to send the server in background, and your kernels
    /// terminal driver will send SIGTTIN to it otherwise. (https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Redirections) and you dont want to use `stty -tostop`
    /// or `nohub` or `tmux` or `screen` or `<<< \"\\004\"` to the program.
    pub non_interactive: bool,
    #[arg(long)]
    /// Run without auth enabled
    pub no_auth: bool,
    #[arg(default_value_t, long, short, value_parser = clap::value_parser!(SqlLogMode))]
    /// Enables SQL logging
    pub sql_log_mode: SqlLogMode,
    #[command(subcommand)]
    pub command: Option<ArgvCommand>,
}

pub fn parse_command(input: &str, msg_s: &mut Sender<Message>) {
    match TuiApp::try_parse_from(shell_words::split(input).unwrap_or_default()) {
        Ok(message) => {
            msg_s
                .send(message.command)
                .unwrap_or_else(|e| error!(?e, "Failed to send CLI message"));
        },
        Err(e) => error!("{}", e),
    }
}