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.
0 comments (0 inline, 0 general) First comment
You need to be logged in to comment. Login now