Changeset - b996dd49a6c7
[Not reviewed]
0 1 0
Branko Majic (branko) - 4 years ago 2020-07-05 16:52:29
branko@majic.rs
Noticket: Updated help for Factorio Manager, and bumped the script version to 0.2.
1 file changed with 90 insertions and 40 deletions:
0 comments (0 inline, 0 general) First comment
games/factorio_manager.sh
Show inline comments
 
#!/bin/bash
 
#
 
# factorio_manager.sh
 
#
 
# Copyright (C) 2020, Branko Majic <branko@majic.rs>
 
#
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
#
 

	
 
# Treat unset variables as errors.
 
set -u
 

	
 
program="factorio_manager.sh"
 
version="0.1"
 
version="0.2"
 

	
 
function synopsis() {
 
cat <<EOF
 
$program $version, helper tool for managing Factorio instances
 

	
 
Usage:
 
  $program [OPTIONS] launch INSTANCE
 
  $program [OPTIONS] info INSTANCE
 
  $program [OPTIONS] list
 

	
 
  $program [OPTIONS] create INSTANCE
 
  $program [OPTIONS] create-server INSTANCE
 
  $program [OPTIONS] copy SOURCE_INSTANCE DESTINATION_INSTANCE
 
  $program [OPTIONS] remove INSTANCE
 
  $program [OPTIONS] import INSTANCE SOURCE_DIRECTORY
 

	
 
  $program [OPTIONS] versions
 
  $program [OPTIONS] set-version INSTANCE
 

	
 
  $program [OPTIONS] list-backups INSTANCE
 
  $program [OPTIONS] backup INSTANCE [DESCRIPTION]
 
  $program [OPTIONS] restore INSTANCE BACKUP_NAME
 
  $program [OPTIONS] remove-backup INSTANCE BACKUP_NAME
 

	
 
  $program [OPTIONS] set-game-dir GAME_INSTALLATIONS_DIRECTORY
 
EOF
 
}
 

	
 
function short_usage() {
 
cat <<EOF
 
$(synopsis)
 

	
 
For more details see $program -h.
 
EOF
 
}
 

	
 
function usage() {
 
    cat <<EOF
 
$(synopsis)
 

	
 
$program is a helper tool for managing multiple Factorio
 
instances.
 
$program is a helper tool for managing multiple Factorio instances.
 

	
 
Each instance is designated a dedicated directory, which contains all
 
of its configuration files, saves games, and mods, and is kept
 
separate from all other instances.
 

	
 
A single Factorio version installation (base files) can be used by
 
multiple instances, but each instance is bound to a specific version
 
of Factorio.
 
Each instance is bound to a specific game version, and the manager
 
fully supports working with multiple versions of Factorio. Base game
 
files are kept intact and can be shared by multiple instances.
 

	
 
Factorio Manager keeps instances in sub-directories under the
 
~/.factorio/ directory. Sub-directories are named after the
 
instances. The following instance names are reserved for special use
 
by the tool:
 
~/.factorio/ directory. Sub-directories correspond to instance
 
names.
 

	
 
The following instance names are reserved for special use by
 
the tool:
 

	
 
 - .game_installations (used for storing symlink towards directory
 
   containing Factorio installations)
 

	
 
Multiple commands are provided for managing Factorio instances, and
 
they all expect different positional parameters:
 

	
 

	
 
set-game-dir GAME_INSTALLATIONS_DIRECTORY
 
Multiple commands are provided for managing Factorio instances. Each
 
command accepts its own set of positional arguments.
 

	
 
    Sets the passed-in directory path as the base directory where
 
    different versions of Factorio will be looked-up. Each
 
    sub-directory within this directory should be named after the
 
    version of Factorio it represents, and should be the base
 
    directory under which the Factorio installation files can be
 
    found.
 

	
 

	
 
versions
 

	
 
    Shows locally available Factorio versions.
 
backup INSTANCE [DESCRIPTION]
 

	
 
    Creates backup of an instance. All backups will be stored as
 
    subdirectories under the .bak directory within the instance
 
    directory. An optional description can be passed-in to make it
 
    easier to distinguish between different backups. Hidden files
 
    (names starting with '.') will be omitted from the backup.
 

	
 
list
 

	
 
    Lists available Factorio instances, and shows some basic
 
    information about them.
 
copy SOURCE_INSTANCE DESTINATION_INSTANCE
 

	
 
    Creates a copy of an existing instance. Requires name of an
 
    existing instance and name of the new instance to be passed-in as
 
    arguments. Command will refuse to overwrite destination instance
 
    if it already exists. Command will prompt user to specify desired
 
    version for the copy, and to choose whether the backups should be
 
    copied as well.
 

	
 
create INSTANCE
 

	
 
    Creates a new Factorio instance with the given name. Command will
 
    prompt the user to pick between locally available Factorio
 
    versions.
 

	
 

	
 
launch INSTANCE
 

	
 
    Launches an instance with the given name.
 

	
 
    NOTE:: When launching the instance for the first time, Factorio
 
    NOTE: When launching the instance for the first time, Factorio
 
    will report that its configuration file is invalid, and offer to
 
    fix it. The reason is that the manager creates a minimal
 
    configuration file when creating an instance, and Factorio does
 
    not like this. It should be safe to allow Factorio to fix the
 
    configuration file (this will populate it with the necessary
 
    commented-out options).
 

	
 
create-server INSTANCE
 

	
 
backup INSTANCE [DESCRIPTION]
 
    Creates a new Factorio server instance with the given
 
    name. Command will prompt the user to pick between locally
 
    available Factorio versions, and to provide settings for server.
 

	
 
    Creates backup of an instance. All backups will be stored as
 
    subdirectories under the .bak directory within the instance
 
    directory. An optional description can be passed-in to make it
 
    easier to distinguish between different backups. Hidden files
 
    (names starting with '.') will be omitted from the backup.
 
import INSTANCE
 

	
 
    Creates a new instance out of existing files found within a
 
    Factorio installation directory. This command is useful when
 
    migrating from Factorio installations that store all data in the
 
    root of the game installation directory. Command will prompt the
 
    user to pick between locally available Factorio versions.
 

	
 
info INSTANCE
 

	
 
    Shows information about the specified instance. This includes:
 

	
 
        - instance name
 
        - game version
 
        - instance directory path
 
        - list of enabled/disabled mods
 
        - list of backups
 

	
 
launch INSTANCE
 

	
 
    Launches the instance with specified name. See the note for the
 
    "create" command.
 

	
 
list
 

	
 
    Lists available Factorio instances.
 

	
 
list-backups INSTANCE
 

	
 
    Lists available backups of an instance, including description (if any is set).
 
    Lists available backups for the specified instance.
 

	
 
remove-backup INSTANCE BACKUP_NAME
 

	
 
    Removes the specified backup for the specified instance. User must
 
    confirm the action prior to any files being removed.
 

	
 
remove INSTANCE
 

	
 
    Removes the specified instance. User must confirm the action prior
 
    to any files being removed.
 

	
 
restore INSTANCE BACKUP_NAME
 

	
 
    Restores the specified instance from the specified backup. User
 
    must confirm the action prior to any files being replaced.
 

	
 
set-game-dir GAME_INSTALLATIONS_DIRECTORY
 

	
 
    Sets the base directory where Factorio game installations can be
 
    found. Each sub-directory within this directory should be named
 
    after the version of Factorio it represents. For example:
 

	
 
        - ~/local/games/factorio/0.17.79/
 
        - ~/local/games/factorio/0.18.30/
 
        - ~/local/games/factorio/0.18.34/
 

	
 
set-version INSTANCE
 

	
 
    Sets Factorio version for the specified instance. User is prompted
 
    to pick between locally available versions.
 

	
 
versions
 

	
 
    Shows locally available Factorio versions.
 

	
 

	
 
$program accepts the following options:
 

	
 
    -q
 
        Quiet mode. Output a message only if newer packages are available.
 
    -d
 
        Enable debug mode.
 
    -v
 
        Show script licensing information.
 
    -h
 
        Show usage help.
 

	
 

	
 
Please report bugs and send feature requests to <branko@majic.rs>.
 
EOF
 
}
 

	
 
function version() {
 
    cat <<EOF
 
$program, version $version
 

	
 
+-----------------------------------------------------------------------+
 
| Copyright (C) 2020, Branko Majic <branko@majic.rs>                    |
 
|                                                                       |
 
| This program is free software: you can redistribute it and/or modify  |
 
| it under the terms of the GNU General Public License as published by  |
 
| the Free Software Foundation, either version 3 of the License, or     |
 
| (at your option) any later version.                                   |
 
|                                                                       |
 
| This program is distributed in the hope that it will be useful,       |
 
| but WITHOUT ANY WARRANTY; without even the implied warranty of        |
 
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         |
 
| GNU General Public License for more details.                          |
 
|                                                                       |
 
| You should have received a copy of the GNU General Public License     |
 
| along with this program.  If not, see <http://www.gnu.org/licenses/>. |
 
+-----------------------------------------------------------------------+
 

	
 
EOF
 
}
 

	
 
# Set-up colours for message printing if we're not piping and terminal is
 
# capable of outputting the colors.
 
_color_terminal=$(tput colors 2>&1)
 
if [[ -t 1 ]] && (( ${_color_terminal} > 0 )); then
 
    _text_black=$(tput setaf 0)
 
    _text_red=$(tput setaf 1)
 
    _text_green=$(tput setaf 2)
 
    _text_yellow=$(tput setaf 3)
 
    _text_blue=$(tput setaf 4)
 
    _text_purple=$(tput setaf 5)
 
    _text_cyan=$(tput setaf 6)
 
    _text_white=$(tput setaf 7)
 

	
 
    _text_bold=$(tput bold)
 
    _text_reset=$(tput sgr0)
 

	
 
    _bg_black=$(tput setab 0)
 
    _bg_red=$(tput setab 1)
 
    _bg_green=$(tput setab 2)
 
    _bg_yellow=$(tput setab 3)
 
    _bg_blue=$(tput setab 4)
 
    _bg_purple=$(tput setab 5)
 
    _bg_cyan=$(tput setab 6)
 
    _bg_white=$(tput setab 7)
 
else
 
    _text_black=""
 
    _text_red=""
 
    _text_green=""
 
    _text_yellow=""
 
    _text_blue=""
 
    _text_purple=""
 
    _text_cyan=""
 
    _text_white=""
 

	
 
    _text_bold=""
 
    _text_reset=""
 

	
 
    _bg_black=""
 
    _bg_red=""
 
    _bg_green=""
 
    _bg_yellow=""
 
    _bg_blue=""
 
    _bg_purple=""
 
    _bg_cyan=""
 
    _bg_white=""
 
fi
 

	
 
# Make the colors available via an associative array as well.
 
declare -A _text_colors=()
 

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

	
 
_text_colors[boldblack]="${_text_bold}${_text_black}"
 
_text_colors[boldblue]="${_text_bold}${_text_blue}"
 
_text_colors[boldcyan]="${_text_bold}${_text_cyan}"
 
_text_colors[boldgreen]="${_text_bold}${_text_green}"
 
_text_colors[boldpurple]="${_text_bold}${_text_purple}"
 
_text_colors[boldred]="${_text_bold}${_text_red}"
 
_text_colors[boldwhite]="${_text_bold}${_text_white}"
 
_text_colors[boldyellow]="${_text_bold}${_text_yellow}"
 

	
 
# Set-up functions for printing coloured messages.
 
function debug() {
 
    if [[ $debug != 0 ]]; then
 
        echo "${_text_bold}${_text_blue}[DEBUG]${_text_reset}" "$@"
 
    fi
 
}
 

	
 
function info() {
 
    echo "${_text_bold}${_text_white}[INFO] ${_text_reset}" "$@"
 
}
 

	
 
function success() {
 
    echo "${_text_bold}${_text_green}[OK]   ${_text_reset}" "$@"
 
}
 

	
 
function warning() {
 
    echo "${_text_bold}${_text_yellow}[WARN] ${_text_reset}" "$@"
 
}
 

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

	
 
#
 
# Prints text in requested color to standard output. Behaves as thin
 
# wrapper around the echo built-in.
 
#
 
# Two invocation variants with different arguments are
 
# supported. First argument is the one that will determine what the
 
# desired invocation is.
 
#
 
# Arguments (variant 1):
 
#
 
#   $1 (echo_options)
 
#     Additional options to pass-in to echo. Must be a single argument
 
#     with leading dash followed by echo's own options. E.g. "-ne".
 
#
 
#   $2 (color)
 
#     Text color to use. Supported values: black, blue, cyan, green,
 
#     purple, red, white, yellow.
 
#
 
#   $3 (text)
 
#     Text to output.
 
#
 
#
 
# Arguments (variant 2):
 
#
 
#   $1 (color)
 
#     Text color to use. Supported values: black, blue, cyan, green,
 
#     purple, red, white, yellow, boldblack, boldblue, boldcyan,
 
#     boldgreen, boldpurple, boldred, boldwhite, boldyellow.
 
#
 
#   $2 (text)
 
#     Text to output.
 
#
 
function colorecho() {
 
    local options="" color text
 
    local reset="$_text_reset"
 

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

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

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

	
 
#
 
# Prints text in requested color to standard output using printf
 
# format string and arguments.. Behaves as thin wrapper around the
 
# printf built-in.
 
#
 
# Arguments:
 
#
 
#   $1 (color)
 
#     Text color to use. Supported values: black, blue, cyan, green,
 
#     purple, red, white, yellow.
 
#
 
#   $2 (format_string)
 
#     printf-compatible format string.
 
#
 
#   $3 .. $n
 
#      Replacement values for the the format string.
 
#
 
function colorprintf() {
 
    local reset="$_text_reset"
 

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

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

	
 
#
 
# Presents user with a warning, asks user for confirmation to
 
# continue, and terminates the script is confirmation is not provided
 
# with designated exit code.
 
#
 
# This function can be used to request confirmation for dangerous
 
# actions/operations (such as removal of large number of files etc).
 
#
 
# The user must type-in YES (with capital casing) to proceed.
 
#
 
# Arguments:
 
#
 
#   $1 (prompt_text)
 
#     Text to prompt the user with.
 
#
 
#   $2 (abort_text)
 
#     Text to show to user in case the operation was aborted by user.
 
#
 
#   $3 (exit_code)
 
#     Exit code when terminating the proram.
 
#
 
function critical_confirmation() {
 
    local prompt_text="$1"
 
    local abort_text="$2"
 
    local exit_code="$3"
 

	
 
    echo -n "${_text_bold}${_text_yellow}[WARN] ${_text_reset}" "${prompt_text} Type YES to confirm (default is no): "
 
    read confirm
 

	
 
    if [[ $confirm != "YES" ]]; then
 
        error "$abort_text"
 
        exit "$ERROR_GENERAL"
 
    fi
 
}
 

	
 
#
 
# Validates that the specified value conforms to designated setting.
 
#
 
# This is a small helper function used within read_server_settings
 
# function to validate the settings.
 
#
 
# Arguments:
 
#
 
#   $1 (name)
 
#     Name of the setting. Used to show erros to the user.
 
#
 
#   $2 (value)
 
#     Value to validate.
 
#
 
#   $3 (type)
 
#
 
#     Value type. Currently supports the following types:
 
#
 
#       - bool (boolean)
 
#       - int (integer/number)
 
#       - str (string, essentially anything can pass this validation)
 
#       - list (space-separated list of strings, essentially anything
 
#         can pass this validation)
 
#       - VAL_1|...|VAL_N (choice between different values)
 
#
 
#     When using the VAL_1|...|VAL_N variant, validation is slightly
 
#     relaxed for string values to allow both quoted and unquoted
 
#     variants. For example, if type was set to true|false|"admins",
 
#     then both 'admin' and '"admins"' will validate successfully
 
#     against the type.
 
#
 
# Returns:
 
#
 
#   0 if validation has passed, 1 if validation failed, and 2 if
 
#   passed-in type is not supported.
 
#
 
function validate_server_setting_value() {
 
    local name="$1"
 
    local value="$2"
 
    local type="$3"
 

	
 
    declare -a possible_values
 

	
 
    local i
 

	
 
    # Assume failure.
 
    local result=1
 

	
 
    if [[ $type == "bool" ]]; then
 
        [[ $value == true || $value == false ]] && result=0 || colorecho "red" "$name must be a boolean [true|false]."
 

	
 
    elif [[ $type == "int" ]]; then
 
        [[ $value =~ ^[[:digit:]]+$ ]] && result=0 || colorecho "red" "$name must be a number."
 

	
 
    elif [[ $type == "str" ]]; then
 
        result=0
 

	
 
    elif [[ $type == "list" ]]; then
 
        # This is free-form space-delimited list.
 
        result=0
 

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

	
 
        for i in "${possible_values[@]}"; do
 
            # Allow strings without quotes to be specified by the user.
 
            [[ $value == $i || \"$value\" == $i ]] && result=0
 
        done
 

	
 
        [[ $result == 0 ]] || colorecho red "$name must be one of listed values [$type]."
 
    else
 
        error "Unsupported type associated with '$name': $type"
 
        result=2
 
    fi
 

	
 
    return "$result"
 
}
 

	
 
#
 
# 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.
 
#
 
# 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.
 
#
 
# Defaults are mostly identical to the ones listed under Factorio's
 
# default "server-settings.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.
 
#
 
# In addition to settings from the "server-settings.json"
 
# configuiration file, settings also cover the server port (specified
 
# in the "config.ini").
 
#
 
# Arguments:
 
#
 
#   $1 (server_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 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 settings_order=()
 

	
 
    # 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. 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"
 
    settings_order+=("$key")
 

	
 
    key="description"
 
    settings_prompt["$key"]="Description"
 
    settings_description["$key"]="Description of the game that will appear in the listing"
 
    settings_value["$key"]=""
 
    settings_type["$key"]="str"
 
    settings_order+=("$key")
 

	
 
    key="tags"
 
    settings_prompt["$key"]="Tags"
 
    settings_description["$key"]="Tags for the game that will appear in the listing (space-separated)"
 
    settings_value["$key"]=""
 
    settings_type["$key"]="list"
 
    settings_order+=("$key")
 

	
 
    # Not part of "server-settings.json", but important to show and
 
    # prompt the user for.
 
    key="port"
 
    settings_prompt["$key"]="Port"
 
    settings_description["$key"]="Port on which the server should listen"
 
    settings_value["$key"]="34197"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    key="max_players"
 
    settings_prompt["$key"]="Maximum players"
 
    settings_description["$key"]="Maximum number of players allowed, admins can join even a full server. 0 means unlimited."
 
    settings_value["$key"]="0"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    key="public"
 
    settings_prompt["$key"]="Publicly available"
 
    settings_description["$key"]="Game will be published on the official Factorio matching server."
 
    settings_value["$key"]="false"
 
    settings_type["$key"]="bool"
 
    settings_order+=("$key")
 

	
 
    key="lan"
 
    settings_prompt["$key"]="Broadcast on LAN"
 
    settings_description["$key"]="Game will be broadcast on LAN."
 
    settings_value["$key"]="false"
 
    settings_type["$key"]="bool"
 
    settings_order+=("$key")
 

	
 
    key="username"
 
    settings_prompt["$key"]="Factorio login username"
 
    settings_description["$key"]="Your factorio.com login credentials. Required for games with visibility public."
 
    settings_value["$key"]=""
 
    settings_type["$key"]="str"
 
    settings_order+=("$key")
 

	
 
    key="password"
 
    settings_prompt["$key"]="Factorio login password"
 
    settings_description["$key"]="Your factorio.com login credentials. Required for games with visibility public."
 
    settings_value["$key"]=""
 
    settings_type["$key"]="str"
 
    settings_order+=("$key")
 

	
 
    key="token"
 
    settings_prompt["$key"]="Factorio authentication token"
 
    settings_description["$key"]="Authentication token. May be used instead of 'password' for factorio.com login credentials."
 
    settings_value["$key"]=""
 
    settings_type["$key"]="str"
 
    settings_order+=("$key")
 

	
 
    key="game_password"
 
    settings_prompt["$key"]="Server password"
 
    settings_description["$key"]="Server password used to authenticate users towards the server itself. Default value has been randomly generated using /dev/urandom."
 
    settings_value["$key"]=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 33)
 
    settings_type["$key"]="str"
 
    settings_order+=("$key")
 

	
 
    key="require_user_verification"
 
    settings_prompt["$key"]="Require user verification"
 
    settings_description["$key"]="When set to true, the server will only allow clients that have a valid Factorio.com account."
 
    settings_value["$key"]="false"
 
    settings_type["$key"]="bool"
 
    settings_order+=("$key")
 

	
 
    key="max_upload_in_kilobytes_per_second"
 
    settings_prompt["$key"]="Maxiumum upload in kilobytes per second"
 
    settings_description["$key"]="Limits the maximum upload speed from server towards the clients (for map transfers etc). 0 means unlimited."
 
    settings_value["$key"]="0"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    key="max_upload_slots"
 
    settings_prompt["$key"]="Maximum upload slots"
 
    settings_description["$key"]="Limits the number of simulataneous uploads from server towards clients. 0 means unlimited."
 
    settings_value["$key"]="5"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    key="minimum_latency_in_ticks"
 
    settings_prompt["$key"]="Minimum latency in ticks"
 
    settings_description["$key"]="Minimum tolerable latency in ticks for connecting clients. One tick is 16ms in default speed. 0 means no minimum."
 
    settings_value["$key"]="0"
 
    settings_type["$key"]="int"
 
    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"]="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"
 
    settings_type["$key"]="true|false|\"admins-only\""
 
    settings_order+=("$key")
 

	
 
    key="autosave_interval"
 
    settings_prompt["$key"]="Autosave interval"
 
    settings_description["$key"]="Autosave interval in minutes."
 
    settings_value["$key"]="10"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    key="autosave_slots"
 
    settings_prompt["$key"]="Autosave slots"
 
    settings_description["$key"]="Number of autosave slots to use. Autosave slots are cycled through when the server autosaves."
 
    settings_value["$key"]="5"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    key="afk_autokick_interval"
 
    settings_prompt["$key"]="AFK auto-kick interval"
 
    settings_description["$key"]="How many minutes until someone is kicked when doing nothing, 0 for never."
 
    settings_value["$key"]="0"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    key="auto_pause"
 
    settings_prompt["$key"]="Auto-pause"
 
    settings_description["$key"]="Whether should the server be paused when no players are present."
 
    settings_value["$key"]="true"
 
    settings_type["$key"]="bool"
 
    settings_order+=("$key")
 

	
 
    key="only_admins_can_pause_the_game"
 
    settings_prompt["$key"]="Only admins can pause the game"
 
    settings_description["$key"]="Specify if only admins should be able to pause the game."
 
    settings_value["$key"]="true"
 
    settings_type["$key"]="bool"
 
    settings_order+=("$key")
 

	
 
    key="autosave_only_on_server"
 
    settings_prompt["$key"]="Autosave only on server"
 
    settings_description["$key"]="Whether autosaves should be saved only on server or also on all connected clients."
 
    settings_value["$key"]="true"
 
    settings_type["$key"]="bool"
 
    settings_order+=("$key")
 

	
 
    key="non_blocking_saving"
 
    settings_prompt["$key"]="[EXPERT] Non-blocking saving"
 
    settings_description["$key"]="Highly experimental feature, enable only at your own risk of losing your saves. On UNIX systems, server will fork itself to create an autosave. Autosaving on connected Windows clients will be disabled regardless of autosave_only_on_server option."
 
    settings_value["$key"]="false"
 
    settings_type["$key"]="bool"
 
    settings_order+=("$key")
 

	
 
    key="minimum_segment_size"
 
    settings_prompt["$key"]="[EXPERT] Minimum segment size"
 
    settings_description["$key"]="Long network messages are split into segments that are sent over multiple ticks. Their size depends on the number of peers currently connected. Increasing the segment size will increase upload bandwidth requirement for the server and download bandwidth requirement for clients. This setting only affects server outbound messages. Changing these settings can have a negative impact on connection stability for some clients."
 
    settings_value["$key"]="25"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    key="minimum_segment_size_peer_count"
 
    settings_prompt["$key"]="[EXPERT] Minimum segment size peer count"
 
    settings_description["$key"]="See description for minimum_segment_size"
 
    settings_value["$key"]="20"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    key="maximum_segment_size"
 
    settings_prompt["$key"]="[EXPERT] Maximum segment size"
 
    settings_description["$key"]="See description for minimum_segment_size"
 
    settings_value["$key"]="100"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    key="maximum_segment_size_peer_count"
 
    settings_prompt["$key"]="[EXPERT] Maxmium segment size peer count"
 
    settings_description["$key"]="See description for minimum_segment_size"
 
    settings_value["$key"]="10"
 
    settings_type["$key"]="int"
 
    settings_order+=("$key")
 

	
 
    # Loop until user has confirmed the settings.
 
    while [[ ${confirmed,,} != 'y' ]]; do
 
        confirmed=""
 

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

	
 
        # Prompt user to confirm settings.
 
        while [[ ${confirmed,,} != 'y' && ${confirmed,,} != 'n' ]]; do
 
            echo
 
            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]:"
 
                elif [[ ${settings_type[$key]} == "int" ]]; then
 
                    prompt="${settings_prompt[$key]} [number]:"
 
                elif [[ ${settings_type[$key]} == "str" ]]; then
 
                    prompt="${settings_prompt[$key]} [text]:"
 
                elif [[ ${settings_type[$key]} == "list" ]]; then
 
                    prompt="${settings_prompt[$key]} [space-delimited list]:"
 
                else
 
                    prompt="${settings_prompt[$key]} [${settings_type[$key]}]:"
 
                fi
 

	
 
                # 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_result=""
 
                until [[ $validation_result == 0 ]]; do
 

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

	
 
                    validate_server_setting_value "${settings_prompt[$key]}" "$value" "${settings_type[$key]}"
 
                    validation_result="$?"
 
                    [[ $validation_result == 2 ]] && error "Internal error, type not set correctly for setting: $key." && exit "$ERROR_GENERAL"
 
                done
 

	
 
                settings_value[$key]="$value"
 

	
 
                echo
 
                let current_question++
 
            done
 
        fi
 
    done
 

	
 
    # Prepare values so they can be used within the JSON file.
 
    for key in "${settings_order[@]}"; do
 
        debug "Raw value for $key: ${settings_value[$key]}"
 

	
 
        if ! validate_server_setting_value "$key (${settings_prompt[$key]})" "${settings_value[$key]}" "${settings_type[$key]}"; then
 
            error "Failed to validate the settings value. This is most likely an internal bug in $program."
 
            exit "$ERROR_GENERAL"
 
        fi
 

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

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

	
 
            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
 

	
 
        # 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
 
        debug "Processed value for $key: ${settings_value[$key]}"
 
    done
 
}
 

	
 
#
 
# Validates paths according to internal tool rules or terminates the
 
# program.
 
#
 
# This function helps to reduce boilerplate and centralise some common
 
# checks around handling of various paths.
 
#
 
# Arguments:
 
#
 
#   $1 (path_test)
 
#     Test to apply against the path. Supported tests are:
 
#
 
#       game_installations_directory
 
#         Checks if path contains Factorio installations in
 
#         sub-directories.
 
#
 
#       instance_directory_new
 
#         Checks if path is unused and can be used for new instance.
 
#
 
#       instance_directory
 
#         Checks if path contains a valid instance.
 
#
 
#       backup_directory_new
 
#         Checks if path is usable as destination for a new backup.
 
#
 
#       backup_directory
 
#         Checks if path contains a valid backup.
 
#
 
#       instance_import_source
 
#         Checks if path is usable as source for importing an instance.
 
#
 
#   $2 (path)
 
#     Path that should be validated.
 
#
 
#   $3 (exit_code)
 
#     Exit code to use when terminating the program.
 
#
 
# Exits:
 
#
 
#   If the path type is unsupported, or if the specified path does not
 
#   conform to requirements for the specified path type.
 
#
 
function validate_path_or_terminate() {
 
    local path_test="$1"
 
    local path="$2"
 
    local exit_code="$3"
 

	
 
    if [[ $path_test == "game_installations_directory" ]]; then
 
        # Make sure user has set directory with game installations -
 
        # test both symlink and target destination.
 
        if [[ ! -L $game_installations_directory || ! -d $game_installations_directory ]]; then
 
            error "Game installations directory has not been properly set. Please run the set-game-dir command first."
 
            exit "$exit_code"
 
        fi
 

	
 
    elif [[ $path_test == "instance_directory_new" ]]; then
 
        if [[ -e $path/config.ini ]]; then
 
            error "Instance already exists."
 
            exit "$exit_code"
 
        fi
 

	
 
        if [[ -e $path ]]; then
 
            error "Instance directory already exists, but does not contain a valid Factorio instance: $path."
 
            exit "$exit_code"
 
        fi
 

	
 
    elif [[ $path_test == "instance_directory" ]]; then
 
        if [[ ! -d $path ]]; then
 
            error "Missing instance directory: $path"
 
            error "Perhaps you have misstyped the instance name or forgot to create one first?"
 
            exit "$exit_code"
 
        fi
 

	
 
        if [[ ! -f $path/instance.conf ]]; then
 
            error "Missing instance configuration file: $path/instance.conf"
 
            exit "$exit_code"
 
        fi
 

	
 
        if [[ ! -f $path/config.ini ]]; then
 
            error "Missing game configuration file: $path/config.ini"
 
            exit "$exit_code"
 
        fi
 

	
0 comments (0 inline, 0 general) First comment
You need to be logged in to comment. Login now