Automated Raffle CLI


Editor’s Note:
For our cluster, I simply setup a Scheduled Task to run every 90 minutes, which executed the ‘raffle’ command within a git-bash environment, running a fully automated periodic raffle.

Optional Setup: Excluded Players
Should you wish to exclude specific players, such as admins, you can enter their EOSID’s in the config under the excluded players heading, so they are excluded from winning the raffle

Dependencies:

Extended Rcon – Because Wildcard’s implementation of giving items via console/rcon is COMPLETELY broken, the Extended Rcon plugin is a requirement so the raffle script can utilize it’s “GiveItemToEOSId” Rcon command.
(newer version available via the developer’s discord)

mcrcon – or whichever Rcon agent you prefer



Description:
CLI for running and maintaining raffles within an Ark Ascended Cluster.
 Raffle will first check for 5 or more players, if met, a raffle is run
 Flags to force a raffle regardless of online players are available

Options:
 raffle         ────  Run a regular raffle (Required: 5 Players)  
 raffle -f      ────  Force a raffle, regardless of how many online players  

 raffle -l      ────  View the last 150 lines of the Raffle log  
 raffle -w      ────  View the last 100 lines of the Winners Log  

 raffle -v      ────  Check Raffle CLI Version

Examples:
 raffle
raffle -f

Download raffle.sh

#!/usr/bin/env bash
#
#

set -euo pipefail

#######################
# RAFFLE CONFIGURATIONS
#######################
serverIP="YOUR-SERVER-IP"
mcrcon="/c/Ark/mcrcon/mcrcon.exe"
rconpass="YOUR-RCON-PASS"

# Minimum numbers of players required for a raffle to be drawn
min_players="5"

logFile="/c/Ark/Logs/raffle-log.txt"
winnerLog="/c/Ark/Logs/raffle-winners.txt"

#################################
# SERVER ARRAYS
#################################
SERVERS=(
    Island
    Ragnarok
    Astraeos
    Extinction
    Aberration
    Genesis1
)

declare -A SERVER_PORT=(
    [Island]=1234
    [Ragnarok]=1234
    [Astraeos]=1234
    [Extinction]=1234
    [Aberration]=1234
    [Genesis1]=1234
)

declare -A SERVER_PASS=(
    [Island]="$rconpass"
    [Ragnarok]="$rconpass"
    [Astraeos]="$rconpass"
    [Extinction]="$rconpass"
    [Aberration]="$rconpass"
    [Genesis1]="$rconpass"
)

#################################
# EXCLUDED PLAYERS
#################################
declare -A EXCLUDED=(
    # APlayerName
    [00012341234123412341234123412341]=1    # < Excluded Player's EOSID

    # APlayerName
    [00012341234123412341234123412341]=1    # < Excluded Player's EOSID
)

#
# END CONFIGURATION
#

#######################
# LOGGING
#######################
mkdir -p \
    "$(dirname "$logFile")" \
    "$(dirname "$winnerLog")"

#######################
# GLOBAL VARIABLES
#######################
OnlinePlayerCount=0
version="3.4.8"

####################
# 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;255m"         # Light Grey
CLR_FRAME="${BOLD}\033[38;5;250m" # Darker Grey
CLR_TITLE="\033[38;5;147m"        # Light Grey (optional)

#################################
# PRIZE PACK FUNCTIONS
#################################

PrizePacks=(
    Prize_CryoPack_Large

    Prize_CryoPack_Small
    Prize_CryoPack_Small

    Prize_Elec250
    Prize_Element

    Prize_ElementDust
    Prize_ElementDust
    Prize_ElementDust

    Prize_ElementShards
    Prize_ElementShards
    Prize_ElementShards

    Prize_Polymer500
    Prize_ResourcePack
    Prize_ShotgunPack
    Prize_ChainsawPack
    Prize_Shit_Large
    Prize_indforge
    Prize_Cons_Mindwipe
    Prize_Cons_Cake
    Prize_Cons_Narcotic
    Prize_Cons_Honey
    Prize_Cons_PrimeJerky
    Prize_Cons_PrimeFish
    Prize_Points
    Prize_Points
    Prize_ConsumablesPack
    Prize_AdvancedRifleAmmo

    Prize_SniperAmmo
    Prize_SniperAmmo

    Prize_ShotgunShells
    Prize_ShotgunShells

    Prize_LazarusChowders
    Prize_FriaCurry
    Prize_MedicalBrews
    Prize_CookedMutton
    Prize_ExtraordinaryKibble
    Prize_ExceptionalKibble
    Prize_Sap
    Prize_RareFlowers
    Prize_Armour2pc

    Prize_RandomSaddle
    Prize_RandomSaddle

    Prize_RandomChibi
    Prize_RandomChibi
    Prize_RandomChibi
)

Armour_Flak=(
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Metal/PrimalItemArmor_MetalHelmet.PrimalItemArmor_MetalHelmet"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Metal/PrimalItemArmor_MetalShirt.PrimalItemArmor_MetalShirt"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Metal/PrimalItemArmor_MetalGloves.PrimalItemArmor_MetalGloves"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Metal/PrimalItemArmor_MetalPants.PrimalItemArmor_MetalPants"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Metal/PrimalItemArmor_MetalBoots.PrimalItemArmor_MetalBoots"
)

Armour_Ghillie=(
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Ghillie/PrimalItemArmor_GhillieHelmet.PrimalItemArmor_GhillieHelmet"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Ghillie/PrimalItemArmor_GhillieShirt.PrimalItemArmor_GhillieShirt"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Ghillie/PrimalItemArmor_GhillieGloves.PrimalItemArmor_GhillieGloves"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Ghillie/PrimalItemArmor_GhilliePants.PrimalItemArmor_GhilliePants"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Ghillie/PrimalItemArmor_GhillieBoots.PrimalItemArmor_GhillieBoots"
)

Armour_Hazmat=(
    "/Game/Aberration/CoreBlueprints/Items/Armor/HazardSuit/PrimalItemArmor_HazardSuitHelmet.PrimalItemArmor_HazardSuitHelmet"
    "/Game/Aberration/CoreBlueprints/Items/Armor/HazardSuit/PrimalItemArmor_HazardSuitShirt.PrimalItemArmor_HazardSuitShirt"
    "/Game/Aberration/CoreBlueprints/Items/Armor/HazardSuit/PrimalItemArmor_HazardSuitGloves.PrimalItemArmor_HazardSuitGloves"
    "/Game/Aberration/CoreBlueprints/Items/Armor/HazardSuit/PrimalItemArmor_HazardSuitPants.PrimalItemArmor_HazardSuitPants"
    "/Game/Aberration/CoreBlueprints/Items/Armor/HazardSuit/PrimalItemArmor_HazardSuitBoots.PrimalItemArmor_HazardSuitBoots"
)

Saddles=(
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_ArgentavisSaddle.PrimalItemArmor_ArgentavisSaddle"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_RexSaddle.PrimalItemArmor_RexSaddle"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_YutySaddle.PrimalItemArmor_YutySaddle"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_TherizinosaurusSaddle.PrimalItemArmor_TherizinosaurusSaddle"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_MaewingSaddle.PrimalItemArmor_MaewingSaddle"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_DoedSaddle.PrimalItemArmor_DoedSaddle"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_AnkyloSaddle.PrimalItemArmor_AnkyloSaddle"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_PteroSaddle.PrimalItemArmor_PteroSaddle"
)

Chibis=(
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_SnowOwl.PrimalItemSkin_ChibiDino_SnowOwl"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_TophatKairuku.PrimalItemSkin_ChibiDino_TophatKairuku"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Raptor_Bone.PrimalItemSkin_ChibiDino_Raptor_Bone"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Trike.PrimalItemSkin_ChibiDino_Trike"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Raptor.PrimalItemSkin_ChibiDino_Raptor"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Pulmonoscorpius.PrimalItemSkin_ChibiDino_Pulmonoscorpius"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Pteranodon.PrimalItemSkin_ChibiDino_Pteranodon"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Phiomia.PrimalItemSkin_ChibiDino_Phiomia"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Sheep.PrimalItemSkin_ChibiDino_Sheep"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Otter.PrimalItemSkin_ChibiDino_Otter"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Mantis.PrimalItemSkin_ChibiDino_Mantis"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Lymantria.PrimalItemSkin_ChibiDino_Lymantria"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Mesopithecus.PrimalItemSkin_ChibiDino_Mesopithecus"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Dodo.PrimalItemSkin_ChibiDino_Dodo"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Anglerfish.PrimalItemSkin_ChibiDino_Anglerfish"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Ammonite.PrimalItemSkin_ChibiDino_Ammonite"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Achatina.PrimalItemSkin_ChibiDino_Achatina"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Reindeer.PrimalItemSkin_ChibiDino_Reindeer"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_WoollyRhino.PrimalItemSkin_ChibiDino_WoollyRhino"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_RockGolem.PrimalItemSkin_ChibiDino_RockGolem"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Saber.PrimalItemSkin_ChibiDino_Saber"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Sarco.PrimalItemSkin_ChibiDino_Sarco"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Stego.PrimalItemSkin_ChibiDino_Stego"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Tapejara.PrimalItemSkin_ChibiDino_Tapejara"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Megalodon.PrimalItemSkin_ChibiDino_Megalodon"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Megatherium.PrimalItemSkin_ChibiDino_Megatherium"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Onyc.PrimalItemSkin_ChibiDino_Onyc"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Paraceratherium.PrimalItemSkin_ChibiDino_Paraceratherium"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Procoptodon.PrimalItemSkin_ChibiDino_Procoptodon"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Ankylosaurus.PrimalItemSkin_ChibiDino_Ankylosaurus"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Araneo.PrimalItemSkin_ChibiDino_Araneo"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Argent.PrimalItemSkin_ChibiDino_Argent"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Baryonyx.PrimalItemSkin_ChibiDino_Baryonyx"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Basilisk.PrimalItemSkin_ChibiDino_Basilisk"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Bronto.PrimalItemSkin_ChibiDino_Bronto"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Carnotaurus.PrimalItemSkin_ChibiDino_Carnotaurus"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Castroides.PrimalItemSkin_ChibiDino_Castroides"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Daeodon.PrimalItemSkin_ChibiDino_Daeodon"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Diplodocus.PrimalItemSkin_ChibiDino_Diplodocus"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Direbear.PrimalItemSkin_ChibiDino_Direbear"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Direwolf.PrimalItemSkin_ChibiDino_Direwolf"
    "/Game/PrimalEarth/CoreBlueprints/Items/Armor/Skin/ChibiDinos/PrimalItemSkin_ChibiDino_Doedicurus.PrimalItemSkin_ChibiDino_Doedicurus"
)

Prize_RandomChibi() {
    PrizeName="A Random Chibi Selection!"
    PrizeCommands=()

    local Chibi

    Chibi="${Chibis[RANDOM % ${#Chibis[@]}]}"

    PrizeCommands+=(
        "GiveItemToEOSID {PLAYER} '$Chibi' 1 0 0"
    )
}

Prize_RandomSaddle() {
    PrizeName="A Random Saddle Selection!"
    PrizeCommands=()

    local Saddle
    local Quality

    Saddle="${Saddles[RANDOM % ${#Saddles[@]}]}"

    # 66.7% quality 5, 22.2% quality 10, 11.1% quality 20
    local Qualities=(5 5 5 5 5 5 10 10 20)
    Quality="${Qualities[RANDOM % ${#Qualities[@]}]}"

    PrizeCommands+=(
        "GiveItemToEOSID {PLAYER} '$Saddle' 1 $Quality 0"
    )
}

Prize_Armour2pc() {
    PrizeName="2 Piece Armour Pack"
    PrizeCommands=()

    local PieceCount=2

    local ArmourTypes=(Flak Ghillie Hazmat)

    SelectedType=${ArmourTypes[RANDOM % ${#ArmourTypes[@]}]}
    local -n Set="Armour_${SelectedType}"

    mapfile -t Picks < <(
        printf '%s\n' "${Set[@]}" |
            shuf |
            head -"${PieceCount}"
    )

    echo "Selected armour type: $SelectedType"

    local Qualities=(5 5 5 5 5 5 10 10 20)
    local Quality="${Qualities[RANDOM % ${#Qualities[@]}]}"

    for item in "${Picks[@]}"; do
        echo "  $item"
    done

    for item in "${Picks[@]}"; do
        PrizeCommands+=(
            "GiveItemToEOSID {PLAYER} '$item' 1 $Quality 0"
        )
    done
}

Prize_ResourcePack() {
    PrizeName="Resource Prize Pack"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Element.PrimalItemResource_Element' 25 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Gunpowder.PrimalItemResource_Gunpowder' 500 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Polymer.PrimalItemResource_Polymer' 500 0 0"
        #"GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_BlackPearl.PrimalItemResource_BlackPearl' 100 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Electronics.PrimalItemResource_Electronics' 150 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_ChitinPaste.PrimalItemResource_ChitinPaste' 500 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Oil.PrimalItemResource_Oil' 500 0 0"
    )
}

Prize_Element() {
    PrizeName="25 Element"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Element.PrimalItemResource_Element' 25 0 0"
    )
}

Prize_ElementDust() {
    PrizeName="5000 Element Dust"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/Extinction/CoreBlueprints/Resources/PrimalItemResource_ElementDust.PrimalItemResource_ElementDust' 5000 0 0"
    )
}

Prize_ElementShards() {
    PrizeName="500 Element Shards"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_ElementShard.PrimalItemResource_ElementShard' 500 0 0"

    )
}

Prize_MetalArmorset() {
    PrizeName="[Ascendant] Complete Flak Armor Set"
    PrizeCommands=(
        "GiveArmorSet {PLAYER} Metal Asc"
    )
}

Prize_ShotgunPack() {
    PrizeName="[Ascendant] Shotgun Prize Pack"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Weapons/PrimalItem_WeaponMachinedShotgun.PrimalItem_WeaponMachinedShotgun' 1 10 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Weapons/PrimalItemAmmo_SimpleShotgunBullet.PrimalItemAmmo_SimpleShotgunBullet' 10 0 0"
    )
}

Prize_ChainsawPack() {
    PrizeName="[Ascendant] Chainsaw Prize Pack"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/ScorchedEarth/WeaponChainsaw/PrimalItem_ChainSaw.PrimalItem_ChainSaw' 1 10 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Gasoline.PrimalItemResource_Gasoline' 100 0 0"
    )
}

Prize_CryoPack_Small() {
    PrizeName="5 Cryopods"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/Extinction/CoreBlueprints/Weapons/PrimalItem_WeaponEmptyCryopod.PrimalItem_WeaponEmptyCryopod' 5 0 0"
    )
}

Prize_CryoPack_Large() {
    PrizeName="Cryogenic Prize Pack"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/Extinction/CoreBlueprints/Weapons/PrimalItem_WeaponEmptyCryopod.PrimalItem_WeaponEmptyCryopod' 10 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/Extinction/CoreBlueprints/Items/PrimalItemStructure_CryoFridge.PrimalItemStructure_CryoFridge' 1 0 0"
    )
}

Prize_Polymer500() {
    PrizeName="500 Polymer"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Polymer.PrimalItemResource_Polymer' 500 0 0"
    )
}

Prize_Elec250() {
    PrizeName="150 Electronics"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Electronics.PrimalItemResource_Electronics' 150 0 0"
    )
}

Prize_Shit_Large() {
    PrizeName="200 Individual Large Dino Shits"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_DinoPoopLarge.PrimalItemConsumable_DinoPoopLarge' 200 0 0"
    )
}

Prize_indforge() {
    PrizeName="An Industrial Forge"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Structures/Misc/PrimalItemStructure_IndustrialForge.PrimalItemStructure_IndustrialForge' 1 0 0"
    )
}

Prize_Cons_Mindwipe() {
    PrizeName="A Forgetty potion"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/BaseBPs/PrimalItemConsumableRespecSoup.PrimalItemConsumableRespecSoup' 1 0 0"
    )
}

Prize_Cons_Cake() {
    PrizeName="10 Sweet Vegetable Cakes"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_SweetVeggieCake.PrimalItemConsumable_SweetVeggieCake' 10 0 0"
    )
}

Prize_Cons_Narcotic() {
    PrizeName="250 Narcotic"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Narcotic.PrimalItemConsumable_Narcotic' 250 0 0"
    )
}

Prize_Cons_Honey() {
    PrizeName="100 Honey"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Honey.PrimalItemConsumable_Honey' 100 0 0"
    )
}

Prize_Cons_PrimeJerky() {
    PrizeName="20 Prime Meat Jerky"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_CookedPrimeMeat_Jerky.PrimalItemConsumable_CookedPrimeMeat_Jerky' 20 0 0"
    )
}

Prize_Cons_PrimeFish() {
    PrizeName="25 Prime Fish Meat"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_RawPrimeMeat_Fish.PrimalItemConsumable_RawPrimeMeat_Fish' 25 0 0"
    )
}

Prize_Points() {
    PrizeName="5 Shop Points!"
    PrizeCommands=(
        "AddPoints {PLAYER} 5"
    )
}

Prize_ConsumablesPack() {
    PrizeName="Consumables Pack - 5 of Each Soup"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Soup_CalienSoup.PrimalItemConsumable_Soup_CalienSoup' 5 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Soup_FriaCurry.PrimalItemConsumable_Soup_FriaCurry' 5 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Soup_FocalChili.PrimalItemConsumable_Soup_FocalChili' 5 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Soup_EnduroStew.PrimalItemConsumable_Soup_EnduroStew' 5 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Soup_LazarusChowder.PrimalItemConsumable_Soup_LazarusChowder' 5 0 0"
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Soup_ShadowSteak.PrimalItemConsumable_Soup_ShadowSteak' 5 0 0"
    )
}
Prize_Sap() {
    PrizeName="100 Sap"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Sap.PrimalItemResource_Sap' 100 0 0"
    )
}

Prize_RareFlowers() {
    PrizeName="250 Rare Flowers"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_RareFlower.PrimalItemResource_RareFlower' 250 0 0"
    )
}

Prize_AdvancedRifleAmmo() {
    PrizeName="150 Advanced Rifle Bullets"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Weapons/PrimalItemAmmo_AdvancedRifleBullet.PrimalItemAmmo_AdvancedRifleBullet' 150 0 0"
    )
}

Prize_SniperAmmo() {
    PrizeName="100 Sniper Bullets"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Weapons/PrimalItemAmmo_SimpleRifleBullet.PrimalItemAmmo_SimpleRifleBullet' 100 0 0"
    )
}

Prize_ShotgunShells() {
    PrizeName="100 Shotgun Shells"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Weapons/PrimalItemAmmo_ShotgunShell.PrimalItemAmmo_ShotgunShell' 100 0 0"
    )
}

Prize_MedicalBrews() {
    PrizeName="50 Medical Brews"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_HealSoup.PrimalItemConsumable_HealSoup' 50 0 0"
    )
}

Prize_LazarusChowders() {
    PrizeName="5 Lazarus Chowders"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Soup_LazarusChowder.PrimalItemConsumable_Soup_LazarusChowder' 5 0 0"
    )
}

Prize_FriaCurry() {
    PrizeName="5 Fria Curry"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Soup_FriaCurry.PrimalItemConsumable_Soup_FriaCurry' 5 0 0"
    )
}

Prize_CookedMutton() {
    PrizeName="500 Cooked Lamb Chops"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_CookedLambChop.PrimalItemConsumable_CookedLambChop' 500 0 0"
    )
}

Prize_ExtraordinaryKibble() {
    PrizeName="10 Extraordinary Kibble"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Kibble_Base_Special.PrimalItemConsumable_Kibble_Base_Special' 10 0 0"
    )
}

Prize_ExceptionalKibble() {
    PrizeName="10 Exceptional Kibble"
    PrizeCommands=(
        "GiveItemToEOSID {PLAYER} '/Game/PrimalEarth/CoreBlueprints/Items/Consumables/PrimalItemConsumable_Kibble_Base_XLarge.PrimalItemConsumable_Kibble_Base_XLarge' 10 0 0"
    )
}

#######################
# FUNCTIONS
#######################
timestamp() {
    local message="$1"
    local colour="${2:-}"
    local line

    line="$(date '+%F %T') - $message"
    echo -e "${colour}${line}${RESET}" >&2
}

send_rcon() {
    local server=$1
    local command=$2
    local result

    result=$(
        "$mcrcon" \
            -H "$serverIP" \
            -P "${SERVER_PORT[$server]}" \
            -p "${SERVER_PASS[$server]}" \
            "$command" 2>&1 |
            sed -E 's/\x1B\[[0-9;]*[[:alpha:]]//g'
    )

    #timestamp "[$server] $command -> $result"
    printf '%s\n' "$result"
}

check_player_num() {
    OnlinePlayerCount=0
    echo
    echo -e "${CLR_GOLD}  ▶ Checking total connected players${RESET}"
    echo

    for servername in "${!SERVER_PORT[@]}"; do
        # Calculate total online on cluster
        onlineNum=$(send_rcon "$servername" getonlinenum)
        OnlinePlayerCount=$((OnlinePlayerCount + onlineNum))
        echo "${servername}: $onlineNum Players"
    done
    echo
    echo -e "  ▶ Total Players Online: ${CLR_GOLD}${OnlinePlayerCount}${RESET}"
    echo
}

broadcast_all() {
    local message=$1

    for server in "${ACTIVE_SERVERS[@]}"; do
        send_rcon "$server" "Broadcast $message" >/dev/null
    done
}

chat_all() {
    local message=$1

    for server in "${ACTIVE_SERVERS[@]}"; do
        send_rcon "$server" "ServerChat $message" >/dev/null
    done
}

get_active_servers() {
    ACTIVE_SERVERS=()

    for server in "${SERVERS[@]}"; do
        local result
        result=$(send_rcon "$server" "ListPlayers")
        [[ $result == *"No Players Connected"* ]] && continue
        ACTIVE_SERVERS+=("$server")
    done
}

get_online_players() {
    PLAYERS=()

    declare -gA PLAYER_NAME=()
    declare -gA PLAYER_SERVER=()

    local -A seen=()

    for server in "${SERVERS[@]}"; do
        timestamp "Querying players on $server"

        local result
        result=$(send_rcon "$server" "ListPlayers")

        [[ $result == *"No Players Connected"* ]] && continue

        while IFS= read -r line; do
            # Remove ANSI escape sequences
            line=$(sed -E 's/\x1B\[[0-9;]*[[:alpha:]]//g' <<<"$line")

            # Trim trailing whitespace
            line="${line%"${line##*[![:space:]]}"}"

            [[ $line =~ ^[0-9]+\.[[:space:]]*([^,]+),[[:space:]]*([a-fA-F0-9]{32})$ ]] || continue

            local name="${BASH_REMATCH[1]}"
            local id="${BASH_REMATCH[2]}"

            name="${name#"${name%%[![:space:]]*}"}"
            name="${name%"${name##*[![:space:]]}"}"

            [[ ${EXCLUDED[$id]+x} ]] && {
                timestamp "Excluded player: $name [$id]" "$CLR_ORANGE"
                continue
            }

            [[ ${seen[$id]+x} ]] && continue

            seen[$id]=1

            PLAYERS+=("$id")
            PLAYER_NAME[$id]="$name"
            PLAYER_SERVER[$id]="$server"

        done <<<"$result"
    done
}

get_prize() {
    selected=$((RANDOM % ${#PrizePacks[@]}))
    "${PrizePacks[$selected]}"
}

award_prize() {
    local server="$1"
    local player="$2"
    local command

    prizeLog=""

    for command in "${PrizeCommands[@]}"; do
        command="${command//\{PLAYER\}/$player}"

        send_rcon "$server" "$command" >/dev/null

        prizeLog+="  $command"$'\n'
    done
}


colour_logs() {
    shopt -s nocasematch

    while IFS= read -r line; do
    line=$(sed -E 's|.*/ShopLog_([^/]+)_WP\.log:|\1: |' <<<"$line")
        case $line in
    # Raffle Logs
    *RAFFLE\ INITIATED*)    echo -e "${CLR_GOLD}$line${RESET}";;
    *Active\ Servers*)    echo -e "${CLR_AQUA}$line${RESET}";;
    *Excluded\ player*)    echo -e "${BOLD}${CLR_ORANGE}$line${RESET}";;
    *Querying\ players*)    echo -e "${CLR_AQUA}$line${RESET}";;
    *Entry:*)               echo -e "${CLR_AQUA}$line${RESET}";;
    *▶\ Winner*)               echo -e "${CLR_GREEN}$line${RESET}";;
    *▶*)               echo -e "${CLR_PURPLE}$line${RESET}";;
    *Eligible\ Players*)    echo -e "${BOLD}${CLR_AQUA}$line${RESET}";;
    *\ \-\ WINNER:*)    echo -e "${BOLD}${CLR_GREEN}$line${RESET}";;
    *sent\ to*)    echo -e "${BOLD}${CLR_GREEN}$line${RESET}";;
    *RAFFLE\ COMPLETE*)    echo -e "${CLR_GOLD}$line${RESET}";;

            *)          echo "$line" ;;
        esac
    done

    shopt -u nocasematch
}

#######################
# COMMAND FUNCTIONS
#######################

cmd_version() {
    echo -e "${CLR_TITLE}${BOLD}Scruffy Cluster Raffle v${version}${RESET}"
    exit 0
}

usage() {
    echo
    echo -e "${CLR_TITLE}${BOLD}Scruffy Cluster Raffle v${version}${RESET}"
    echo
    echo -e "${BOLD}Description:${RESET}${CLR_TEXT}"
    echo -e "  CLI for running and maintaining raffles within the Scruffy Cluster"
    echo -e "  Raffle will first check for 5 or more players, if met, a raffle is run"
    echo -e "  Flags to force a raffle regardless are available."
    echo
    echo -e "${RESET}${BOLD}Usage:${RESET}${CLR_TEXT}"
    echo -e "  raffle         ────  Run a regular raffle (Required: 5 Players) "
    echo -e "  raffle -f      ────  Force a raffle, regardless of how many online players "
    echo
    echo -e "  raffle -l      ────  View the last 150 lines of the Raffle log "
    echo -e "  raffle -w      ────  View the last 100 lines of the Winners Log "
    echo -e "  raffle -lw     ────  View the last entry in Winners Log "
    echo
    echo -e "  raffle -v      ────  Check Raffle CLI Version "
    echo

    exit 1
}

#
# Raffle logs
#
cmd_log() {
    echo
    echo "Loading Live Raffle Log..."
    echo -e "${CLR_AQUA}${BOLD}  CTRL + C to Exit${RESET}"
    sleep 1
    tail -n 75 -F "$logFile" | colour_logs
}

cmd_winners() {
    echo
    echo "Loading Raffle Winners Log..."
    sleep 1
    tail -n 72 "$winnerLog" | colour_logs
}

cmd_last_winner() {

    echo
    echo -e "${CLR_GOLD}Last raffle winner${RESET}"
    echo "───────────────────"
    tail -n 12  "$winnerLog" | colour_logs
    echo
}

####################
# MAIN CODE
###################
cmd_raffle() {

local mode="${1:-}"

# Log raffle output only
    exec > >(tee >(sed -E 's/\x1B\[[0-9;]*[[:alpha:]]//g' >> "$logFile")) 2>&1

    timestamp ""

    timestamp "───── RAFFLE INITIATED ─────"

    if [[ "$mode" != "-f" ]]; then
        check_player_num

        if [[ $OnlinePlayerCount -lt "${min_players}" ]]; then
            timestamp "Less than ${min_players} players online"
            timestamp "───── RAFFLE CANCELLED ─────" "$CLR_RED"
            exit 0
        fi
    fi

    get_active_servers

    if ((${#ACTIVE_SERVERS[@]} == 0)); then
        timestamp "No active servers online."
        timestamp "───── RAFFLE CANCELLED ─────"
        exit 0
    fi

    timestamp "Active Servers: $(
        IFS=', '
        echo "${ACTIVE_SERVERS[*]}"
    )"

    broadcast_all "SCRUFFY CLUSTER RAFFLE! - A winner will be drawn in 30 seconds!"
    timestamp "Broadcasting Raffle Draw in 30 Seconds -> Active Servers"

    timestamp "Waiting 30 seconds before draw..."
    sleep 20

    broadcast_all "10 seconds until the raffle draw!"
    timestamp "Broadcasting Raffle Draw in 10 Seconds -> Active Servers"
    sleep 7

    chat_all "Drawing raffle..."
    timestamp "Drawing raffle..."

    chat_all "3..."
    timestamp "3..."
    sleep 1

    chat_all "2..."
    timestamp "2..."
    sleep 1

    chat_all "1..."
    timestamp "1..."
    sleep 1

    chat_all "Drawing winner..."
    timestamp "Drawing winner..."

    get_online_players

    timestamp ""
    timestamp "Eligible Players: ${#PLAYERS[@]}" "$CLR_GREEN"

    if ((${#PLAYERS[@]} == 0)); then
        timestamp "No eligible players found."
        chat_all "No eligible players were online for the raffle."
        timestamp "───── RAFFLE CANCELLED ─────"
        exit 0
    fi

    for id in "${PLAYERS[@]}"; do
        timestamp "Entry: ${PLAYER_NAME[$id]} [$id] on ${PLAYER_SERVER[$id]}"
    done

    mapfile -t PLAYERS < <(printf '%s\n' "${PLAYERS[@]}" | shuf)

    echo
    echo -e "${CLR_PURPLE}  ▶ PLAYER COUNT: ${CLR_GOLD}${#PLAYERS[@]}${RESET}"
    echo

    echo -e "${CLR_PURPLE}Shuffled player list:${RESET}"
    for i in "${!PLAYERS[@]}"; do
        playerID="${PLAYERS[$i]}"
        echo "$i : ${PLAYER_NAME[$playerID]}"
    done

    echo

    index=$((RANDOM % ${#PLAYERS[@]}))
    echo -e "${CLR_PURPLE}  ▶ Random index chosen: ${CLR_GREEN}${index}${RESET}"

    winnerID="${PLAYERS[$index]}"

    echo
    echo "WinnerID: $winnerID"
    echo -e "  ▶ Winner: ${CLR_GREEN}${PLAYER_NAME[$winnerID]}${RESET}"
    echo
    winnerName="${PLAYER_NAME[$winnerID]}"
    winnerServer="${PLAYER_SERVER[$winnerID]}"

    [[ -z $winnerName ]] && {
        echo "Winner selection failed." >&2
        exit 1
    }

    timestamp ""
    timestamp "WINNER: $winnerName [$winnerID]" "$CLR_GREEN"
    timestamp ""

    get_prize

    broadcast_all "RAFFLE WINNER: $winnerName!"
    chat_all "RAFFLE WINNER: $winnerName!"

    timestamp "Waiting 10 seconds to congratulate..."
    sleep 10

    broadcast_all "Congratulations $winnerName! - You have won: $PrizeName"
    chat_all "Congratulations $winnerName! You have won: $PrizeName"
    timestamp "Congratulations $winnerName! You have won: $PrizeName" "$CLR_GOLD"

    award_prize "$winnerServer" "$winnerID" "$CLR_GOLD"
    timestamp "Prize $PrizeName sent to $winnerName" "$CLR_GREEN"

    # Log all raffle details
    winnerRecord=$(
        cat <<EOF

────────────────────────────────────────────────────────────
Date      : $(date '+%Y-%m-%d %H:%M:%S')
Winner    : $winnerName
Player ID : $winnerID
Map       : $winnerServer

Prize     : $PrizeName
Prize(s):
$prizeLog
───────────────────────────────────────────────────────

EOF
    )
    printf '%s\n' "$winnerRecord" >>"$winnerLog"

    timestamp "───── RAFFLE COMPLETE ─────"
}

####################
# COMMANDS
####################
case "${1:-}" in
    "")
        cmd_raffle
        ;;
    -f)
        cmd_raffle -f
        ;;
    -v)
        cmd_version
        ;;
    -w)
        cmd_winners
        ;;
    -lw)
        cmd_last_winner
        ;;
    -l)
        cmd_log
        ;;
    -h)
        usage
        ;;
    *)
        usage
        ;;
esac