Changeset - 06c014f251e3
[Not reviewed]
0 1 0
Branko Majic (branko) - 4 years ago 2020-07-04 21:46:06
branko@majic.rs
Noticket: Improve factorio_manager.sh server settings prompt handling:

- Use dedicated functions for colored output (colorecho and
colorprintf).
- Update function documentation to be a clearer.
- Declare all local variables correctly.
- Fix invalid setting type for
ignore_player_limit_for_returning_players.
- Validate user's input (should validate all settings even if user has
not changed them, but not implemented yet).
1 file changed with 144 insertions and 44 deletions:
0 comments (0 inline, 0 general) First comment
games/factorio_manager.sh
Show inline comments
 
@@ -245,12 +245,63 @@ function warning() {
 
}
 

	
 
function error() {
 
    echo "${_text_bold}${_text_red}[ERROR]${_text_reset}" "$@" >&2
 
}
 

	
 
function colorecho() {
 
    local options="" color text
 
    local reset="$_text_reset"
 

	
 
    if [[ ${1-} =~ -.* ]]; then
 
        options="$1"
 
        shift
 
    fi
 

	
 
    color="$1"
 
    text="$2"
 

	
 
    declare -A colors
 

	
 
    colors[black]="${_text_black}"
 
    colors[red]="${_text_red}"
 
    colors[green]="${_text_green}"
 
    colors[yellow]="${_text_yellow}"
 
    colors[blue]="${_text_blue}"
 
    colors[purple]="${_text_purple}"
 
    colors[cyan]="${_text_cyan}"
 
    colors[white]="${_text_white}"
 

	
 
    if [[ -n $options ]]; then
 
        echo "$options" "${colors[$color]}$text${reset}"
 
    else
 
        echo "${colors[$color]}$text${reset}"
 
    fi
 
}
 

	
 
function colorprintf() {
 
    local reset="$_text_reset"
 

	
 
    local color="$1"
 
    local format="$2"
 
    shift 2
 

	
 
    declare -A colors
 

	
 
    colors[black]="${_text_black}"
 
    colors[red]="${_text_red}"
 
    colors[green]="${_text_green}"
 
    colors[yellow]="${_text_yellow}"
 
    colors[blue]="${_text_blue}"
 
    colors[purple]="${_text_purple}"
 
    colors[cyan]="${_text_cyan}"
 
    colors[white]="${_text_white}"
 

	
 
    printf "${colors[$color]}${format}${reset}" "$@"
 
}
 

	
 
function warning_prompt() {
 
    echo -n "${_text_bold}${_text_yellow}[WARN] ${_text_reset}" "$@"
 
}
 

	
 
function bold_green() {
 
    echo -n "${_text_bold}${_text_green}$@${_text_reset}"
 
@@ -262,54 +313,68 @@ function bold_red() {
 

	
 
function bold_blue() {
 
    echo -n "${_text_bold}${_text_blue}$@${_text_reset}"
 
}
 

	
 
#
 
# Reads servers settings from user.
 
# Prompts user to provide settings for a server instance.
 
#
 
# Function will go over a number of questions, providing a set of
 
# default values, and allowing the user to edit them in-place. Once
 
# all questions have been answered, user is presented with summary and
 
# ability to revisit the settings again.
 
#
 
# Function will go over a number of question, providing the user with
 
# default values, and allowing editing in-place. Once all questions
 
# have been answered, user will be shown the settings, and then
 
# allowed to update them again until satisfied.
 
# The function will transform the answers to be fully usable in the
 
# server configuration file, making sure the parameters are properly
 
# escaped for use in a JSON file.
 
#
 
# Once the user is done, the function will transform the answers so
 
# they are fully usable in the server configuration file, adding
 
# quoting for strings etc.
 
# Defaults are mostly identical to the ones listed under Factorio's
 
# default "server-setting.json" with some exceptions to options that
 
# may be considered invasion of privacy:
 
#
 
#   - User verification is disabled. Otherwise the central
 
#     authentication server will always be aware of who is playing the
 
#     game at the moment.
 
#   - Game is specifically configured to be non-public.
 
#
 
# Arguments:
 
#
 
#   $1 (server_name)
 
#     Default name to use for the server. Normally should be an
 
#     instance name.
 
#     Default name to use for the server.
 
#
 
# Sets:
 
#
 
#   settings_value
 
#     Associative array storing settings and their values. Keys are
 
#     equivalent to setting names in the server configuration file.
 
#
 
function read_server_settings() {
 

	
 
    # Read arguments.
 
    local server_name="$1"
 

	
 
    # Local variables.
 
    local key value prompt confirmed item
 
    # Local helper variables.
 
    local key="" value="" prompt="" confirmed="" item="" validation_passed possible_values i
 

	
 
    declare -A settings_prompt
 
    declare -A settings_description
 
    declare -A settings_type
 
    declare -A option
 

	
 
    declare -a settings_order
 
    declare -a possible_options
 

	
 
    # Global variables.
 
    declare -g -A settings_value option
 
    # Global variables set by the function.
 
    declare -g -A settings_value
 

	
 
    # Set-up listings of server settings. Each setting is described
 
    # with name, prompt, description, default value, and
 
    # type. Maintain additional array with keys in order to maintain
 
    # type. Supported types are: bool, int, str, list (input treated
 
    # as space-delimited list).
 
    #
 
    # Maintain additional array with keys in order to maintain
 
    # order when displaying questions to users.
 
    key="name"
 
    settings_prompt["$key"]="Name"
 
    settings_description["$key"]="Name of the game as it will appear in the game listing"
 
    settings_value["$key"]="$server_name"
 
    settings_type["$key"]="str"
 
@@ -407,13 +472,13 @@ function read_server_settings() {
 
    settings_order+=("$key")
 

	
 
    key="ignore_player_limit_for_returning_players"
 
    settings_prompt["$key"]="Ignore player limit for returning players"
 
    settings_description["$key"]="Players that played on this map already can join even when the max player limit was reached."
 
    settings_value["$key"]="false"
 
    settings_type["$key"]="int"
 
    settings_type["$key"]="bool"
 
    settings_order+=("$key")
 

	
 
    key="allow_commands"
 
    settings_prompt["$key"]="Allow commands"
 
    settings_description["$key"]="Allow execution of commands via Lua console."
 
    settings_value["$key"]="admins-only"
 
@@ -502,85 +567,120 @@ function read_server_settings() {
 
        confirmed=""
 

	
 
        # Display the settings.
 
        echo "Current settings:"
 
        echo
 
        for key in "${settings_order[@]}"; do
 
            echo -e "${_text_yellow}${settings_prompt[$key]}: ${_text_reset}${settings_value[$key]}"
 
            colorecho -n "yellow" "${settings_prompt[$key]}: "
 
            echo "${settings_value[$key]}"
 
        done
 

	
 
        # Prompt user to confirm settings.
 
        while [[ ${confirmed,,} != 'y' && ${confirmed,,} != 'n' ]]; do
 
            echo
 
            read -n1 -p "${_text_green}Are you satisfied with current settings (y/n)? ${_text_reset}" confirmed
 
            colorecho -n "green" "Are you satisfied with current settings (y/n)? "
 
            read -n1 confirmed
 
            echo
 
        done
 

	
 
        # Allow user to provide changed values if not satisfied with
 
        # settings.
 
        if [[ ${confirmed,,} == 'n' ]]; then
 
            echo
 

	
 
            local total_questions="${#settings_order[@]}"
 
            local current_question=1
 

	
 
            for key in "${settings_order[@]}"; do
 

	
 
                # Prompt based on associated value type for the setting.
 
                if [[ ${settings_type[$key]} == "bool" ]]; then
 
                    prompt="${settings_prompt[$key]} [true|false]: "
 
                    prompt="${settings_prompt[$key]} [true|false]:"
 
                elif [[ ${settings_type[$key]} == "int" ]]; then
 
                    prompt="${settings_prompt[$key]} [number]: "
 
                    prompt="${settings_prompt[$key]} [number]:"
 
                elif [[ ${settings_type[$key]} == "str" ]]; then
 
                    prompt="${settings_prompt[$key]} [text]: "
 
                    prompt="${settings_prompt[$key]} [text]:"
 
                elif [[ ${settings_type[$key]} == "list" ]]; then
 
                    prompt="${settings_prompt[$key]} [space-delimited list]: "
 
                    prompt="${settings_prompt[$key]} [space-delimited list]:"
 
                else
 
                    prompt="${settings_prompt[$key]} [${settings_type[$key]}]: "
 
                    prompt="${settings_prompt[$key]} [${settings_type[$key]}]:"
 
                fi
 

	
 
                echo "${_text_white}${settings_description[$key]}${_text_reset}" | fold -s
 
                printf "${_text_green}%02d/%02d ${prompt}${_text_reset}" "$current_question" "$total_questions"
 
                read -e -i "${settings_value[$key]}" value
 
                # Add some coloring to the prompt.
 
                prompt=$(colorprintf "green" "%02d/%02d ${prompt} " "$current_question" "$total_questions")
 

	
 
                # Show setting description.
 
                colorecho "white" "${settings_description[$key]}" | fold -s
 

	
 
                # Keep prompting user until a valid value is provided.
 
                validation_passed=false
 
                until [[ $validation_passed == true ]]; do
 

	
 
                    read -p "$prompt" -e -i "${settings_value[$key]}" value
 

	
 
                    if [[ ${settings_type[$key]} == "bool" ]]; then
 
                        [[ $value == true || $value == false ]] && validation_passed=true || colorecho "red" "Value must be boolean [true|false]."
 

	
 
                    elif [[ ${settings_type[$key]} == "int" ]]; then
 
                        [[ $value =~ ^[[:digit:]]+$ ]] && validation_passed=true || colorecho "red" "Value must be a number."
 

	
 
                    elif [[ ${settings_type[$key]} == "str" ]]; then
 
                        validation_passed=true
 

	
 
                    elif [[ ${settings_type[$key]} == "list" ]]; then
 
                        # This is free-form space-delimited list.
 
                        validation_passed=true
 

	
 
                    elif [[ ${settings_type[$key]} =~ ^.+\|.+$ ]]; then
 
                        readarray -d "|" -t possible_values < <(echo -n "${settings_type[$key]}")
 

	
 
                        for i in "${possible_values[@]}"; do
 
                            [[ $value == $i ]] && validation_passed=true
 

	
 
                            # Convenience for allowing strings without quotes specified by the user.
 
                            [[ \"$value\" == $i ]] && value="\"$value\"" && validation_passed=true
 
                        done
 

	
 
                        [[ $validation_passed == true ]] || colorecho red "Value must be one of selection [${settings_type[$key]}]"
 
                    else
 
                        error "Unsupported type: ${settings_type[$key]}"
 
                        return 1
 
                    fi
 
                done
 

	
 
                settings_value[$key]="$value"
 

	
 
                echo
 
                let current_question++
 
            done
 
        fi
 
    done
 

	
 
    # Prepare values so they can be used within the JSON file.
 
    # @TODO: Perform proper validation of settings during prompts.
 
    #
 
    # @TODO: Perform same validation as for user input (since script
 
    # itself could have a typo).
 
    for key in "${settings_order[@]}"; do
 
        debug "Raw value for $key: ${settings_value[$key]}"
 

	
 
        if [[ ${settings_type[$key]} == "str" ]]; then
 
            settings_value[$key]="\"${settings_value[$key]}\""
 

	
 
        # @TODO: This is a hack for true|false|"admins-only"
 
        # really. If moved into proper validation process, could be
 
        # dealt away with probably.
 
        elif [[ ${settings_type[$key]} =~ .*\|.* ]]; then
 
            readarray -t -d '|' <<<"${settings_type[$key]}" possible_options
 
        # Used for selecting between multiple values.
 
        elif [[ ${settings_type[$key]} =~ ^.+\|.+$ ]]; then
 
            readarray -d "|" -t possible_values < <(echo -n "${settings_type[$key]}")
 

	
 
            # If we find an exact match, that is the value we want to
 
            # use. Otherwise add quotes around the settings value.
 
            value=""
 
            for option in "${possible_options[@]}"; do
 
                if [[ ${settings_value[$key]} == $option ]]; then
 
                    value="${settings_value[$key]}"
 
                fi
 
            for i in "${possible_values[@]}"; do
 
                # Convenience for allowing strings without quotes specified by the user.
 
                [[ \"${settings_value[$key]}\" == $i ]] && settings_value[$key]="\"${settings_value[$key]}\""
 
            done
 

	
 
            if [[ -z $value ]]; then
 
                value="\"${settings_value[$key]}\""
 
            fi
 
            settings_value[$key]="$value"
 

	
 
        # List of strings.
 
        elif [[ ${settings_type[$key]} == "list" ]]; then
 
            value=""
 

	
 
            for item in ${settings_value[$key]}; do
 
                value="$value, \"$item\""
 
            done
 

	
 
            settings_value[$key]="[${value##, }]"
 
        fi
0 comments (0 inline, 0 general) First comment
You need to be logged in to comment. Login now