{
    "_comment": "DO NOT EDIT: FILE GENERATED AUTOMATICALLY BY PTERODACTYL PANEL - PTERODACTYL.IO",
    "meta": {
        "version": "PTDL_v2",
        "update_url": null
    },
    "exported_at": "2022-07-20T17:34:40+02:00",
    "name": "alt:V",
    "author": "parker@parkervcp.com",
    "description": "alt:V Multiplayer a third-party multiplayer modification for Grand Theft Auto: V",
    "features": null,
    "docker_images": {
        "ghcr.io\/parkervcp\/games:altv": "ghcr.io\/parkervcp\/games:altv"
    },
    "file_denylist": [],
    "startup": ".\/altv-server",
    "config": {
        "files": "{\r\n    \"server.cfg\": {\r\n        \"parser\": \"file\",\r\n        \"find\": {\r\n            \"host:\": \"host: 0.0.0.0\",\r\n            \"port:\": \"port: {{server.build.default.port}}\",\r\n            \"password:\": \"password: {{server.build.env.PASSWORD}}\",\r\n            \"description:\": \"description: {{server.build.env.SERVER_DESC}}\"\r\n        }\r\n    },\r\n    \"update.cfg\": {\r\n        \"parser\": \"json\",\r\n        \"find\": {\r\n            \"branch\": \"{{server.build.env.BUILD}}\"\r\n        }\r\n    }\r\n}",
        "startup": "{\r\n    \"done\": \"Server started\"\r\n}",
        "logs": "{}",
        "stop": "quit"
    },
    "scripts": {
        "installation": {
            "script": "#!\/bin\/bash\r\n#\r\n# Script Name: update.sh\r\n#\r\n# Author: Lhoerion\r\n#\r\n# Description: The following script compares SHA-1 hashes of remote and local alt:V files. If local file is missing or outdated, script automatically downloads it to script directory.\r\n#              Old files are preserved as *.old. Script also keep track of current branch and build. Server start script gets created if missing.\r\n#\r\n# Run Information: This script is run manually.\r\n# Dependencies: coreutils, wget, >=jq_1.4, pcregrep\r\n#\r\n\r\napt update\r\napt install -y libatomic1 zip unzip jq curl wget libc-bin coreutils pcregrep\r\ncd \/mnt\/server\r\n\r\n\r\n#!\/bin\/bash\r\n#\r\n# Script Name: update.sh\r\n#\r\n# Author: Lhoerion\r\n#\r\n# Description: The following script compares SHA-1 hashes of remote and local alt:V files. If local file is missing or outdated, script automatically downloads it to script directory.\r\n#              Old files are preserved as *.old. Script also keep track of current branch and build. Server start script gets created if missing.\r\n#\r\n# Run Information: This script is run manually.\r\n# Dependencies: coreutils, wget, >=jq_1.4, pcregrep\r\n#\r\n\r\nnoBackup=false\r\nnoLogFile=false\r\ndryRun=false\r\nsilent=false\r\nfor arg in \"$@\"\r\ndo\r\n  if [ $arg = '--no-logfile' ]; then\r\n    noLogFile=true\r\n  elif [ $arg = '--no-backup' ]; then\r\n    noBackup=true\r\n  elif [ $arg = '--dry-run' ]; then\r\n    dryRun=true\r\n  elif [ $arg = '--silent' ]; then\r\n    silent=true\r\n  fi\r\ndone\r\nfiles=()\r\nprintAndLog() {\r\n  if [[ \"$silent\" == false ]]; then\r\n    outFd=1\r\n  else\r\n    exec {outFd}>\/dev\/null\r\n  fi\r\n  if [ \"$2\" = 'ERR' ]; then\r\n    printf \"\\e[91m[$(date +%T)][Error] $1\\e[39m\" |& ([ $noLogFile != true ] && tee -a 'update.log' >& $outFd || cat)\r\n  elif [ \"$2\" = 'WARN' ]; then\r\n    printf \"\\e[93m[$(date +%T)][Warning] $1\\e[39m\" |& ([ $noLogFile != true ] && tee -a 'update.log' >& $outFd || cat)\r\n  elif [ \"$2\" = 'APP' ]; then\r\n    printf \"$1\" |& ([ $noLogFile != true ] && tee -a 'update.log' >& $outFd || cat)\r\n  else\r\n    printf \"[$(date +%T)] $1\" |& ([ $noLogFile != true ] && tee -a 'update.log' >& $outFd || cat)\r\n  fi\r\n}\r\nsemVerCmp() {\r\n  declare -r \"semVerRegex=^(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*))?\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$\"\r\n  local matchA=(${1##v})\r\n  local matchB=(${2##v})\r\n  for i in {1..5}\r\n  do\r\n    matchA+=$(`echo ${1##v} | pcregrep \"-o$i\" $semVerRegex`)\r\n    matchB+=$(`echo ${2##v} | pcregrep \"-o$i\" $semVerRegex`)\r\n  done\r\n  if [ ${1##v} == ${2##v} ]; then\r\n    echo 0 && return 0\r\n  fi\r\n  if [ \\( -z $matchA[4] \\) -a \\( ! -z $matchB[4] \\) ]; then\r\n    echo 1 && return 0\r\n  elif [ \\( ! -z $matchA[4] \\) -a \\( -z $matchB[4] \\) ]; then\r\n    echo -1 && return 0\r\n  fi\r\n  local i=0\r\n  for a in ${matchA[@]}\r\n  do\r\n    local b=\"${matchB[$i]}\"\r\n    if [ \\( $i -eq 2 \\) -a \\( \\( -z $a \\) -o \\( -z $b \\) \\) ]; then\r\n      continue\r\n    fi\r\n    if [[ $a > $b ]]; then\r\n      echo 1 && return 0\r\n    elif [[ $a < $b ]]; then\r\n      echo -1 && return 0\r\n    fi\r\n    i=$((i + 1))\r\n  done\r\n  echo 0 && return 0\r\n}\r\ngetFileHash() {\r\n  local file=(${1##v})\r\n  sha1sum \"$file\" | awk '{print $1}'\r\n}\r\nfetchUpdateData() {\r\n  updateData=$(curl -s \"https:\/\/cdn.altv.mp\/server\/$localBranch\/x64_linux\/update.json\" -A 'AltPublicAgent')\r\n  echo $updateData | jq -e '.' >\/dev\/null 2>&1\r\n  if [ $? -ne 0 ]; then\r\n    printAndLog \"Failed to check for update, try again later\\n\" 'ERR'\r\n    exit 1\r\n  fi\r\n\r\n  str='. | to_entries | map(if .key==\"hashList\" then {\"key\":.key} + {\"value\":(.value | to_entries | map(. + {\"value\":[.value, \"%s\"]}) | from_entries)} else . end) | from_entries'\r\n  \r\n  local updateTmp=($(mktemp '\/tmp\/update.sh.XXX'))\r\n\r\n  echo '{}' > ${updateTmp[0]}\r\n  echo $updateData | jq -c \"$(printf \"$str\" 'server')\" > ${updateTmp[0]}\r\n\r\n  updateData=$(curl -s \"https:\/\/cdn.altv.mp\/data\/$localBranch\/update.json\" -A 'AltPublicAgent')\r\n    if [ $? -ne 0 ]; then\r\n    printAndLog \"Failed to check for update, try again later\\n\" 'ERR'\r\n    exit 1\r\n  fi\r\n\r\n  updateTmp+=($(mktemp '\/tmp\/update.sh.XXX'))\r\n  echo '{}' > \"${updateTmp[${#updateTmp[@]} - 1]}\"\r\n  echo $updateData | jq -c \"$(printf \"$str\" 'data')\" > \"${updateTmp[${#updateTmp[@]} - 1]}\"\r\n\r\n  for (( i=0; i < ${#modules[@]}; i++ ))\r\n  do\r\n    if [[ \"${modules[$i]}\" == 'csharp-module' ]]; then\r\n      modules[$i]='coreclr-module'\r\n    fi\r\n    local moduleName=${modules[$i]}\r\n    updateData=$(curl -s \"https:\/\/cdn.altv.mp\/$moduleName\/$localBranch\/x64_linux\/update.json\" -A 'AltPublicAgent')\r\n    echo $updateData | jq -e '.' >\/dev\/null 2>&1\r\n    if [ $? -ne 0 ]; then\r\n      printAndLog \"Failed to check for $moduleName update\\n\" 'WARN'\r\n    else\r\n      updateTmp+=($(mktemp '\/tmp\/update.sh.XXX'))\r\n      echo '{}' > \"${updateTmp[${#updateTmp[@]} - 1]}\"\r\n      echo $updateData | jq -c \"$(printf \"$str\" \"$moduleName\")\" > \"${updateTmp[${#updateTmp[@]} - 1]}\"\r\n    fi\r\n  done\r\n  updateData=$(jq -s '.[0].latestBuildNumber as $b | .[0].version as $c | reduce .[] as $x ({}; . * $x) | .latestBuildNumber=$b | .version=$c' ${updateTmp[@]})\r\n  remoteBuild=\"$(echo \"$updateData\" | jq -r '.latestBuildNumber')\"\r\n  [[ $remoteBuild -eq -1 ]] && remoteBuild=$(echo \"$updateData\" | jq -r '.version')\r\n}\r\nvalidateFiles() {\r\n  files=()\r\n  for file in $(echo $updateData | jq -r '.hashList | keys[]')\r\n  do\r\n    if [[ ! -e \"$file\" || (\"$(printf \"%0.s0\" {1..40})\" != \"$(echo \"$updateData\" | jq -r \".hashList.\\\"$file\\\"[0]\")\" && $(getFileHash \"$file\") != \"$(echo \"$updateData\" | jq -r \".hashList.\\\"$file\\\"[0]\")\") ]]; then\r\n      files+=(\"$file\")\r\n    fi\r\n  done\r\n  if [ ! -e 'server.cfg' ]; then\r\n    printAndLog \"Server file server.cfg not found, creating one . . . \"\r\n    if [[ \"$dryRun\" == false ]]; then\r\n      printf 'name: \"alt:V Server\"\\nhost: 0.0.0.0\\nport: 7788\\nplayers: 128\\n#password: ultra-password\\nannounce: false\\n#token: YOUR_TOKEN\\ngamemode: Freeroam\\nwebsite: example.com\\nlanguage: en\\ndescription: \"alt:V Sample Server\"\\nmodules: [\\n  \\n]\\nresources: [\\n  \\n]\\n' > 'server.cfg' && printAndLog 'done\\n' 'APP' || printAndLog 'failed\\n' 'APP'\r\n    else\r\n      printAndLog 'done\\n' 'APP'\r\n    fi\r\n  fi\r\n  if [[ ! $localBuild =~ ^[0-9]+$ || $localBuild -ge 1232 ]]; then\r\n    local nodeExist=$([ -e 'libnode.so.72' ] && echo true || echo false)\r\n    local moduleExist=$([ -e 'modules\/libnode-module.so' ] && echo true || echo false)\r\n    if [[ \"$nodeExist\" == true || \"$moduleExist\" == true ]]; then\r\n      printAndLog \"Found old node-module files, removing . . . \"\r\n      if [[ \"$dryRun\" == false ]]; then\r\n        local result1=true\r\n        local result2=true\r\n        if [[ \"$nodeExist\" == true ]]; then\r\n          rm -f 'libnode.so.72'\r\n          result1=$([[ \"$?\" -eq 0 ]] && echo true || echo false)\r\n        fi\r\n        if [[ \"$moduleExist\" == true ]]; then\r\n          rm -f 'modules\/libnode-module.so'\r\n          result2=$([[ \"$?\" -eq 0 ]] && echo true || echo false)\r\n        fi\r\n        if [[ \"$result1\" == true && \"$result2\" == true ]]; then\r\n          printAndLog 'done\\n' 'APP'\r\n        else\r\n          printAndLog 'failed\\n' 'APP'\r\n        fi\r\n      else\r\n        printAndLog 'done\\n' 'APP'\r\n      fi\r\n    fi\r\n  fi\r\n  if [ $localBuild != $remoteBuild ]; then\r\n    printAndLog \"Server files update is available\\n\"\r\n  elif [ \"${#files[@]}\" -ne 0 ]; then\r\n    printAndLog \"Server files are invalidated\/corrupted, ${#files[@]} in total\\n\"\r\n  else\r\n    printAndLog \"Server files are up-to-date, no action required\\n\"\r\n  fi\r\n\r\n  if [[ \"$dryRun\" == false ]]; then\r\n    localBuild=$remoteBuild\r\n    modulesTemp=\"\"\r\n    for (( i=0; i < ${#modules[@]}; i++ ))\r\n    do\r\n      modulesTemp+=\"\\\"${modules[$i]}\\\"\"\r\n      if [ $(($i + 1)) -ne ${#modules[@]} ]; then\r\n        modulesTemp+=','\r\n      fi\r\n    done\r\n    if [[ $localBuild =~ ^[0-9]+$ ]]; then\r\n      printf '{\"branch\":\"%s\",\"build\":%d,\"modules\":[%s]}' $localBranch $localBuild $modulesTemp | jq '.' > 'update.cfg'\r\n    else\r\n      printf '{\"branch\":\"%s\",\"build\":\"%s\",\"modules\":[%s]}' $localBranch $localBuild $modulesTemp | jq '.' > 'update.cfg'\r\n    fi\r\n  fi\r\n}\r\ndownloadFiles() {\r\n  if [ \"${#files[@]}\" -eq 0 ]; then\r\n    return\r\n  fi\r\n  for file in ${files[@]}\r\n  do\r\n    dlType=\"$(echo \"$updateData\" | jq -r \".hashList.\\\"$file\\\"[1]\")\"\r\n    platform=$([ \"$dlType\" == \"data\" ] && echo \"\" || echo \"x64_linux\/\")\r\n    updateData=\"$(echo $updateData | jq -r '.hashList.\"altv-server\"[1] = \"server\"')\"\r\n    outDir=\"$(dirname $file)\"\r\n    printAndLog \"Downloading file $file . . . \"\r\n    if [[ \"$dryRun\" == false ]]; then\r\n      if [[ \"$noBackup\" == false && -e \"$file\" ]]; then\r\n        mv \"$file\" \"$file.old\"\r\n      fi\r\n      if [[ ! -e \"$outDir\/\" ]]; then\r\n        mkdir -p \"$outDir\/\"\r\n      fi\r\n\r\n      wget \"https:\/\/cdn.altv.mp\/$dlType\/$localBranch\/$platform${file}?build=$localBuild\" -U 'AltPublicAgent' -O \"$file\" -N -q && printAndLog 'done\\n' 'APP' || printAndLog 'failed\\n' 'APP'\r\n      if [ ! -e \"$file\" ]; then\r\n        continue\r\n      fi\r\n      if [ -e \"$file.old\" ]; then\r\n        chmod --reference=\"$file.old\" \"$file\" || printAndLog \"Failed to copy chmod to file $file\\n\" 'ERR'\r\n        chmod -x \"$file.old\" || printAndLog \"Failed to remove execution permissions from file $file.old\\n\" 'ERR'\r\n      else\r\n        chmod +x \"$file\" || printAndLog \"Failed to add execution permissions to file $file\\n\" 'ERR'\r\n      fi\r\n    else\r\n      printAndLog 'done\\n' 'APP'\r\n    fi\r\n  done\r\n  validateFiles\r\n}\r\n\r\nif [ $noLogFile != true ]; then\r\n  truncate -s 0 'update.log'\r\nfi\r\nif [[ ( \"$dryRun\" == false ) && ( ! -e 'update.cfg' ) ]]; then\r\n  printf '{\"branch\":\"release\",\"modules\":[\"js-module\"]}' | jq '.' > 'update.cfg'\r\nfi\r\nupdateCfg=$([[ -e 'update.cfg' ]] && cat 'update.cfg' || printf '{\"branch\":\"release\",\"modules\":[\"js-module\"]}' | jq '.')\r\nlocalBranch=$(echo \"$updateCfg\" | jq -r '.branch')\r\n[[ ! -n \"$localBranch\" || \"$localBranch\" != 'release' && \"$localBranch\" != 'rc' && \"$localBranch\" != 'dev' ]] && localBranch='release'\r\nmodules=($(echo \"$updateCfg\" | jq -r '.modules \/\/ \"\"' | tr -d '[],\"'))\r\n[[ ! -n \"$modules\" ]] && modules=('js-module')\r\nfetchUpdateData\r\nlocalBuild=\"$(echo \"$updateCfg\" | jq -r 'if .build == null then empty else .build end')\"\r\n[[ -z $localBuild || $localBuild == \"-1\" ]] && localBuild=$remoteBuild\r\nprintAndLog \"Current version: $localBuild\\n\"\r\nprintAndLog \"Latest version: $remoteBuild\\n\"\r\nvalidateFiles\r\ndownloadFiles\r\n\r\nchmod +x .\/altv-server\r\n\r\ncd \/mnt\/server\/data\r\n\r\nif [ -f \"vehmodels.bin\" ];\r\nthen\r\n    echo \"vehmodels.bin found.\"\r\nelse\r\n    echo \"vehmodels.bin has not been found, downloading it\"\r\n    wget https:\/\/cdn.altv.mp\/data\/${BUILD}\/data\/vehmodels.bin\r\nfi\r\n\r\nif [ -f \"vehmods.bin\" ];\r\nthen\r\n    echo \"vehmods.bin found.\"\r\nelse\r\n    echo \"vehmods.bin has not been found, downloading it\"\r\n    wget https:\/\/cdn.altv.mp\/data\/${BUILD}\/data\/vehmods.bin\r\nfi\r\n\r\nif [ -f \"clothes.bin\" ];\r\nthen\r\n    echo \"clothes.bin found.\"\r\nelse\r\n    echo \"clothes.bin has not been found, downloading it\"\r\n    wget wget https:\/\/cdn.altv.mp\/data\/${BUILD}\/data\/clothes.bin\r\nfi\r\n\r\ncd \/mnt\/server\r\necho -e \"install complete\"",
            "container": "debian:buster-slim",
            "entrypoint": "bash"
        }
    },
    "variables": [
        {
            "name": "ld lib path",
            "description": "Needed to load modules correctly. Best left at default.",
            "env_variable": "LD_LIBRARY_PATH",
            "default_value": ".",
            "user_viewable": false,
            "user_editable": false,
            "rules": "required|string|max:20",
            "field_type": "text"
        },
        {
            "name": "Release Branch",
            "description": "The supported versions are release, rc, dev (default: release).\r\nIf you don't know what you are doing leave it on release",
            "env_variable": "BUILD",
            "default_value": "release",
            "user_viewable": true,
            "user_editable": true,
            "rules": "required|string|max:20",
            "field_type": "text"
        },
        {
            "name": "Server Password",
            "description": "The password to join the server",
            "env_variable": "PASSWORD",
            "default_value": "ChangeMe",
            "user_viewable": true,
            "user_editable": true,
            "rules": "nullable|string|min:6",
            "field_type": "text"
        },
        {
            "name": "Server Description",
            "description": "Description for the server.",
            "env_variable": "SERVER_DESC",
            "default_value": "A Pterodactyl Hosted Server",
            "user_viewable": true,
            "user_editable": true,
            "rules": "required|string",
            "field_type": "text"
        }
    ]
}
