#!/bin/bash

. ${SCRIPTS:-/}start-utils
isDebugging && set -x

if versionLessThan 1.7.6; then
  opsFile=ops.txt
  whitelistFile=white-list.txt
else
  opsFile=ops.json
  whitelistFile=whitelist.json
fi

function process_user_file() {
  local output=$1
  local source=$2

  if isURL "$source"; then
    log "Downloading $output from $source"
    if ! get -o /data/$output "$source"; then
      log "ERROR: failed to download from $source"
      exit 2
    fi
  else
    log "Copying $output from $source"
    if ! cp "$source" /data/$output; then
      log "ERROR: failed to copy from $source"
      exit 1
    fi
  fi
}

function process_user_csv() {
  local output=$1
  local list=$2
  local playerDataList

  if [[ "$output" == *"ops"* ]]; then
    # Extra data for ops.json
    userData='{"uuid": .id, "name": .username, "level": 4}'
  else
    userData='{"uuid": .id, "name": .username}'
  fi

  log "Updating ${output%.*}"
  for i in ${list//,/ }
  do
    if [ -e "$output" ] && grep -q "$i" "$output"; then
      log "$i already present in $output, skipping"
      continue
    fi
    if ! playerData=$(get "https://playerdb.co/api/player/minecraft/$i" | jq -re ".data.player"); then
      log "WARNING: Could not lookup user $i for ${output} addition"
    else
      playerDataList=$playerDataList$(echo $playerData | jq -r "$userData")
    fi
  done
  local newUsers=$(echo $playerDataList | jq -s .)
  if [[ $output =~ .*\.txt ]]; then
    # username list for txt config (Minecraft <= 1.7.5)
    echo $newUsers | jq -r '.[].name' >> /data/${output}
    sort -u /data/${output} -o /data/${output}
  elif [ -e /data/${output} ]; then
    # Merge with existing json file
    local currentUsers=$(cat /data/${output})
    jq --argjson current "$currentUsers" --argjson new "$newUsers" -n '$new + $current | unique_by(.uuid)' > /data/${output}
  else
    # New json file
    echo $newUsers > /data/${output}
  fi
}

if isTrue "${OVERRIDE_OPS}"; then
  log "Recreating ${opsFile} file at server startup"
  rm -f /data/${opsFile}
fi
if [ -n "${OPS_FILE}" ] && [ ! -e "/data/${opsFile}" ]; then
  process_user_file ${opsFile} "$OPS_FILE"
fi
if [ -n "${OPS}" ]; then
  process_user_csv ${opsFile} "$OPS"
fi

if isTrue "${OVERRIDE_WHITELIST}"; then
  log "Recreating ${whitelistFile} file at server startup"
  rm -f /data/${whitelistFile}
fi
if [ -n "${WHITELIST_FILE}" ] && [ ! -e "/data/${whitelistFile}" ]; then
  process_user_file ${whitelistFile} "$WHITELIST_FILE"
fi
if [ -n "${WHITELIST}" ]; then
  process_user_csv ${whitelistFile} "$WHITELIST"
fi

if [ -n "$ICON" ]; then
    if [ ! -e server-icon.png ] || [ "${OVERRIDE_ICON}" == "TRUE" ]; then
      log "Using server icon from $ICON..."
      # Not sure what it is yet...call it "img"
      curl -sSL -o /tmp/icon.img $ICON
      specs=$(identify /tmp/icon.img | awk '{print $2,$3}')
      if [ "$specs" = "PNG 64x64" ]; then
        mv /tmp/icon.img /data/server-icon.png
      else
        log "Converting image to 64x64 PNG..."
        convert /tmp/icon.img -resize 64x64! /data/server-icon.png
      fi
    fi
fi

if isTrue ${ENABLE_ROLLING_LOGS:-false}; then
  # Set up log configuration
  LOGFILE="/data/log4j2.xml"
  if [ ! -e "$LOGFILE" ]; then
    log "Creating log4j2.xml in ${LOGFILE}"
    cp /tmp/log4j2.xml "$LOGFILE"
  else
    log "log4j2.xml already created, skipping"
  fi
  JVM_OPTS="-Dlog4j.configurationFile=/data/log4j2.xml ${JVM_OPTS}"
fi

# Make sure files exist and are valid JSON (for pre-1.12 to 1.12 upgrades)
log "Checking for JSON files."
JSON_FILES=$(find /data -maxdepth 1 -name '*.json')
for j in $JSON_FILES; do
  if [[ $(cat "$j" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') == "" ]]; then
    log "Fixing JSON $j"
    echo '[]' > $j
  fi
done

# Optional disable console
if versionLessThan 1.14 && [[ ${CONSOLE,,} = false ]]; then
  EXTRA_ARGS+=" --noconsole"
fi

# Optional disable GUI for headless servers
if [[ ${GUI} = false || ${GUI} = FALSE ]]; then
  EXTRA_ARGS+=" nogui"
fi

: "${INIT_MEMORY:=${MEMORY}}"
: "${MAX_MEMORY:=${MEMORY}}"

expandedDOpts=
if [ -n "$JVM_DD_OPTS" ]; then
      for dopt in $JVM_DD_OPTS
      do
          expandedDOpts="${expandedDOpts} -D${dopt/:/=}"
      done
fi

patchLog4jConfig() {
  file=${1?}
  url=${2?}
  if ! get -o "$file" "$url"; then
    log "ERROR: failed to download corrected log4j config"
    exit 1
  fi
  JVM_OPTS="-Dlog4j.configurationFile=${file} ${JVM_OPTS}"
}

# Patch Log4j remote code execution vulnerability
# See https://www.minecraft.net/en-us/article/important-message--security-vulnerability-java-edition
if versionLessThan 1.7; then
  : # No patch required here.
elif isFamily VANILLA && versionLessThan 1.12; then
  patchLog4jConfig log4j2_17-111.xml https://launcher.mojang.com/v1/objects/dd2b723346a8dcd48e7f4d245f6bf09e98db9696/log4j2_17-111.xml
elif isFamily VANILLA && versionLessThan 1.17; then
  patchLog4jConfig log4j2_112-116.xml https://launcher.mojang.com/v1/objects/02937d122c86ce73319ef9975b58896fc1b491d1/log4j2_112-116.xml
elif versionLessThan 1.18.1; then
  JVM_OPTS="-Dlog4j2.formatMsgNoLookups=true ${JVM_OPTS}"
fi

if isTrue ${ENABLE_JMX}; then
  : ${JMX_PORT:=7091}
  JVM_OPTS="${JVM_OPTS}
  -Dcom.sun.management.jmxremote.local.only=false
  -Dcom.sun.management.jmxremote.port=${JMX_PORT}
  -Dcom.sun.management.jmxremote.rmi.port=${JMX_PORT}
  -Dcom.sun.management.jmxremote.authenticate=false
  -Dcom.sun.management.jmxremote.ssl=false
  -Dcom.sun.management.jmxremote.host=${JMX_BINDING:-0.0.0.0}
  -Djava.rmi.server.hostname=${JMX_HOST:-localhost}"

  log "JMX is enabled. Make sure you have port forwarding for ${JMX_PORT}"
fi

if isTrue "${USE_AIKAR_FLAGS}"; then
  # From https://mcflags.emc.gs/

  if (( $(normalizeMemSize "${MAX_MEMORY}") >= $(normalizeMemSize 12g) )); then
    log "Using Aikar's >12GB flags"
    G1NewSizePercent=40
    G1MaxNewSizePercent=50
    G1HeapRegionSize=16M
    G1ReservePercent=15
    InitiatingHeapOccupancyPercent=20
  else
    log "Using Aikar's flags"
    G1NewSizePercent=30
    G1MaxNewSizePercent=40
    G1HeapRegionSize=8M
    G1ReservePercent=20
    InitiatingHeapOccupancyPercent=15
  fi

  JVM_XX_OPTS="${JVM_XX_OPTS}
  -XX:+UseG1GC
  -XX:+ParallelRefProcEnabled
  -XX:MaxGCPauseMillis=200
  -XX:+UnlockExperimentalVMOptions
  -XX:+DisableExplicitGC
  -XX:+AlwaysPreTouch
  -XX:G1NewSizePercent=${G1NewSizePercent}
  -XX:G1MaxNewSizePercent=${G1MaxNewSizePercent}
  -XX:G1HeapRegionSize=${G1HeapRegionSize}
  -XX:G1ReservePercent=${G1ReservePercent}
  -XX:G1HeapWastePercent=5
  -XX:G1MixedGCCountTarget=4
  -XX:InitiatingHeapOccupancyPercent=${InitiatingHeapOccupancyPercent}
  -XX:G1MixedGCLiveThresholdPercent=90
  -XX:G1RSetUpdatingPauseTimePercent=5
  -XX:SurvivorRatio=32
  -XX:+PerfDisableSharedMem
  -XX:MaxTenuringThreshold=1
  -Dusing.aikars.flags=https://mcflags.emc.gs
  -Daikars.new.flags=true
  "
fi

if isTrue "${USE_LARGE_PAGES}"; then
  JVM_XX_OPTS="${JVM_XX_OPTS}
  -XX:+UseLargePagesInMetaspace
  "
fi

if isTrue "${USE_FLARE_FLAGS}"; then
  JVM_XX_OPTS="${JVM_XX_OPTS}
  -XX:+UnlockDiagnosticVMOptions
  -XX:+DebugNonSafepoints
  "
fi

if isTrue "${DEBUG_MEMORY}"; then
  log "Memory usage and availability (in MB)"
  uname -a
  free -m
fi

if [[ ${INIT_MEMORY} || ${MAX_MEMORY} ]]; then
  log "Setting initial memory to ${INIT_MEMORY:=${MEMORY}} and max to ${MAX_MEMORY:=${MEMORY}}"
  if [[ ${INIT_MEMORY} ]]; then
    JVM_OPTS="-Xms${INIT_MEMORY} ${JVM_OPTS}"
  fi
  if [[ ${MAX_MEMORY} ]]; then
    JVM_OPTS="-Xmx${MAX_MEMORY} ${JVM_OPTS}"
  fi
fi

function copyFilesForCurseForge() {
  # copy player modification files unconditionally since their
  # processing into json is additive anyway
  [ -f /data/ops.txt ] && cp -f /data/ops.txt "${FTB_DIR}/"
  [ -f /data/white-list.txt ] && cp -f /data/white-list.txt "${FTB_DIR}/"

  if [ ! -e "${FTB_DIR}/server-icon.png" -a -e /data/server-icon.png ]; then
    cp -f /data/server-icon.png "${FTB_DIR}/"
  fi

  cp -f /data/eula.txt "${FTB_DIR}/"
}

mcServerRunnerArgs=(
--stop-duration "${STOP_DURATION:-60}s"
--named-pipe "${CONSOLE_IN_NAMED_PIPE:-/tmp/minecraft-console-in}"
)
if [[ ${STOP_SERVER_ANNOUNCE_DELAY} ]]; then
  mcServerRunnerArgs+=(--stop-server-announce-delay "${STOP_SERVER_ANNOUNCE_DELAY}s")
fi

if [[ ${TYPE} == "CURSEFORGE" && "${SERVER}" ]]; then
  copyFilesForCurseForge

  cd "${FTB_DIR}" || (log "ERROR: can't go into ${FTB_DIR}"; exit 1)
  log "Starting CurseForge server in ${FTB_DIR}..."
  if isTrue ${DEBUG_EXEC}; then
    set -x
  fi
  exec mc-server-runner ${bootstrapArgs} "${mcServerRunnerArgs[@]}" java $JVM_XX_OPTS $JVM_OPTS $expandedDOpts -jar $(basename "${SERVER}") "$@" $EXTRA_ARGS
elif [[ ${TYPE} == "CURSEFORGE" ]]; then
  mcServerRunnerArgs+=(--shell bash)

  copyFilesForCurseForge

  cat > "${FTB_DIR}/settings-local.sh" <<EOF
export MIN_RAM="${INIT_MEMORY}"
export MAX_RAM="${MAX_MEMORY}"
export JAVA_PARAMETERS="${JVM_XX_OPTS} ${JVM_OPTS} $expandedDOpts"
EOF

  # patch CurseForge cfg file, if present
  if [ -f "${FTB_DIR}/settings.cfg" ] && [[ ${MAX_MEMORY} ]]; then
    sed -i "s/MAX_RAM=[^;]*/MAX_RAM=${MAX_MEMORY}/" "${FTB_DIR}/settings.cfg"
  fi

  cd "${FTB_DIR}" || (log "ERROR: can't go into ${FTB_DIR}"; exit 1)
  log "Running FTB ${FTB_SERVER_START} in ${FTB_DIR} ..."

  finalArgs="${FTB_SERVER_START}"

  if isTrue "${SETUP_ONLY:=false}"; then
    echo "SETUP_ONLY: ${finalArgs}"
    exit
  fi

  if isTrue ${DEBUG_EXEC}; then
    set -x
  fi
  if isTrue ${EXEC_DIRECTLY:-false}; then
    "${finalArgs[@]}"
  else
    exec mc-server-runner "${mcServerRunnerArgs[@]}" "${finalArgs[@]}"
  fi
elif [[ -x run.sh ]]; then
  log "Using Forge supplied run.sh script..."
  echo $JVM_XX_OPTS $JVM_OPTS $expandedDOpts > user_jvm_args.txt
  exec mc-server-runner "${mcServerRunnerArgs[@]}" --shell bash run.sh
else
  # If we have a bootstrap.txt file... feed that in to the server stdin
  if [ -f /data/bootstrap.txt ]; then
    bootstrapArgs="--bootstrap /data/bootstrap.txt"
  fi

  log "Starting the Minecraft server..."

  finalArgs=(
    $JVM_XX_OPTS
    $JVM_OPTS
    $expandedDOpts
    -jar "$SERVER"
    "$@" $EXTRA_ARGS
  )

  if isTrue ${SETUP_ONLY:=false}; then
    echo "SETUP_ONLY: java ${finalArgs[*]}"
    exit
  fi

  if isTrue "${DEBUG_EXEC}"; then
    set -x
  fi

  if isTrue "${EXEC_DIRECTLY:-false}"; then
    exec java "${finalArgs[@]}"
  else
    exec mc-server-runner ${bootstrapArgs} "${mcServerRunnerArgs[@]}" java "${finalArgs[@]}"
  fi
fi

