ArkShop Manager CLI

Dependencies:
Requires jq.exe – for interaction with the Json arkshop config file.
Simply download the windows amd64 .exe, rename it to jq.exe, and put it somewhere already on the server’s PATH.
──────────────────────────
If you need help with adding a location to your server’s PATH,
I’ve created a super quick and simple guide here: Git Bash & Editing your Server’s PATH

Download jq.exe: https://github.com/jqlang/jq/releases/tag/jq-1.8.2
──────────────────────────────

Description:
For use with the ArkShop plugin
Allows one to easily visualise abstracted JSON data.
View/Search/Add/Remove shop items
And roll out changes from a single master config.json to 1 or all servers at once.

Usage:

 List:
   arkshop -ls                   – List all available Kits
   arkshop -ls <item>            – List all items within a kit

 Search:
   arkshop -s <item>             – Search for a Kit/Item

 Add:
   arkshop -a                    – Add a new Kit and Items within
   arkshop -a <kit>              – Add a new item to an existing Kit

 Delete:
   arkshop -d <kit>              – Delete an entire Kit
   arkshop -d <kit> <item-#>     – Delete an item from a kit
                                 (item-# attainable using arkshop -ls)

 Logs:
   arkshop -l <map>              – Display a map’s entire ArkShop Log
   arkshop -f <search>           – Search all maps Arkshop Logs for a search-term
   arkshop -m                    – List all maps you’ve defined in this CLI’s config

 Rollout:
   arkshop -r                    – Rollout the updated config.json to 1 or ALL servers

Examples:
 arkshop -ls
 arkshop -s shotgun
 arkshop -d StarterKit 2
 arkshop -f element


Download arkshop.sh

#!/bin/bash


####################
# CONFIGURATION
####################

# ARKSHOP MASTER CONFIG FILE
SHOP_FILE="/c/ark/arkshop/config.json"

# YOUR ARK SERVERS AND THEIR DIRECTORY
SERVERS=(
    "Island|/C/ARK/Island"
    "Ragnarok|/C/ARK/Ragnarok"
    "Aberration|/C/ARK/Aberration"
    "Extinction|/C/ARK/Extinction"
    "Astraeos|/C/ARK/Astraeos"
    "Genesis|/C/ARK/Genesis"
)

####################
# END CONFIGURATION
####################

# GLOBAL VARIABLES
VERSION="Odi's ArkShop CLI 2.3.2"

####################
# COLOUR VARIABLES
####################

# Terminal Formatting
RESET="\033[0m"
BOLD="\033[1m"

# UI Colours
CLR_RED="\033[38;5;196m"  # Bright Red
CLR_GREEN="\033[38;5;46m" # Bright Lime Green

CLR_GOLD="\033[38;5;226m"         # Gold
CLR_PURPLE="\033[38;5;141m"       # Light Purple / Lavender
CLR_BLUE="\033[38;5;69m"          # Bright Sky Blue
CLR_DGREEN="\033[38;5;2m"         # Dark Forest Green
CLR_ORANGE="\033[38;5;208m"       # Orange
CLR_AQUA="\033[38;5;51m"          # Aqua
CLR_TEXT="\033[38;5;248m"         # Light Grey
CLR_FRAME="${BOLD}\033[38;5;250m" # Darker Grey
CLR_TITLE="\033[38;5;147m"        # Light Grey (optional)

set -e

#######################
# Unicode box Chars
########################
H="─"
V="${CLR_FRAME}│${RESET}"
V2="${CLR_ORANGE}│${RESET}"
VB="│"

TL="┌"
TR="┐"
BL="└"
BR="┘"

LT="├"
RT="┤"
TT="┬"
BT="┴"
CR="┼"

TH_FRAME="${BOLD}${CLR_ORANGE}${TL}──────────────────────────────────────────${TR}${RESET}"
#MD_FRAME="${CLR_FRAME}${LT}────${CR}────────────${CR}───────${RT}${RESET}"
BH_FRAME="${BOLD}${CLR_ORANGE}${BL}──────────────────────────────────────────${BR}${RESET}"

ROW_PREFIX="${CLR_FRAME}${V}${RESET}"

##########
# HELPERS
###########
error() {
    echo
    echo -e "  ${CLR_RED}Error: ${RESET}$1"
    echo
}

#######
# VALIDATION
######

is_int() {
    [[ $1 =~ ^[0-9]+$ ]]
}

is_number() {
    [[ $1 =~ ^[0-9]+([.][0-9]+)?$ ]]
}
####################################
# JSON API
####################################

shop_exists() {

    local id="$1"

    jq -e --arg id "$id" \
        '.ShopItems[$id]' \
        "$SHOP_FILE" >/dev/null
}

shop_get() {

    local id="$1"
    local field="$2"

    if [[ -z "$field" ]]; then
        jq --arg id "$id" \
            '.ShopItems[$id]' \
            "$SHOP_FILE"
    else
        jq -r \
            --arg id "$id" \
            --arg field "$field" \
            '.ShopItems[$id][$field]' \
            "$SHOP_FILE"
    fi
}

shop_list() {

    jq -r '
        .ShopItems
        | to_entries[]
        | [
            .key,
            (.value.Price // "-"),
            (.value.Type // "-"),
            (.value.Description // "-")
        ]
        | @tsv
    ' "$SHOP_FILE"
}

shop_add() {

    local id="$1"
    local tmp
    local new_item

    if [[ "$type" == "dino" ]]; then

        new_item=$(
            jq -n \
                --arg desc "$description" \
                --arg type "$type" \
                --argjson price "$price" \
                --arg bp "$blueprint" \
                --argjson level "$level" \
                --argjson cryo "$prevent_cryo" '
            {
                Type: $type,
                Description: $desc,
                Level: $level,
                Price: $price,
                Blueprint: $bp,
                PreventCryo: $cryo
            }'
        )

    else

        local json_items="[]"

        json_items=$(printf '%s
' "${items[@]}" | jq -s .)

        new_item=$(
            jq -n \
                --arg desc "$description" \
                --arg type "$type" \
                --argjson price "$price" \
                --argjson items "$json_items" '
            {
                Type: $type,
                Description: $desc,
                Price: $price,
                Items: $items
            }'
        )

    fi

    tmp=$(mktemp)

    jq \
        --arg id "$id" \
        --argjson item "$new_item" \
        '.ShopItems[$id] = $item' \
        "$SHOP_FILE" >"$tmp"

    mv "$tmp" "$SHOP_FILE"
}

shop_add_item() {

    local id="$1"
    local item="$2"

    local tmp
    tmp=$(mktemp)

    jq \
        --arg id "$id" \
        --argjson item "$item" \
        '
        .ShopItems[$id].Items += [$item]
        ' \
        "$SHOP_FILE" >"$tmp" || {
        rm -f "$tmp"
        return 1
    }

    mv "$tmp" "$SHOP_FILE"
}

####################################
# COMMANDS
####################################

usage() {
    echo
    echo -e "${CLR_TITLE}${BOLD}${VERSION}${RESET}"
    echo
    echo -e "${BOLD}Description:${RESET}"
    echo -e "${CLR_TEXT} For use with the ArkShop plugin - allows one to easily visualize abstracted JSON data"
    echo -e "${CLR_TEXT}  add/remove entries, and roll out changes from a single master config.json to 1 or all servers.${RESET}"
    echo
    echo -e "${BOLD}Usage:${RESET}"
    echo
    echo -e "  ${BOLD}List:${RESET}"
    echo -e "${CLR_TEXT}    arkshop -ls                   - List all available Kits"
    echo -e "${CLR_TEXT}    arkshop -ls <item>            - List all items within a kit${RESET}"
    echo
    echo -e "  ${BOLD}Search:${RESET}"
    echo -e "${CLR_TEXT}    arkshop -s <item>             - Search for a Kit/Item${RESET}"
    echo
    echo -e "  ${BOLD}Add:${RESET}"
    echo -e "${CLR_TEXT}    arkshop -a                    - Add a new Kit and Items within"
    echo -e "    arkshop -a <kit>              - Add a new item to an existing Kit${RESET}"
    echo
    echo -e "  ${BOLD}Delete:${RESET}"
    echo -e "${CLR_TEXT}    arkshop -d <kit>              - Delete an entire Kit"
    echo -e "    arkshop -d <kit> <item-#>     - Delete an item from a kit"
    echo -e "                                  ${RESET}(item-# attainable using ${CLR_BLUE}arkshop -ls)${RESET}"
    echo
    echo -e "  ${BOLD}Logs:${RESET}"
    echo -e "${CLR_TEXT}    arkshop -l <map>              - Display a map's entire ArkShop Log"
    echo -e "    arkshop -f <search>           - Search all maps Arkshop Logs for a search-term"
    echo -e "    arkshop -m                    - List all maps you've defined in this CLI's config${RESET}"
    echo
    echo -e "  ${BOLD}Rollout:${RESET}"
    echo -e "${CLR_TEXT}    arkshop -r                    - Rollout the updated config.json to 1 or ALL servers${RESET}"
    echo
    echo -e "${BOLD}Examples:${RESET}"
    echo -e "${CLR_TEXT}  arkshop -ls"
    echo -e "  arkshop -s shotgun"
    echo -e "  arkshop -d StarterKit 2"
    echo -e "  arkshop -f element${RESET}"
    echo
}

cmd_list() {
    #
    # List the entire shop
    #

    if [[ -z "$2" ]]; then

        echo
        echo -e " ${CLR_FRAME}────────────────────── ${BOLD}Shop Contents${RESET} ──────────────────────────${RESET}"

        #
        # SHOP CONTENTS
        #
        shop_list |
            while IFS=$'\t' read -r id price type description; do
                #echo
                echo -e "  Name:${CLR_ORANGE}   ${BOLD}$id${RESET}"
                echo -e "  Desc:${RESET}   $description"
                echo -e "  ${CLR_GREEN}Price:  ${BOLD}$price${RESET}"
                #echo
                echo -e " ${CLR_FRAME}────────────────────────────────────────────────${RESET}"
            done

        echo
        return

    fi

    #
    # List a Specific Kit
    #

    local ITEM="$2"

    if ! shop_exists "$ITEM"; then
        echo
        echo -e "${CLR_RED}${BOLD}Error:${RESET} Item '$ITEM' not found."
        echo
        return 1
    fi

    local item
    item=$(shop_get "$ITEM")

    #
    # KIT CONTENTS
    #
    echo
    echo -e "${CLR_FRAME}───────────────────── Kit ───────────────────────────${RESET}"
    echo -e "${BOLD} Name:        ${CLR_ORANGE}$ITEM${RESET}"
    echo -e "${BOLD} Description:${BOLD} $(jq -r '.Description' <<<"$item")${RESET}"
    echo -e "${BOLD} Type:${BOLD}        $(jq -r '.Type' <<<"$item")${RESET}"
    echo -e "${BOLD} Price:${BOLD}       ${CLR_GREEN}$(jq -r '.Price' <<<"$item")${RESET}"
    echo
    echo -e "${CLR_FRAME}─────────────────── Contents ────────────────────────${RESET}"

    if jq -e '.Items' >/dev/null <<<"$item"; then

        items=$(jq -c ".ShopItems[\"$ITEM\"].Items[]?" "$SHOP_FILE")

        if [[ -z "$items" ]]; then
            echo -e "     ${CLR_FRAME}${TL}─────────────────────────────${TR}${RESET}"
            echo -e "     ${V}                             ${V}"
            echo -e "     ${V}    No items in this kit.    ${V}"
            echo -e "     ${V}                             ${V}"
            echo -e "     ${CLR_FRAME}${BL}─────────────────────────────${BR}${RESET}"
            echo
            return
        fi

        local item_no=1
        jq -c '.Items[]' <<<"$item" |
            while read -r entry; do
                path=$(jq -r '.Blueprint' <<<"$entry")
                amount=$(jq -r '.Amount' <<<"$entry")
                quality=$(jq -r '.Quality // empty' <<<"$entry")
                damage=$(jq -r '.Damage // empty' <<<"$entry")
                armor=$(jq -r '.Armor // empty' <<<"$entry")
                durability=$(jq -r '.Durability // empty' <<<"$entry")
                blueprint=$(jq -r '.ForceBlueprint' <<<"$entry")

                title=$(
                    basename "${path#*PrimalItem_}" |
                        cut -d. -f1 |
                        sed -E \
                            -e 's/^PrimalItemAmmo_//' \
                            -e 's/^PrimalItemConsumable_//' \
                            -e 's/^PrimalItemResource_//' \
                            -e 's/^PrimalItemStructure_//' \
                            -e 's/^PrimalItemSkin_//' \
                            -e 's/^PrimalItemDye_//' \
                            -e 's/^Weapon//' \
                            -e 's/^Armor//' \
                            -e 's/^Structure//' \
                            -e 's/^Consumable//' \
                            -e 's/^Resource//' \
                            -e 's/^Ammo//' \
                            -e 's/([a-z0-9])([A-Z])/\1 \2/g'
                )

                #START BOX
                echo -e "  ${BOLD}[ITEM ${CLR_GOLD}${item_no}${RESET}${BOLD}] - ${CLR_AQUA}$title${RESET}"

                echo -e "    Amount:      ${BOLD}${CLR_GREEN}$amount${RESET}"

                [[ -n "$quality" ]] && echo -e "    Quality:     ${BOLD}${CLR_BLUE}$quality${RESET}"
                [[ -n "$damage" ]] && echo -e "    Damage:      ${BOLD}${CLR_ORANGE}${damage}%${RESET}"
                [[ -n "$armor" ]] && echo -e "    Armor:       ${BOLD}${CLR_ORANGE}$armor${RESET}"
                [[ -n "$durability" ]] && echo -e "    Durability:  ${BOLD}${CLR_ORANGE}$durability${RESET}"

                if [[ "$blueprint" == "true" ]]; then
                    echo -e "    Blueprint:   ${CLR_GREEN}true${RESET}"
                else
                    echo -e "    Blueprint:   ${CLR_RED}false${RESET}"
                fi

                echo -e " ${BOLD} Path:${RESET}"
                echo -e "  ${CLR_TEXT}$path${RESET}"
                #echo -e " ${V}"
                #END BOX
                echo -e "${CLR_FRAME}─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────${RESET}"
                ((item_no++))
            done

    else

        blueprint=$(jq -r '.Blueprint' <<<"$item")
        level=$(jq -r '.Level // empty' <<<"$item")
        cryo=$(jq -r '.PreventCryo // empty' <<<"$item")

        dino_title=$(sed -E 's|.*/Dinos/([^/]+)/.*|\1|' <<<"$blueprint")

        echo -e "  ${BOLD}[DINO] - ${CLR_AQUA}$dino_title${RESET}"
        [[ -n "$level" ]] && echo -e "    Level:       ${BOLD}${CLR_BLUE}$level${RESET}"
        [[ -n "$cryo" ]] && echo -e "    Cryopod:     ${BOLD}$cryo${RESET}"
        echo -e "    Path:"
        echo -e "  ${CLR_TEXT}${blueprint}${RESET}"
        echo -e "${CLR_FRAME}─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────${RESET}"

    fi

    echo
}

cmd_search() {
    local term="${1,,}"
    echo
    echo -e "  Keyword: ${BOLD}${term}${REEST}"
    echo -e "${CLR_FRAME}──────────────────────── ${RESET}${BOLD}Search Results${RESET}${CLR_FRAME} ───────────────────────${RESET}"
    echo

    jq -r --arg term "$term" '
        .ShopItems
        | to_entries[]
        | select(
            (.key | ascii_downcase | contains($term))
        or
            (.value.Description | ascii_downcase | contains($term))
        or
        any(
            (.value.Items // [])[];
            (.Blueprint | ascii_downcase | contains($term))
        )
        )
        | [
            .key,
            .value.Description,
            .value.Price
          ] | @tsv
    ' "$SHOP_FILE" |
        while IFS=$'\t' read -r id desc price; do

            echo -e "  Name:${CLR_ORANGE}   ${BOLD}$id${RESET}"
            echo -e "  Desc:${RESET}   $desc"
            echo -e "  ${CLR_GREEN}Price:  ${BOLD}$price${RESET}"
            #echo
            echo -e "${CLR_FRAME}───────────────────────────────────────────────${RESET}"
        done

    echo
}

prompt_item() {

    #
    # Blueprint
    #
    while true; do
        read -rp "Blueprint Path: " blueprint

        [[ -n "$blueprint" ]] && break

        error "Blueprint Path cannot be blank."
    done

    #
    # Amount
    #
    while true; do
        read -rp "Amount [1]: " amount
        amount=${amount:-1}

        if is_int "$amount" && ((amount > 0)); then
            break
        fi

        error "Amount must be a whole number greater than zero."
    done

    #
    # Quality
    #
    while true; do
        read -rp "Quality (blank = omit): " quality

        [[ -z "$quality" ]] && break
        is_number "$quality" && break

        error "Quality must be a number."
    done

    #
    # Damage
    #
    while true; do
        read -rp "Damage (blank = omit): " damage

        [[ -z "$damage" ]] && break
        is_number "$damage" && break

        error "Damage must be a number."
    done

    #
    # Armor
    #
    while true; do
        read -rp "Armor (blank = omit): " armor

        [[ -z "$armor" ]] && break
        is_number "$armor" && break

        error "Armor must be a number."
    done

    #
    # Durability
    #
    while true; do
        read -rp "Durability (blank = omit): " durability

        [[ -z "$durability" ]] && break
        is_number "$durability" && break

        error "Durability must be a number."
    done

    #
    # Blueprint?
    #
    read -rp "Give as Blueprint? (y/N): " ans
    [[ "${ans,,}" =~ ^y ]] && forcebp=true || forcebp=false

    echo
    echo "──────────────────────────────"
    echo -e "${BOLD} Blueprint :${RESET} $blueprint"
    echo -e "${BOLD} Amount    :${RESET} $amount"
    [[ -n "$quality" ]] && echo -e "${BOLD} Quality   :${RESET} $quality"
    [[ -n "$damage" ]] && echo -e "${BOLD} Damage    :${RESET} $damage"
    [[ -n "$armor" ]] && echo -e "${BOLD} Armor     :${RESET} $armor"
    [[ -n "$durability" ]] && echo -e "${BOLD} Durability:${RESET} $durability"
    echo -e "${BOLD} Blueprint :${RESET} $forcebp"
    echo "──────────────────────────────"

    item_json=$(
        jq -n \
            --arg bp "$blueprint" \
            --argjson amount "$amount" \
            --argjson force "$forcebp" \
            '{
            Amount: $amount,
            ForceBlueprint: $force,
            Blueprint: $bp
        }'
    )

    if [[ -n "$quality" ]]; then
        item_json=$(jq --argjson q "$quality" '. + {Quality:$q}' <<<"$item_json")
    fi

    if [[ -n "$damage" ]]; then
        item_json=$(jq --argjson d "$damage" '. + {Damage:$d}' <<<"$item_json")
    fi

    if [[ -n "$armor" ]]; then
        item_json=$(jq --argjson a "$armor" '. + {Armor:$a}' <<<"$item_json")
    fi

    if [[ -n "$durability" ]]; then
        item_json=$(jq --argjson d "$durability" '. + {Durability:$d}' <<<"$item_json")
    fi
}

cmd_add() {

    echo
    echo -e "${BOLD}${RESET}"
    echo -e "${BOLD}         Add New Shop Item${RESET}"
    echo

    echo -e "${CLR_FRAME}─────────── ${RESET}${BOLD}Kit Details${RESET}${CLR_FRAME} ───────────${RESET}"
    local id="$2"

    if [[ -z "$id" ]]; then
        read -rp "Name: " id
    fi

    if shop_exists "$id"; then
        type=$(shop_get "$id" "Type")

        if [[ "$type" != "item" ]]; then
            error "'$id' is a dino entry."
            error "Items cannot be added."
            return
        fi

        echo
        echo "Adding item to '$id'"
        echo

        prompt_item

        shop_add_item "$id" "$item_json"

        echo
        echo -e "──────────────────────────────"
        echo -e " ${CLR_GREEN}Added item to '$id'.${RESET}"
        echo -e "──────────────────────────────"
        echo
        return
    fi

    #
    # Description
    #
    while true; do
        read -rp "Description: " description

        [[ -n "$description" ]] && break

        error "Description cannot be blank."
    done

    #
    # Type
    #
    while true; do
        read -rp "Type (item/dino): " type
        type="${type,,}"

        case "$type" in
            item | dino)
                break
                ;;
            *)
                error "Type must be 'item' or 'dino'."
                ;;
        esac
    done

    #
    # Price
    #
    while true; do
        read -rp "Price: " price

        if is_int "$price" && ((price > 0)); then
            break
        fi

        error "Price must be a whole number greater than zero."
    done

    if [[ "$type" == "dino" ]]; then

        #
        # Level
        #
        while true; do
            read -rp "Level: " level

            if is_int "$level" && ((level > 0)); then
                break
            fi

            error "Level must be a whole number greater than zero."
        done

        #
        # Prevent Cryopod
        #
        read -rp "Prevent Cryopod? (y/N): " ans
        [[ "${ans,,}" =~ ^y ]] && prevent_cryo=true || prevent_cryo=false

        #
        # Blueprint
        #
        while true; do
            read -rp "Blueprint: " blueprint

            [[ -n "$blueprint" ]] && break

            error "Blueprint cannot be blank."
        done

    else

        echo
        echo -e "${CLR_FRAME}─────────── ${RESET}${BOLD}Adding Kit Items${RESET}${CLR_FRAME} ───────────${RESET}"
        #echo -e "${BOLD}Adding kit contents...${RESET}"

        items=()

        while true; do

            echo

            prompt_item

            items+=("$item_json")

            echo
            read -rp "Add another item? (y/N): " again

            [[ "${again,,}" =~ ^y ]] || break

        done

    fi

    shop_add "$id"

    echo
    echo -e "──────────────────────────────"
    echo -e "${CLR_GREEN}  Created shop entry '$id'.${RESET}"
    echo -e "──────────────────────────────"

    echo
}

shop_delete() {
    local id="$1"
    local idx="$2"
    local tmp
    tmp=$(mktemp)
    if [[ -z "$idx" ]]; then
        jq --arg id "$id" '.ShopItems |= del(.[$id])' "$SHOP_FILE" >"$tmp"
    else
        jq --arg id "$id" --argjson i "$idx" '.ShopItems[$id].Items |= del(.[ $i ])' "$SHOP_FILE" >"$tmp"
    fi
    mv "$tmp" "$SHOP_FILE"
}

cmd_delete() {
    local id="$2"
    local num="$3"
    [[ -z "$id" ]] && {
        usage
        return 1
    }
    if ! shop_exists "$id"; then
        error "Kit '$id' not found."
        return 1
    fi
    if [[ -z "$num" ]]; then
        echo
        echo -en "  Delete ${CLR_RED}Entire Kit${RESET}: ${BOLD}${CLR_GOLD}$id${RESET}? (y/N) "
        read -r ans
        echo
        [[ ${ans,,} != y ]] && return
        shop_delete "$id"
        echo -e "──────────────────────────────"
        echo -e "${CLR_RED}  Kit Deleted: $id${RESET}"
        echo -e "──────────────────────────────"
        echo
    else
        if ! jq -e --arg id "$id" '.ShopItems[$id].Items' "$SHOP_FILE" >/dev/null; then
            error "'$id' does not contain an Items array."
            return 1
        fi
        count=$(jq --arg id "$id" '.ShopItems[$id].Items|length' "$SHOP_FILE")
        [[ "$num" =~ ^[0-9]+$ ]] || {
            error "Invalid item number."
            return 1
        }
        ((num >= 1 && num <= count)) || {
            error "Item $num does not exist."
            return 1
        }
        echo
        echo -en "  Delete ${CLR_GOLD}Item $num${RESET} from ${CLR_GOLD}$id${RESET}? (y/N) "
        read -r ans
        echo

        [[ ${ans,,} != y ]] && return
        shop_delete "$id" "$((num - 1))"
        echo -e "────────────────────────────────"
        echo -e "${CLR_GREEN} Item $num removed from $id.${RESET}"
        echo -e "────────────────────────────────"
        echo
    fi
}

cmd_rollout() {

    echo
    echo -e "${BOLD}         Rollout Updated Config.json${RESET}"
    echo

    echo -e "${CLR_FRAME}───────────── ${RESET}Select Servers to Rollout Changes to${CLR_FRAME} ─────────────${RESET}"
    echo

    number=1

    for server in "${SERVERS[@]}"; do
        map="${server%%|*}"
        echo "$number) $map"
        ((number++))
    done

    echo
    echo "$number) All Servers"
    echo

    read -rp "Selection: " ans

    if ((ans == number)); then

        for server in "${SERVERS[@]}"; do
            path="${server#*|}"

            cp -f "$SHOP_FILE" \
                "$path/ShooterGame/Binaries/Win64/ArkApi/Plugins/ArkShop/config.json"
        done

        echo
        echo "──────────────────────────────"
        echo -e "${CLR_GREEN}config.json deployed to all servers.${RESET}"
        echo "──────────────────────────────"
        echo
        return
    fi

    if ((ans >= 1 && ans < number)); then

        server="${SERVERS[$((ans - 1))]}"
        map="${server%%|*}"
        path="${server#*|}"

        cp -f "$SHOP_FILE" \
            "$path/ShooterGame/Binaries/Win64/ArkApi/Plugins/ArkShop/config.json"

        echo
        echo "──────────────────────────────"
        echo -e "${CLR_GREEN}$map config.json updated.${RESET}"
        echo "──────────────────────────────"
        echo
        return
    fi

    echo -e "${CLR_RED}Invalid selection.${RESET}"
}

#
# Shop Logs
#
cmd_shop_logs() {

    if [[ -z "$1" ]]; then
        echo
        echo -e "${CLR_RED}Error:${RESET} No Map Specified!"
        echo "Usage: arkshop -l <map>"
        echo
        exit 1
    fi

    if [[ ! "${SERVERS[@],,}" =~ "${1,,}" ]]; then
        echo
        echo -e "${CLR_RED}Error:${RESET} ${1} Not found"
        echo " Use 'arkshop -m' to see which map you have configured in this CLI"
        echo
        exit 1
    fi

    echo
    echo -e " ▶ ${CLR_GOLD}Displaying ArkShop logs for $1 ${RESET}"
    echo
    sleep 2

    for server in "${SERVERS[@]}"; do
        server_name="${server%%|*}" # pass array content to server_name so we can convert it to lowercase
        if [[ "${1,,}" == "${server_name,,}" ]]; then
            path="${server#*|}"
            cat "${path}"/ShooterGame/Binaries/Win64/ArkApi/Plugins/ArkShop/*.log | colour_shop_logs
        fi
    done

    # ALL shop logs at once (too much)
    #for server in "${SERVERS[@]}"; do
    #   path="${server#*|}"
    #  cat "${path}"/ShooterGame/Binaries/Win64/ArkApi/Plugins/ArkShop/*.log | colour_shop_logs
    #done
}

cmd_shop_find() {

    if [[ -z "$1" ]]; then
        echo
        echo -e "${CLR_RED}Error:${RESET} No Search-term Specified!"
        echo "Usage: arkshop -f <Search>"
        echo
        exit 1
    fi

    echo
    echo -e " ▶ ${CLR_GOLD}Searching existing ArkShop logs for:${CLR_AQUA} '${1}' ${RESET}"
    echo
    sleep 1

    for server in "${SERVERS[@]}"; do
        server_path="${server#*|}"
        log_path="${server_path}/ShooterGame/Binaries/Win64/ArkApi/Plugins/ArkShop"
        grep -H -i --color=never "$1" "${log_path}"/*.log | colour_shop_logs
    done
}

colour_shop_logs() {
    while IFS= read -r line; do
        line=$(sed -E 's|.*/ShopLog_([^/]+)_WP\.log:|\1: |' <<<"$line")
        echo "$line"
    done
}

cmd_list_maps() {
    echo
    echo -e "${CLR_PURPLE}Servers you've defined in this CLI's Config:${RESET}"
    echo "───────────────────────────────────────────"
    for server in "${SERVERS[@]}"; do
        map="${server%%|*}"
        echo " - $map"
    done
}

####################################
# MAIN
####################################

case "${1:-}" in
    -ls)
        cmd_list "$@"
        ;;
    -s | --search)
        cmd_search "$2"
        ;;
    -a | --add)
        cmd_add "$@"
        ;;
    -d | --delete)
        cmd_delete "$@"
        ;;
    -r | --rollout)
        cmd_rollout "$@"
        ;;
    -l | --log)
        shift
        cmd_shop_logs "$@"
        ;;
    -f | --find)
        shift
        cmd_shop_find "$@"
        ;;
    -m | --maps)
        cmd_list_maps
        ;;
    *)
        usage
        ;;
esac