Changeset - 8551913bda80
[Not reviewed]
0 1 0
Branko Majic (branko) - 5 years ago 2020-07-05 15:50:03
branko@majic.rs
Noticket: Implement additional path validations for Factorio Manager:

- Added test for instance directories, new backup directories, backup
directories, and import sources.
- Refactored commands to use the new path validations.
- Updated some comments and formatting.
1 file changed with 156 insertions and 190 deletions:
0 comments (0 inline, 0 general)
games/factorio_manager.sh
Show inline comments
 
@@ -822,82 +822,145 @@ function read_server_settings() {
 
        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_code0
 
#   $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
 

	
 
    elif [[ $path_test == "backup_directory_new" ]]; then
 
        if [[ -e $path/config.ini ]]; then
 
            error "Backup already exists under directory: $path"
 
            exit "$exit_code"
 
        elif [[ -e $path ]]; then
 
            error "Backup directory already exists, but does not contain a valid backup: $path"
 
            exit "$exit_code"
 
        fi
 

	
 
    elif [[ $path_test == "backup_directory" ]]; then
 

	
 
        if [[ ! -d $path ]]; then
 
            error "Specified backup not available under: $path"
 
            exit "$ERROR_ARGUMENTS"
 
        fi
 

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

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

	
 
    elif [[ $path_test == "instance_import_source" ]]; then
 
        if [[ ! -f $path/bin/x64/factorio ]]; then
 
            error "Could not locate Factorio binary in directory under: $path/bin/x64/factorio"
 
            error "Factorio Manager natively supports instance imports only when all game data (savegames etc) are stored within Factorio installation directory."
 
            exit "$ERROR_ARGUMENTS"
 
        fi
 

	
 
    else
 
        error "Unable to validate path '$path', unsupported test requested: '$path_test'."
 
        exit "$ERROR_GENERAL"
 
    fi
 
}
 

	
 
#
 
# Prompts user to pick version of Factorio.
 
#
 
# Arguments:
 
#
 
#   $1 (game_installations_directory)
 
#     Path to directory containing Factorio installations.
 
#
 
#   $2 (default_version, optional)
 
#     Default version to fill-in for the user. If not set, last
 
#     version in the directory (sorted alphabetically) will be used.
 
#
 
#   $3 (default_marker, optional)
 
#     Marker text to use for the default version. Default is "default".
 
#
 
# Returns:
 
#
 
#   0 if version was successfully selected, 1 otherwise.
 
@@ -989,147 +1052,159 @@ fi
 
while getopts "qdvh" opt; do
 
    case "$opt" in
 
	q) quiet=1;;
 
	d) debug=1;;
 
        v) version
 
           exit "$SUCCESS";;
 
        h) usage
 
           exit "$SUCCESS";;
 
        *) usage
 
           exit "$ERROR_ARGUMENTS";;
 
    esac
 
done
 
i=$OPTIND
 
shift $(($i-1))
 

	
 
# Make sure the manager home directory exists.
 
if [[ ! -e $manager_directory ]]; then
 
    info "Creating Factorio Manager home directory under: $manager_directory"
 
    mkdir -p "$manager_directory"
 
fi
 

	
 
command="$1"
 
shift
 

	
 

	
 
#==============#
 
# set-game-dir #
 
#==============#
 
if [[ $command == set-game-dir ]]; then
 

	
 
    # Read and verify additional positional arguments.
 
    game_installations_directory_target="${1-}"
 
    shift
 

	
 
    if [[ -z $game_installations_directory_target ]]; then
 
        error "Missing argument: GAME_INSTALLATIONS_DIRECTORY"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ ! -e $game_installations_directory_target ]]; then
 
        error "No such directory: $game_installations_directory"
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    # Verify that at least one Factorio instance can be found.
 
    factorio_versions_found=0
 
    for candidate in "$game_installations_directory_target"/*; do
 
        if [[ -f $candidate/bin/x64/factorio ]]; then
 
            let factorio_versions_found++
 
        fi
 
    done
 

	
 
    if (( $factorio_versions_found == 0 )); then
 
        error "Could not locate any Factorio installations under: $game_installations_directory_target"
 

	
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    # Update the link
 
    if [[ -L $game_installations_directory ]]; then
 
        rm "$game_installations_directory"
 
    fi
 

	
 
    if ! ln -s "$game_installations_directory_target" "$game_installations_directory"; then
 
        error "Could not create symlink from '$game_installations_directory_target' to '$game_installations_directory'."
 

	
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 

	
 
#==========#
 
# versions #
 
#==========#
 
elif [[ $command == versions ]]; then
 

	
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    echo "Locally available Factorio versions:"
 
    echo
 

	
 
    # Find all sub-directories that are Factorio installations.
 
    for candidate in "$game_installations_directory"/*; do
 
        if [[ -d $candidate && -e $candidate/bin/x64/factorio ]]; then
 
            echo "  - $(basename "$candidate")"
 
        fi
 
    done
 
    echo
 

	
 

	
 
#======#
 
# list #
 
#======#
 
elif [[ $command == list ]]; then
 

	
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    echo "Available instances:"
 
    echo
 

	
 
    # Find all sub-directories that are valid instances.
 
    for candidate in "$manager_directory"/*; do
 
        if [[ -f $candidate/instance.conf ]]; then
 
            source "$candidate/instance.conf"
 
            echo "  - $(basename "$candidate") ($game_version)"
 
        fi
 
    done
 
    echo
 

	
 

	
 
#========#
 
# create #
 
#========#
 
elif [[ $command == create ]]; then
 

	
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    shift
 

	
 
    # Calculate derived variables.
 
    instance_directory="$manager_directory/$instance"
 
    instance_config="$instance_directory/instance.conf"
 
    game_config="$instance_directory/config.ini"
 

	
 
    # Verify arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Make sure new instance directory can be used.
 
    validate_path_or_terminate "instance_directory_new" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    select_factorio_version "$game_installations_directory" || exit "$ERROR_GENERAL"
 

	
 
    # Set-up the instance.
 
    mkdir "$instance_directory"
 
    mkdir "$instance_directory/mods"
 
    echo "{}" > "$instance_directory/mods/mod-list.json"
 

	
 
    cat <<EOF > "$instance_config"
 
game_version="$game_version_selected"
 
EOF
 

	
 
    # @TODO: If we could somehow obtain stock config.ini file without
 
    # having to first run Factorio, that would be great. As it is, the
 
    # user will presented with warning about corrupt config.ini when
 
    # running the instance for the first time.
 
    cat <<EOF > "$game_config"
 
[path]
 
read-data=__PATH__executable__/../../data
 
write-data=${instance_directory}
 

	
 
[general]
 
locale=
 
@@ -1138,314 +1213,288 @@ locale=
 
check-updates=false
 
enable-crash-log-uploading=false
 

	
 
[interface]
 

	
 
[controls]
 

	
 
[sound]
 

	
 
[map-view]
 

	
 
[debug]
 

	
 
[multiplayer-lobby]
 

	
 
[graphics]
 
EOF
 

	
 
    echo
 
    warning "Factorio Manager has created a minimal empty configuration file for Factorio under $game_config."
 
    warning "Since the generated configuration file is almost empty, Factorio will complain that the file seems corrupt."
 
    warning "Factorio will offer to fix the corrupted configuration file by filling-in the missing information during the first startup."
 
    warning "It should be safe to accept this. This warning will be shown by Factorio only the first time."
 

	
 

	
 
#========#
 
# launch #
 
#========#
 
elif [[ $command == launch ]]; then
 

	
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    shift
 

	
 
    # Set-up derived variables.
 
    instance_directory="$manager_directory/$instance"
 
    instance_config="$instance_directory/instance.conf"
 
    game_config="$instance_directory/config.ini"
 
    server_config="$instance_directory/server-settings.json"
 

	
 
    # Verify arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ ! -f $instance_config ]]; then
 
        error "Missing instance configuration file: $instance_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    if [[ ! -f $game_config ]]; then
 
        error "Missing game configuration file: $game_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 
    # Validate that instance directory contains valid instance.
 
    validate_path_or_terminate "instance_directory" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    # Update instance write directory prior to launching - this is a
 
    # failsafe in case it got changed by hand through some other means
 
    # (or maybe it was copied over from another instance).
 
    # failsafe in case it got changed by hand or perhaps the value
 
    # comes from backups of a copied instance.
 
    current_write_data=$(grep "^write-data=" "$game_config")
 
    expected_write_data="write-data=${instance_directory}"
 

	
 
    if [[ $current_write_data != $expected_write_data ]]; then
 
        warning "Incorrect path specified for write-data in game configuration file: $game_config"
 
        warning "Current configuration is: $current_write_data"
 
        warning "Configuration will be replaced with: $expected_write_data"
 
        sed -i -e "s#^write-data=.*#$expected_write_data#" "$game_config"
 
    fi
 

	
 
    # Read launcher configuration for the instance.
 
    source "$instance_config"
 

	
 
    if [[ -z $game_version ]]; then
 
        error "Missing game version information in $game_config."
 
        exit "$ERROR_CONFIGURATION"
 
    fi
 

	
 
    # Set-up paths for launching the game, and ensure they still exist
 
    # (versions can be removed by user).
 
    game_directory="${game_installations_directory}/${game_version}"
 
    factorio_bin="$game_directory/bin/x64/factorio"
 

	
 
    if [[ ! -e $factorio_bin ]]; then
 
        error "Could not locate Factorio binary under: $factorio_bin"
 
        error "Factorio $game_version installation may have been removed from game installations directory:"
 
        error "   $(readlink -f "$game_installations_directory")"
 
        exit "$ERROR_CONFIGURATION"
 
    fi
 

	
 
    # Launch instance
 
    if [[ -e $server_config ]]; then
 
        "$factorio_bin" --config "$game_config" --start-server "$instance_directory/saves/default.zip"
 
    else
 
        "$factorio_bin" --config "$game_config"
 
    fi
 

	
 

	
 
#========#
 
# backup #
 
#========#
 
elif [[ $command == backup ]]; then
 

	
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    description="${2-}"
 
    shift 2
 

	
 
    # Use timestamp-based names for backups.
 
    timestamp=$(date +%Y-%m-%d_%H:%M:%S)
 

	
 
    # Set-up derived variables.
 
    instance_directory="$manager_directory/$instance"
 
    instance_config="$instance_directory/instance.conf"
 
    game_config="$instance_directory/config.ini"
 
    lock_file="$instance_directory/.lock"
 

	
 
    backup_directory="$instance_directory/.bak"
 
    backup_destination="$backup_directory/$timestamp"
 
    backup_description="$backup_destination/.description"
 

	
 
    # Verify positional arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Verify that we are trying to backup an actual instance.
 
    if [[ ! -f $instance_config ]]; then
 
        error "Missing instance configuration file: $instance_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    if [[ ! -f $game_config ]]; then
 
        error "Missing game configuration file: $game_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 
    # Validate that instance directory contains valid instance.
 
    validate_path_or_terminate "instance_directory" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    # Make sure we do not overwrite the backup destination by mistake
 
    # - although highly unlikely unless user spams the backup command.
 
    if [[ -e $backup_destination ]]; then
 
        error "Backup already exists: $backup_destination"
 
    fi
 
    # Validate destination path for backup directory can be used.
 
    validate_path_or_terminate "backup_directory_new" "$backup_destination" "$ERROR_GENERAL"
 

	
 
    (
 

	
 
        # Obtain lock - Factorio uses the same mechanism, so we should
 
        # be able to detect the game is running in this way.
 
        flock --exclusive --nonblock 200
 
        if [[ $? != 0 ]]; then
 
            error "Could not lock instance directory via lock file $lock_file. Is Factorio instance still running?"
 
            exit "$ERROR_GENERAL"
 
        fi
 

	
 
        function remove_lock() {
 
            rm "$lock_file"
 
        }
 

	
 
        trap remove_lock EXIT
 

	
 
        # Backup the instance. Clean-up the backup destination in case of failure.
 
        mkdir -p "$backup_destination"
 
        if ! cp -a "$instance_directory"/* "$backup_destination"; then
 
            error "Could not create backup under: $backup_destination"
 
            rm -rf "$backup_destination"
 
            exit "$ERROR_GENERAL"
 
        fi
 

	
 
        # Store (optional) description.
 
        if [[ -n $description ]]; then
 
            echo "$description" > "$backup_description"
 
        fi
 

	
 
        success "Backup saved to: $backup_destination"
 

	
 
    ) 200>"$lock_file"
 

	
 

	
 
#==============#
 
# list-backups #
 
#==============#
 
elif [[ $command == list-backups ]]; then
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    shift
 

	
 
    # Set-up derived variables.
 
    instance_directory="$manager_directory/$instance"
 
    instance_config="$instance_directory/instance.conf"
 
    game_config="$instance_directory/config.ini"
 
    backup_directory="$instance_directory/.bak"
 

	
 
    # Verify positional arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Verify instance.
 
    if [[ ! -d $instance_directory ]]; then
 
        error "No such instance: $instance"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ ! -f $instance_config ]]; then
 
        error "Missing instance configuration file: $instance_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    if [[ ! -f $game_config ]]; then
 
        error "Missing game configuration file: $game_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 
    # Validate that instance directory contains valid instance.
 
    validate_path_or_terminate "instance_directory" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    # Extended regular expression for matching YYYY-MM-DD-hh:mm:ss format.
 
    backup_destination_name_pattern="[[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}_[[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2}"
 

	
 
    # Read current game version.
 
    source "$instance_config"
 
    current_game_version="$game_version"
 
    unset game_version
 

	
 
    # Detect and show available backups to the user.
 
    if [[ ! -d $backup_directory ]] || ! ls "$backup_directory" | grep -q -E "^$backup_destination_name_pattern\$"; then
 
        echo "No backups are available for instance $(colorecho -n green "$instance")."
 
    else
 
        echo "Available backups for instance $(colorecho -n green "$instance") (current version $(colorecho -n green "$current_game_version"))."
 
        echo
 

	
 
        for backup_destination in "$backup_directory"/*; do
 

	
 
            if [[ $backup_destination =~ $backup_destination_name_pattern ]]; then
 
                backup_date=$(basename "$backup_destination")
 

	
 
                # Read instance configuration for backup.
 
                source "$backup_destination/instance.conf"
 

	
 
                if [[ -f "$backup_destination/.description" ]]; then
 
                    backup_description=$(<"$backup_destination/.description")
 
                    echo "  - $backup_date - version $(colorecho -n green "$game_version") ($backup_description)"
 
                else
 
                    echo "  - $backup_date - version $(colorecho -n green "$game_version")"
 
                fi
 

	
 

	
 
            fi
 
        done
 

	
 
        echo
 

	
 
    fi
 

	
 

	
 
#=========#
 
# restore #
 
#=========#
 
elif [[ $command == restore ]]; then
 

	
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    backup_name="${2-}"
 
    shift 2
 

	
 
    # Set-up derived values.
 
    instance_directory="$manager_directory/$instance"
 
    restore_source="$instance_directory/.bak/$backup_name"
 
    backup_instance_config="$restore_source/instance.conf"
 
    backup_game_config="$restore_source/config.ini"
 
    lock_file="$instance_directory/.lock"
 

	
 
    # Verify positional arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ -z $backup_name ]]; then
 
        error "Missing argument: BACKUP_NAME"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Verify we are working with legitimate instance and backup.
 
    if [[ ! -d $instance_directory ]]; then
 
        error "No such instance: $instance"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ ! -d $restore_source ]]; then
 
        error "Specified backup not available under: $restore_source"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 
    # Validate that instance directory contains valid instance.
 
    validate_path_or_terminate "instance_directory" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    if [[ ! -f $backup_instance_config ]]; then
 
        error "Invalid backup, missing instance configuration file: $backup_instance_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    if [[ ! -f $backup_game_config ]]; then
 
        error "Invalid backup, missing game configuration file: $backup_game_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 
    # Validate that backup directory contains valid backup.
 
    validate_path_or_terminate "backup_directory" "$restore_source" "$ERROR_ARGUMENTS"
 

	
 
    (
 
        # Obtain lock - Factorio uses the same mechanism, so we should
 
        # be able to detect the game is running in this way.
 
        flock --exclusive --nonblock 200
 
        if [[ $? != 0 ]]; then
 
            error "Could not lock instance directory via lock file $lock_file. Is Factorio instance still running?"
 
            exit "$ERROR_GENERAL"
 
        fi
 

	
 
        function remove_lock() {
 
            rm "$lock_file"
 
        }
 
        trap remove_lock EXIT
 

	
 
        # Set-up a list of files and directories that will get removed.
 
        shopt -s nullglob
 
        entries_to_remove=($instance_directory/*)
 
        shopt -u nullglob
 

	
 
        # Present user with extensive warning on consequences.
 
        echo
 
        warning "You are about to replace current instance's files, including (but not limited to):"
 
        warning
 
@@ -1481,295 +1530,254 @@ elif [[ $command == restore ]]; then
 
            echo
 
        fi
 

	
 
        # Request from user to confirm the operation.
 
        critical_confirmation "Are you sure you want to proceed?" \
 
                              "Aborted restore process, no changes have been made to instance files." \
 
                              "$ERROR_GENERAL"
 

	
 
        if [[ ${#entries_to_remove[@]} != 0 ]]; then
 
            if ! rm -rf "${entries_to_remove[@]}"; then
 
                error "Failed to remove existing instance files."
 
                exit "$ERROR_GENERAL"
 
            fi
 
        fi
 

	
 
        if ! cp -a "$restore_source"/* "$instance_directory"; then
 
            error "Failed to restore backup from: $restore_source"
 
            exit "$ERROR_GENERAL"
 
        fi
 

	
 
        success "Instance restored from backup."
 

	
 
    ) 200>"$lock_file"
 

	
 

	
 
#===============#
 
# remove-backup #
 
#===============#
 
elif [[ $command == remove-backup ]]; then
 

	
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    backup_name="${2-}"
 
    shift 2
 

	
 
    # Verify positional arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ -z $backup_name ]]; then
 
        error "Missing argument: BACKUP_NAME"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Set-up derived values.
 
    instance_directory="$manager_directory/$instance"
 
    removal_target="$instance_directory/.bak/$backup_name"
 
    backup_instance_config="$removal_target/instance.conf"
 
    backup_game_config="$removal_target/config.ini"
 
    lock_file="$instance_directory/.lock"
 

	
 
    # Verify we are working with legitimate instance and backup.
 
    if [[ ! -d $instance_directory ]]; then
 
        error "No such instance: $instance"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 
    # Validate that instance directory contains valid instance.
 
    validate_path_or_terminate "instance_directory" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    if [[ ! -d $removal_target ]]; then
 
        error "Specified backup not available under: $removal_target"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ ! -f $backup_instance_config ]]; then
 
        error "Invalid backup, missing instance configuration file: $backup_instance_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    if [[ ! -f $backup_game_config ]]; then
 
        error "Invalid backup, missing game configuration file: $backup_game_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 
    # Validate that backup directory contains valid backup.
 
    validate_path_or_terminate "backup_directory" "$removal_target" "$ERROR_ARGUMENTS"
 

	
 
    # Present user with warning.
 
    echo
 
    if [[ -f "$removal_target/.description" ]]; then
 
        backup_description=$(<"$removal_target/.description")
 
        backup_info="$backup_name ($backup_description)"
 
    else
 
        backup_info="$backup_name"
 
    fi
 

	
 
    warning "You are about to remove an instance backup. All files belonging to specified backup will be removed."
 
    echo
 
    echo "Instance: $(colorecho -n green "$instance")"
 
    echo
 
    echo "Backup: $(colorecho -n green "$backup_info")"
 
    echo
 
    echo "Files and directories that will be removed:"
 
    echo
 
    echo " - $removal_target"
 
    echo
 

	
 
    # Request from user to confirm the operation.
 
    critical_confirmation "Are you sure you want to proceed?" \
 
                          "Aborted backup removal, no changes have been made to backup files." \
 
                          "$ERROR_GENERAL"
 

	
 
    if ! rm -rf "$removal_target"; then
 
        error "Failed to remove existing instance files."
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    success "Backup removed."
 

	
 

	
 
#=============#
 
# set-version #
 
#=============#
 
elif [[ $command == set-version ]]; then
 

	
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    shift
 

	
 
    # Verify positional arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

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

	
 
    if [[ ! -d $game_installations_directory ]]; then
 
        error "Game installations directory has not been properly set. Please run the set-game-dir command first."
 
        exit "$ERROR_CONFIGURATION"
 
    fi
 

	
 
    # Set-up derived variables.
 
    instance_directory="$manager_directory/$instance"
 
    instance_config="$instance_directory/instance.conf"
 
    game_config="$instance_directory/config.ini"
 

	
 
    # Verify we are working with legitimate instance.
 
    if [[ ! -f $instance_config ]]; then
 
        error "Missing instance configuration file: $instance_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    if [[ ! -f $game_config ]]; then
 
        error "Missing game configuration file: $game_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 
    # Validate that instance directory contains valid instance.
 
    validate_path_or_terminate "instance_directory" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    # Load instance configuration.
 
    source "$instance_config"
 

	
 
    # Display list of available Factorio versions.
 
    echo "Current version used for instance $(colorecho -n green "$instance") is $(colorecho -n green "$game_version")."
 
    echo
 
    select_factorio_version "$game_installations_directory" "$game_version" "current" || exit "$ERROR_GENERAL"
 
    echo
 

	
 
    # Change instance game version.
 
    if [[ $game_version_selected == $game_version ]]; then
 
        info "Current version has been kept."
 
    else
 
        sed -i -e "s/^game_version=.*/game_version=$game_version_selected/" "$instance_config"
 
        success "Version changed to: $(colorecho -n green "$game_version_selected")"
 
    fi
 

	
 

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

	
 
    if [[ ! -d $game_installations_directory ]]; then
 
        error "Game installations directory has not been properly set. Please run the set-game-dir command first."
 
        exit "$ERROR_CONFIGURATION"
 
    fi
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    shift
 

	
 
    # Verify positional arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Set-up derived variables.
 
    instance_directory="$manager_directory/$instance"
 
    instance_config="$instance_directory/instance.conf"
 
    game_config="$instance_directory/config.ini"
 

	
 
    # Verify we are working with legitimate instance.
 
    if [[ ! -f $instance_config ]]; then
 
        error "Missing instance configuration file: $instance_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    if [[ ! -f $game_config ]]; then
 
        error "Missing game configuration file: $game_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 
    # Validate that instance directory contains valid instance.
 
    validate_path_or_terminate "instance_directory" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    # Load instance configuration.
 
    source "$instance_config"
 

	
 
    # Basic information.
 
    echo "Instance name: $(colorecho -n green "$instance")"
 

	
 
    if [[ -e "$game_installations_directory/$game_version" ]]; then
 
        echo "Game version: $(colorecho -n green "$game_version")"
 
    else
 
        echo "Game version: $(colorecho -n red "$game_version [not available locally]")"
 
    fi
 

	
 
    echo "Instance path: $(colorecho -n green "$instance_directory")"
 
    echo
 

	
 
    # Mod information.
 
    shopt -s nullglob
 
    mod_files=($instance_directory/mods/*.zip)
 
    mod_list="$instance_directory/mods/mod-list.json"
 
    shopt -u nullglob
 

	
 
    if [[ ${#mod_files[@]} == 0 ]]; then
 
        echo "Available mods: none"
 

	
 
    # Plain listing.
 
    # Listing using just regular tools.
 
    elif ! hash jqs 2>/dev/null; then
 
        echo "Available mods:"
 
        echo
 

	
 
        # Determine if mod is enabled by default or not when added to
 
        # mods directory (and is not yet listed in the mod list file).
 
        if grep -i -q "^[[:blank:]]*enable-new-mods=false" "$game_config"; then
 
            enable_new_mods="false"
 
        else
 
            enable_new_mods="true"
 
        fi
 

	
 
        # Process every mod found.
 
        for mod_file in "${mod_files[@]}"; do
 

	
 
            if [[ -f $mod_file ]]; then
 
                # Extract basic information about mod from the filename.
 
                mod_basename=$(basename "$mod_file" .zip)
 
                mod_name="${mod_basename%_*}"
 
                mod_version="${mod_basename##*_}"
 

	
 
                # Determine if mod is enabled or not.
 

	
 
                if grep -A1 "\"name\": \"$mod_name\"" "$mod_list" | tail -n1 | grep -q '"enabled": false'; then
 
                    color="$_text_yellow"
 
                elif grep -A1 "\"name\": \"$mod_name\"" "$mod_list" | tail -n1 | grep -q '"enabled": true'; then
 
                    color=""
 
                elif [[ $enable_new_mods == "false" ]]; then
 
                    color="$_text_yellow"
 
                else
 
                    color=""
 
                fi
 

	
 
                # Show colored-information for the mod.
 
                printf "$color  - %-48s %8s $_text_reset\n" "$mod_name" "$mod_version"
 
            fi
 

	
 
        done
 

	
 
    # Fancy listing with detection for enabled mods using jq.
 
    # Listing using the jq utility.
 
    elif hash jq 2>/dev/null; then
 
        echo "Available mods (enabled/${_text_yellow}disabled${_text_reset}):"
 
        echo
 

	
 
        # Determine if mod is enabled by default or not when added to
 
        # mods directory (and is not yet listed in the mod list file).
 
        if grep -i -q "^[[:blank:]]*enable-new-mods=false" "$game_config"; then
 
            enable_new_mods="false"
 
        else
 
            enable_new_mods="true"
 
        fi
 

	
 
        # Query string used in jq tool to determine if mod is enabled
 
        # or not. Take note that this string should remain
 
        # single-quoted, and that $ expansions are actually done
 
        # internally in jq itself. Query accepts two vars - mod_name
 
        # and enable_new_mods.
 
        jq_is_enabled_query='.mods | map(select(.name==$mod_name))[0] // {"name": "default", "enabled": $enable_new_mods} | .enabled'
 

	
 
        # Process every mod found.
 
        for mod_file in "${mod_files[@]}"; do
 

	
 
            if [[ -f $mod_file ]]; then
 

	
 
@@ -1783,73 +1791,68 @@ elif [[ $command == info ]]; then
 
                      --arg "mod_name" "$mod_name" \
 
                      --argjson "enable_new_mods" "$enable_new_mods" \
 
                      "$jq_is_enabled_query" "$mod_list" > /dev/null; then
 
                    color=""
 
                else
 
                    color="$_text_yellow"
 
                fi
 

	
 
                # Show colored-information for the mod.
 
                printf "$color  - %-48s %8s $_text_reset\n" "$mod_name" "$mod_version"
 
            fi
 
        done
 
    fi
 
    echo
 

	
 
    # Call self for displaying list of backups. Better than duplicating code.
 
    "$program" list-backups "$instance" | sed -e "s/^Available backups for instance.*/Available backups:/"
 

	
 

	
 
#========#
 
# remove #
 
#========#
 
elif [[ $command == remove ]]; then
 

	
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    shift
 

	
 
    # Verify positional arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Set-up derived values.
 
    instance_directory="$manager_directory/$instance"
 
    instance_config="$instance_directory/instance.conf"
 
    lock_file="$instance_directory/.lock"
 

	
 
    # Verify we are working with legitimate instance and backup.
 
    if [[ ! -d $instance_directory ]]; then
 
        error "No such instance: $instance"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ ! -f $instance_config ]]; then
 
        error "Missing instance configuration file: $instance_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 
    # Validate that instance directory contains valid instance.
 
    validate_path_or_terminate "instance_directory" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    (
 
        # Obtain lock - Factorio uses the same mechanism, so we should
 
        # be able to detect the game is running in this way.
 
        flock --exclusive --nonblock 200
 
        if [[ $? != 0 ]]; then
 
            error "Could not lock instance directory via lock file $lock_file. Is Factorio instance still running?"
 
            exit "$ERROR_GENERAL"
 
        fi
 

	
 
        # Removing lock file will most likely fail if we manage to
 
        # remove the instance.
 
        function remove_lock() {
 
            rm -f "$lock_file"
 
        }
 
        trap remove_lock EXIT
 

	
 
        # Set-up a list of files and directories that will get removed.
 
        shopt -s nullglob dotglob
 
        entries_to_remove=($instance_directory/*)
 
        shopt -u nullglob dotglob
 

	
 
        # Present user with extensive warning on consequences.
 
        echo
 
@@ -1891,101 +1894,80 @@ elif [[ $command == remove ]]; then
 
                              "$ERROR_GENERAL"
 

	
 
        if [[ ${#entries_to_remove[@]} != 0 ]]; then
 
            if ! rm -rf "${entries_to_remove[@]}"; then
 
                error "Failed to remove instance files."
 
                exit "$ERROR_GENERAL"
 
            fi
 
        fi
 

	
 
        if ! rmdir "$instance_directory"; then
 
            error "Failed to remove instance files."
 
            exit "$ERROR_GENERAL"
 
        fi
 

	
 
        success "Instance removed."
 

	
 
    ) 200>"$lock_file"
 

	
 

	
 
#======#
 
# copy #
 
#======#
 
elif [[ $command == copy ]]; then
 

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

	
 
    if [[ ! -d $game_installations_directory ]]; then
 
        error "Game installations directory has not been properly set. Please run the set-game-dir command first."
 
        exit "$ERROR_CONFIGURATION"
 
    fi
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    # Read positional arguments.
 
    source_instance="${1-}"
 
    destination_instance="${2-}"
 
    shift 2
 

	
 
    # Verify positional arguments.
 
    if [[ -z $source_instance ]]; then
 
        error "Missing argument: SOURCE_INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ -z $destination_instance ]]; then
 
        error "Missing argument: DESTINATION_INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Calculate derived variables.
 
    source_instance_directory="$manager_directory/$source_instance"
 
    source_instance_config="$source_instance_directory/instance.conf"
 
    source_game_config="$source_instance_directory/config.ini"
 
    source_lock_file="$source_instance_directory/.lock"
 

	
 
    destination_instance_directory="$manager_directory/$destination_instance"
 
    destination_instance_config="$destination_instance_directory/instance.conf"
 

	
 
    # Verify we are working with legitimate source and destination.
 
    if [[ ! -d $source_instance_directory ]]; then
 
        error "No such instance: $source_instance"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ ! -f $source_instance_config ]]; then
 
        error "Missing instance configuration file: $source_instance_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 

	
 
    if [[ ! -f $source_game_config ]]; then
 
        error "Missing game configuration file: $source_game_config"
 
        exit "$ERROR_GENERAL"
 
    fi
 
    # Validate that source instance directory contains valid instance.
 
    validate_path_or_terminate "instance_directory" "$source_instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    # Make sure destination instance directory can be used.
 
    validate_path_or_terminate "instance_directory_new" "$destination_instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    (
 
        # Obtain lock - Factorio uses the same mechanism, so we should
 
        # be able to detect the game is running in this way.
 
        flock --exclusive --nonblock 200
 
        if [[ $? != 0 ]]; then
 
            error "Could not lock instance directory via lock file $source_lock_file. Is Factorio instance still running?"
 
            exit "$ERROR_GENERAL"
 
        fi
 

	
 
        function remove_lock() {
 
            rm "$source_lock_file"
 
        }
 

	
 
        trap remove_lock EXIT
 

	
 
        # Load source instance configuration.
 
        source "$source_instance_config"
 

	
 
        # Display list of available Factorio versions.
 
        echo
 
        echo "If you wish to, you can now change version of Factorio used for destination instance, or keep the same version as for source instance."
 
        echo
 
@@ -2010,111 +1992,102 @@ elif [[ $command == copy ]]; then
 

	
 
        # Set-up a list of files and directories to copy.
 
        entries_to_copy=($source_instance_directory/*)
 

	
 
        if [[ $copy_backups == "y" && -e "$source_instance_directory/.bak" ]]; then
 
             entries_to_copy+=("$source_instance_directory/.bak")
 
        fi
 

	
 
        # Create copy of source instance.
 
        mkdir "$destination_instance_directory"
 
        cp -a "${entries_to_copy[@]}" "$destination_instance_directory/"
 

	
 
        # Update write-data directory of destination instance,
 
        # including the backups.
 
        write_data="write-data=${destination_instance_directory}"
 
        find "$destination_instance_directory/" -type f -name config.ini -exec \
 
             sed -i -e "s#^write-data=.*#$write_data#" '{}' \;
 

	
 
        sed -i -e "s/^game_version=.*/game_version=$game_version_selected/" "$destination_instance_config"
 

	
 
        success "Created new instance $(colorecho -n green "$destination_instance") using version $(colorecho -n green "$game_version_selected") as copy of instance $(colorecho -n green "$source_instance")."
 

	
 
    ) 200>"$source_lock_file"
 

	
 

	
 
#========#
 
# import #
 
#========#
 
elif [[ $command == import ]]; then
 

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

	
 
    if [[ ! -d $game_installations_directory ]]; then
 
        error "Game installations directory has not been properly set. Please run the set-game-dir command first."
 
        exit "$ERROR_CONFIGURATION"
 
    fi
 
    # Make sure game installations directory has been set.
 
    validate_path_or_terminate "game_installations_directory" "$game_installations_directory" "$ERROR_CONFIGURATION"
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    source_directory="${2-}"
 
    shift 2
 

	
 
    # Verify positional arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    if [[ -z $source_directory ]]; then
 
        error "Missing argument: SOURCE_DIRECTORY"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Calculate derived variables.
 
    instance_directory="$manager_directory/$instance"
 
    instance_config="$instance_directory/instance.conf"
 
    game_config="$instance_directory/config.ini"
 

	
 
    source_config="$source_directory/config/config.ini"
 
    source_lock_file="$source_directory/.lock"
 

	
 
    # Make sure new instance directory can be used.
 
    validate_path_or_terminate "instance_directory_new" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    # Make sure that the import source is valid (should be Factorio
 
    # installation directory).
 
    validate_path_or_terminate "instance_import_source" "$source_directory" "$ERROR_ARGUMENTS"
 

	
 
    # List of entries to import from the source directory.
 
    declare -A import_entries=()
 
    import_entries["achievements-modded.dat"]="contains achivement information for modded plays"
 
    import_entries["achievements.dat"]="contains achievement information for vanilla plays"
 
    import_entries["archive"]="contains desync reports"
 
    import_entries["blueprint-storage.dat"]="contains global (non-savegame specific) blueprints"
 
    import_entries["config/config.ini"]="contains game configuration, including things like shortcuts etc."
 
    import_entries["crop-cache.dat"]="purpose is not known"
 
    import_entries["factorio-current.log"]="contains logs from the currently running game"
 
    import_entries["factorio-previous.log"]="contains logs from the previously running game"
 
    import_entries["mods"]="contains mods and mod settings"
 
    import_entries["player-data.json"]="contains global information about the player, such as username, login token, chat history, etc."
 
    import_entries["saves"]="contains savegames"
 

	
 
    validate_path_or_terminate "instance_directory_new" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    if [[ ! -f $source_directory/bin/x64/factorio ]]; then
 
        error "Could not locate Factorio binary in source directory under: $source_directory/bin/x64/factorio"
 
        error "Factorio Manager natively supports instance imports only when all game data (savegames etc) are stored within Factorio installation directory."
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Display list of available Factorio versions and let user pick one.
 
    echo "Factorio version must  be selected manually for imported instances."
 
    echo
 
    select_factorio_version "$game_installations_directory" || exit "$ERROR_GENERAL"
 

	
 
    (
 

	
 
        # Obtain lock - Factorio uses the same mechanism, so we should
 
        # be able to detect the game is running in this way.
 
        flock --exclusive --nonblock 200
 
        if [[ $? != 0 ]]; then
 
            error "Could not lock instance directory via lock file $source_lock_file. Is Factorio instance still running?"
 
            exit "$ERROR_GENERAL"
 
        fi
 

	
 
        function remove_lock() {
 
            rm "$source_lock_file"
 
        }
 

	
 
        trap remove_lock EXIT
 

	
 
        # Create instance directory.
 
        mkdir "$instance_directory"
 

	
 
@@ -2151,80 +2124,73 @@ EOF
 
        write_data="write-data=${instance_directory}"
 
        sed -i -e "s#^write-data=.*#$write_data#" "$game_config"
 

	
 
        if [[ ${#missing_import_entries[@]} != 0 ]]; then
 
            warning "A number of files or directories were missing from the specified source."
 
            warning "For some of the entries this is perfectly normal, but you should verify that no critical files have been missed by mistake before switching to using this instance."
 
            echo
 
            for missing_import_entry in "${missing_import_entries[@]}"; do
 
                echo "$(colorecho blue "$missing_import_entry"), ${import_entries[$missing_import_entry]}"
 
                echo
 
            done
 
            warning "Press any key to continue."
 
            read -n1
 
        fi
 

	
 
        success "Finished import of instance $(colorecho -n green "$instance")."
 

	
 
    ) 200>"$source_lock_file"
 

	
 

	
 
#===============#
 
# create-server #
 
#===============#
 
elif [[ $command == create-server ]]; then
 
    instance="${1-}"
 

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

	
 
    if [[ ! -d $game_installations_directory ]]; then
 
        error "Game installations directory has not been properly set. Please run the set-game-dir command first."
 
        exit "$ERROR_CONFIGURATION"
 
    fi
 
    instance="${1-}"
 

	
 
    # Read positional arguments.
 
    instance="${1-}"
 
    shift
 

	
 
    # Calculate derived variables.
 
    instance_directory="$manager_directory/$instance"
 
    instance_config="$instance_directory/instance.conf"
 
    game_config="$instance_directory/config.ini"
 
    server_config="$instance_directory/server-settings.json"
 
    saves_directory="$instance_directory/saves"
 
    main_save="$saves_directory/default.zip"
 

	
 
    # Verify arguments.
 
    if [[ -z $instance ]]; then
 
        error "Missing argument: INSTANCE"
 
        exit "$ERROR_ARGUMENTS"
 
    fi
 

	
 
    # Make sure new instance directory can be used.
 
    validate_path_or_terminate "instance_directory_new" "$instance_directory" "$ERROR_ARGUMENTS"
 

	
 
    select_factorio_version "$game_installations_directory" || exit "$ERROR_GENERAL"
 

	
 
    # Grab server settings from user.
 
    echo "You will now be prompted to provide settings for the server, with some pre-filled settings."
 
    echo "Do not change settings marked with [EXPERT] unless you know what you are doing."
 
    echo
 
    read_server_settings "$instance"
 

	
 
    # Set-up the instance.
 
    mkdir "$instance_directory"
 
    mkdir "$instance_directory/mods"
 
    echo "{}" > "$instance_directory/mods/mod-list.json"
 

	
 
    cat <<EOF > "$instance_config"
 
game_version="$game_version_selected"
 
EOF
 
    cat <<EOF > "$game_config"
 
[path]
 
read-data=__PATH__executable__/../../data
 
write-data=${instance_directory}
 

	
 
[general]
0 comments (0 inline, 0 general)