#!/bin/bash

: "${DEBUG_EXEC:=false}"
: "${SETUP_ONLY:=false}"
: "${CUSTOM_JAR_EXEC:=}"

# shellcheck source=start-utils
. "$(dirname "$0")/start-utils"
isDebugging && set -x
baseDataDir=/data
tmpServerIconPath=/tmp/icon.img
serverIconPath=${baseDataDir}/server-icon.png
mcHealthEnvPath=${baseDataDir}/.mc-health.env
bootstrapPath=${baseDataDir}/bootstrap.txt

if [ -n "$ICON" ]; then
    if [ ! -e server-icon.png ] || isTrue "${OVERRIDE_ICON}"; then
      log "Using server icon from $ICON..."
      if isURL "$ICON"; then
        # Not sure what it is yet...call it "img"
        if ! get -o "$tmpServerIconPath" "$ICON"; then
          logError "Failed to download icon from $ICON"
          exit 1
        fi
        ICON="$tmpServerIconPath"
        iconSrc="url"
      elif [ -f "$ICON" ]; then
        iconSrc="file"
      else
        logError "$ICON does not appear to be a URL or existing file"
        exit 1
      fi
      read -r -a specs < <(identify "$ICON" | awk 'NR == 1 { print $2, $3 }')
      if [ "${specs[0]} ${specs[1]}" = "PNG 64x64" ]; then
        if [ $iconSrc = url ]; then
          mv -f "$tmpServerIconPath" "$serverIconPath"
        else
          cp -f "$ICON" "$serverIconPath"
        fi
      elif [ "${specs[0]}" = GIF ]; then
        log "Converting GIF image to 64x64 PNG..."
        convert "$ICON"[0] -resize 64x64! "$serverIconPath"
      else
        log "Converting image to 64x64 PNG..."
        convert "$ICON" -resize 64x64! "$serverIconPath"
      fi
    fi
fi

useGeneratedLogs=${GENERATE_LOG4J2_CONFIG:-${ENABLE_ROLLING_LOGS:-false}}
useFallbackJvmFlag=false

SERVER_DIR="$baseDataDir"
if [[ ${FTB_DIR:-} ]]; then
  SERVER_DIR="$FTB_DIR"
fi


patchLog4jConfig() {
  file=${1?}
  url=${2?}
  if ! get -o "${SERVER_DIR}/${file}" "$url"; then
    logError "Failed to download corrected log4j config, fallback to JVM flag"
    useFallbackJvmFlag=true
    return 1
  fi
  JVM_OPTS="-Dlog4j.configurationFile=${file} ${JVM_OPTS}"
  useGeneratedLogs=false
}

# Temporarily disable debugging output
oldState=$(shopt -po xtrace || true)
shopt -u -o xtrace
# 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
# See https://purpurmc.org/docs/Log4j/
elif isType PURPUR && versionLessThan 1.17; then
  patchLog4jConfig purpur_log4j2_1141-1165.xml https://purpurmc.org/docs/xml/purpur_log4j2_1141-1165.xml
elif isType PURPUR && versionLessThan 1.18.1; then
  patchLog4jConfig purpur_log4j2_117.xml https://purpurmc.org/docs/xml/purpur_log4j2_117.xml
elif versionLessThan 1.18.1; then
  useFallbackJvmFlag=true
fi
eval "$oldState"

if ${useFallbackJvmFlag}; then
  JVM_OPTS="-Dlog4j2.formatMsgNoLookups=true ${JVM_OPTS}"
fi

if versionLessThan 1.7; then
  : # No patch required here.
elif versionLessThan 1.18.1; then
  if isTrue ${SKIP_LOG4J_PATCHER:-false}; then
    log "Skipping Log4jPatcher, make sure you are not affected"
  else
    JVM_OPTS="-javaagent:/image/Log4jPatcher.jar ${JVM_OPTS}"
  fi
fi

# Set up log4j2 configuration with templating support
LOGFILE="${SERVER_DIR}/log4j2.xml"
if ${useGeneratedLogs}; then
  # Set up log configuration defaults
  : "${LOG_LEVEL:=info}"
  : "${ROLLING_LOG_MAX_FILES:=1000}"
  # Note: Can't use ${VAR:=default} syntax for values containing } as it breaks parsing
  if [ -z "${ROLLING_LOG_FILE_PATTERN}" ]; then
    ROLLING_LOG_FILE_PATTERN='logs/%d{yyyy-MM-dd}-%i.log.gz'
  fi

  # Pattern format defaults (compatible with vanilla Minecraft)
  # Note: Can't use ${VAR:=default} syntax because } in the value breaks parsing
  if [ -z "${LOG_CONSOLE_FORMAT}" ]; then
    LOG_CONSOLE_FORMAT='[%d{HH:mm:ss}] [%t/%level]: %msg%n'
  fi
  if [ -z "${LOG_TERMINAL_FORMAT}" ]; then
    LOG_TERMINAL_FORMAT='[%d{HH:mm:ss} %level]: %msg%n'
  fi
  if [ -z "${LOG_FILE_FORMAT}" ]; then
    LOG_FILE_FORMAT='[%d{HH:mm:ss}] [%t/%level]: %msg%n'
  fi

  export LOG_LEVEL ROLLING_LOG_FILE_PATTERN ROLLING_LOG_MAX_FILES
  export LOG_CONSOLE_FORMAT LOG_TERMINAL_FORMAT LOG_FILE_FORMAT


  # Always regenerate if file doesn't exist
  if [ ! -e "$LOGFILE" ] || isTrue "${REGENERATE_LOG4J2:-true}"; then
    log "Generating log4j2.xml from template in ${LOGFILE}"

    # Generate log4j2.xml using heredoc for reliable variable substitution
    cat > "$LOGFILE" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="SysOut" target="SYSTEM_OUT">
            <PatternLayout pattern="${LOG_CONSOLE_FORMAT}" />
        </Console>
        <Queue name="TerminalConsole">
            <PatternLayout pattern="${LOG_TERMINAL_FORMAT}" />
        </Queue>
        <RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="${ROLLING_LOG_FILE_PATTERN}">
            <PatternLayout pattern="${LOG_FILE_FORMAT}" />
            <Policies>
                <TimeBasedTriggeringPolicy />
                <OnStartupTriggeringPolicy />
            </Policies>
            <DefaultRolloverStrategy max="${ROLLING_LOG_MAX_FILES}"/>
        </RollingRandomAccessFile>
    </Appenders>
    <Loggers>
        <Root level="${LOG_LEVEL}">
            <filters>
                <MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL" />
            </filters>
            <AppenderRef ref="SysOut"/>
            <AppenderRef ref="File"/>
            <AppenderRef ref="TerminalConsole"/>
        </Root>
    </Loggers>
</Configuration>
EOF
  else
    log "log4j2.xml already exists and is up to date, skipping generation"
  fi

  # Apply the log4j2 configuration
  JVM_OPTS="-Dlog4j.configurationFile=log4j2.xml ${JVM_OPTS}"
else
  rm -f "${LOGFILE}"
fi

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

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

expandedDOpts=
if [ -n "$JVM_DD_OPTS" ]; then
      for dopt in $JVM_DD_OPTS
      do
          expandedDOpts="${expandedDOpts} -D${dopt/:/=}"
      done
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

: "${USE_AIKAR_FLAGS:=false}"
: "${USE_MEOWICE_FLAGS:=false}"
: "${USE_MEOWICE_GRAALVM_FLAGS:=false}"

if isTrue "${USE_MEOWICE_FLAGS}"; then
  java_major_version=$(mc-image-helper java-release)
  if [[ $java_major_version -gt 16 ]]; then
    log "Java version $java_major_version using MeowIce's flags for Java 17+"
  else
    log "Your Java version is $java_major_version, MeowIce's flags are for Java 17+ falling back to Aikar's"
    USE_MEOWICE_FLAGS=FALSE
    USE_AIKAR_FLAGS=TRUE
  fi
fi

if isTrue "${USE_AIKAR_FLAGS}" || isTrue "${USE_MEOWICE_FLAGS}"; then
  # From https://mcflags.emc.gs/
  if isTrue "${USE_MEOWICE_FLAGS}"; then
    log "Using MeowIce's flags"
    G1NewSizePercent=28
    G1MaxNewSizePercent=50
    G1HeapRegionSize=16M
    G1ReservePercent=15
    InitiatingHeapOccupancyPercent=20
    G1MixedGCCountTarget=3
    G1RSetUpdatingPauseTimePercent=0
  elif [[ $MAX_MEMORY ]] && (( $(normalizeMemSize "${MAX_MEMORY}") >= $(normalizeMemSize 12g) )); then
    log "Using Aikar's >12GB flags"
    G1NewSizePercent=40
    G1MaxNewSizePercent=50
    G1HeapRegionSize=16M
    G1ReservePercent=15
    InitiatingHeapOccupancyPercent=20
    G1MixedGCCountTarget=4
    G1RSetUpdatingPauseTimePercent=5
  else
    log "Using Aikar's flags"
    G1NewSizePercent=30
    G1MaxNewSizePercent=40
    G1HeapRegionSize=8M
    G1ReservePercent=20
    InitiatingHeapOccupancyPercent=15
    G1MixedGCCountTarget=4
    G1RSetUpdatingPauseTimePercent=5
  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=${G1MixedGCCountTarget}
  -XX:InitiatingHeapOccupancyPercent=${InitiatingHeapOccupancyPercent}
  -XX:G1MixedGCLiveThresholdPercent=90
  -XX:G1RSetUpdatingPauseTimePercent=${G1RSetUpdatingPauseTimePercent}
  -XX:SurvivorRatio=32
  -XX:+PerfDisableSharedMem
  -XX:MaxTenuringThreshold=1
  "
  if isTrue "${USE_AIKAR_FLAGS}"; then
    JVM_XX_OPTS="${JVM_XX_OPTS}
    -Dusing.aikars.flags=https://mcflags.emc.gs
    -Daikars.new.flags=true
    "
  fi
fi

if isTrue "${USE_MEOWICE_FLAGS}"; then
  JVM_XX_OPTS="${JVM_XX_OPTS}
  -XX:+UnlockDiagnosticVMOptions
  -XX:+UnlockExperimentalVMOptions
  -XX:G1SATBBufferEnqueueingThresholdPercent=30
  -XX:G1ConcMarkStepDurationMillis=5
  -XX:+UseNUMA
  -XX:-DontCompileHugeMethods
  -XX:MaxNodeLimit=240000
  -XX:NodeLimitFudgeFactor=8000
  -XX:ReservedCodeCacheSize=400M
  -XX:NonNMethodCodeHeapSize=12M
  -XX:ProfiledCodeHeapSize=194M
  -XX:NonProfiledCodeHeapSize=194M
  -XX:NmethodSweepActivity=1
  -XX:+UseFastUnorderedTimeStamps
  -XX:+UseCriticalJavaThreadPriority
  -XX:AllocatePrefetchStyle=3
  -XX:+AlwaysActAsServerClassMachine
  -XX:+UseTransparentHugePages
  -XX:LargePageSizeInBytes=2M
  -XX:+UseLargePages
  -XX:+EagerJVMCI
  -XX:+UseStringDeduplication
  -XX:+UseAES
  -XX:+UseAESIntrinsics
  -XX:+UseFMA
  -XX:+UseLoopPredicate
  -XX:+RangeCheckElimination
  -XX:+OptimizeStringConcat
  -XX:+UseCompressedOops
  -XX:+UseThreadPriorities
  -XX:+OmitStackTraceInFastThrow
  -XX:+RewriteBytecodes
  -XX:+RewriteFrequentPairs
  -XX:+UseFPUForSpilling
  -XX:+UseVectorCmov
  -XX:+UseXMMForArrayCopy
  -XX:+EliminateLocks
  -XX:+DoEscapeAnalysis
  -XX:+AlignVector
  -XX:+OptimizeFill
  -XX:+EnableVectorSupport
  -XX:+UseCharacterCompareIntrinsics
  -XX:+UseCopySignIntrinsic
  -XX:+UseVectorStubs
  "
  if [[ $(arch) == "x86_64" ]]; then
    JVM_XX_OPTS="${JVM_XX_OPTS}
    -XX:+UseFastStosb
    -XX:+UseNewLongLShift
    -XX:+UseXmmI2D
    -XX:+UseXmmI2F
    -XX:+UseXmmLoadAndClearUpper
    -XX:+UseXmmRegToRegMoveAll
    -XX:UseAVX=2
    -XX:UseSSE=4
    "
  else
    log "cpu not x86_64, disabling architecture specific flags"
  fi
fi

if isTrue "${USE_MEOWICE_GRAALVM_FLAGS}"; then
  if [[ $java_major_version -gt 23 ]]; then
    log "Java 24 or higher detected, using modified GraalVM flags"
    JVM_XX_OPTS="${JVM_XX_OPTS}
    -XX:+UseFastJNIAccessors
    -XX:+UseInlineCaches
    -XX:+SegmentedCodeCache
    -Djdk.nio.maxCachedBufferSize=262144
    -Djdk.graal.UsePriorityInlining=true
    -Djdk.graal.Vectorization=true
    -Djdk.graal.OptDuplication=true
    -Djdk.graal.DetectInvertedLoopsAsCounted=true
    -Djdk.graal.LoopInversion=true
    -Djdk.graal.VectorizeHashes=true
    -Djdk.graal.EnterprisePartialUnroll=true
    -Djdk.graal.VectorizeSIMD=true
    -Djdk.graal.StripMineNonCountedLoops=true
    -Djdk.graal.SpeculativeGuardMovement=true
    -Djdk.graal.TuneInlinerExploration=1
    -Djdk.graal.LoopRotation=true
    -Djdk.graal.CompilerConfiguration=enterprise
    --enable-native-access=ALL-UNNAMED
    "
  else
    log "Using MeowIce's flags for Graalvm"
    JVM_XX_OPTS="${JVM_XX_OPTS}
    -XX:+UseFastJNIAccessors
    -XX:+UseInlineCaches
    -XX:+SegmentedCodeCache
    -Djdk.nio.maxCachedBufferSize=262144
    -Dgraal.UsePriorityInlining=true
    -Dgraal.Vectorization=true
    -Dgraal.OptDuplication=true
    -Dgraal.DetectInvertedLoopsAsCounted=true
    -Dgraal.LoopInversion=true
    -Dgraal.VectorizeHashes=true
    -Dgraal.EnterprisePartialUnroll=true
    -Dgraal.VectorizeSIMD=true
    -Dgraal.StripMineNonCountedLoops=true
    -Dgraal.SpeculativeGuardMovement=true
    -Dgraal.TuneInlinerExploration=1
    -Dgraal.LoopRotation=true
    -Dgraal.OptWriteMotion=true
    -Dgraal.CompilerConfiguration=enterprise
    "
  fi
fi

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

if isTrue "${USE_SIMD_FLAGS}"; then
  JVM_XX_OPTS="${JVM_XX_OPTS}
  --add-modules=jdk.incubator.vector
  "
fi

# Handle GTNH args
if isType "GTNH"; then
  expandedDOpts="${expandedDOpts} -Dfml.readTimeout=180"
  java_major_version=$(mc-image-helper java-release)
  if (( java_major_version == 8 )); then
    debug "Setting GTNH java8 args."
    JVM_XX_OPTS="${JVM_XX_OPTS}
    -XX:+UseStringDeduplication
    -XX:+UseCompressedOops
    -XX:+UseCodeCacheFlushing
    "
  elif (( java_major_version >= 17 )); then
    debug "Setting GTNH java17+ args."
    expandedDOpts="${expandedDOpts} @java9args.txt"
  fi
fi

if [[ ${INIT_MEMORY} || ${MAX_MEMORY} ]]; then
  log "Setting initial memory to ${INIT_MEMORY:=${MEMORY}} and max to ${MAX_MEMORY:=${MEMORY}}"
  if isPercentage "$INIT_MEMORY"; then
    JVM_OPTS="-XX:InitialRAMPercentage=$(getPercentageValue "${INIT_MEMORY}") ${JVM_OPTS}"
  elif [[ ${INIT_MEMORY} ]]; then
    JVM_OPTS="-Xms${INIT_MEMORY} ${JVM_OPTS}"
  fi

  if isPercentage "$MAX_MEMORY"; then
    JVM_OPTS="-XX:MaxRAMPercentage=$(getPercentageValue "${MAX_MEMORY}") ${JVM_OPTS}"
  elif [[ ${MAX_MEMORY} ]]; then
    JVM_OPTS="-Xmx${MAX_MEMORY} ${JVM_OPTS}"
  fi
fi

function copyFilesForCurseForge() {
  if [ ! -e "${FTB_DIR}/server-icon.png" ] && [ -e "$serverIconPath" ]; then
    cp -f "$serverIconPath" "${FTB_DIR}/"
  fi

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

if versionLessThan 'b1.8'; then
  echo "
DISABLE_HEALTHCHECK=true
  " > "$mcHealthEnvPath"
elif versionLessThan 1.3; then
  echo "
MC_HEALTH_EXTRA_ARGS=(
  --use-old-server-list-ping
)
  " > "$mcHealthEnvPath"
elif versionLessThan 1.7; then
  echo "
MC_HEALTH_EXTRA_ARGS=(
  --use-server-list-ping
)
  " > "$mcHealthEnvPath"
elif isTrue "$USES_PROXY_PROTOCOL"; then
  echo "
MC_HEALTH_EXTRA_ARGS=(
  --use-proxy
)
  " > "$mcHealthEnvPath"
else
  rm -f "$mcHealthEnvPath"
fi

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

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

  cd "${FTB_DIR}" || (logError "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

if isPercentage "$INIT_MEMORY" || isPercentage "$MAX_MEMORY"; then
# Convert to bytes
NORM_INIT_MEM=$(normalizeMemSize "$INIT_MEMORY")
NORM_MAX_MEM=$(normalizeMemSize "$MAX_MEMORY")
# Convert to MB
((NORM_INIT_MEM*=1048576))
((NORM_MAX_MEM*=1048576))

  cat > "${FTB_DIR}/settings-local.sh" <<EOF
export MIN_RAM="${NORM_INIT_MEM}M"
export MAX_RAM="${NORM_MAX_MEM}M"
export JAVA_PARAMETERS="${JVM_XX_OPTS} ${JVM_OPTS} $expandedDOpts"
EOF
else
  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
fi

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

  cd "${FTB_DIR}" || (logError "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}"; then
    echo "SETUP_ONLY: ${finalArgs}"
    exit
  fi

  if isTrue "${DEBUG_EXEC}"; then
    set -x
  fi
  if [ -f "${FTB_DIR}/variables.txt" ]; then
      JVM_ARGS="${JVM_XX_OPTS} ${JVM_OPTS} $expandedDOpts"
      JVM_ARGS=${JVM_ARGS//$'\n'/}
      sed -i "s~JAVA_ARGS=.*~JAVA_ARGS=\"${JVM_ARGS}\"~" "${FTB_DIR}/variables.txt"
  fi
  exec mc-server-runner "${mcServerRunnerArgs[@]}" "${finalArgs[@]}"
elif [[ $SERVER =~ run.sh ]]; then
  log "Using Forge supplied run.sh script..."
  echo $JVM_XX_OPTS $JVM_OPTS $expandedDOpts > user_jvm_args.txt
  if isTrue ${SETUP_ONLY}; then
    echo "SETUP_ONLY: bash ${SERVER}"
    exit
  fi
  if isTrue "${DEBUG_EXEC}"; then
    set -x
  fi
  exec mc-server-runner "${mcServerRunnerArgs[@]}" --shell bash "${SERVER}" $EXTRA_ARGS
else
  # If we have a bootstrap.txt file... feed that in to the server stdin
  if [ -f $bootstrapPath ]; then
    bootstrapArgs="--bootstrap $bootstrapPath"
  fi

  log "Starting the Minecraft server..."

  # Specifically want the variables to expand to args, so...
  # shellcheck disable=SC2206
  finalArgs=(
    $JVM_XX_OPTS
    $JVM_OPTS
    $expandedDOpts
  )

  if [[ $CUSTOM_JAR_EXEC ]]; then
    # shellcheck disable=SC2206
    finalArgs+=($CUSTOM_JAR_EXEC)
  else
    finalArgs+=(-jar "$SERVER")
  fi

  # shellcheck disable=SC2206
  finalArgs+=(
    "$@" $EXTRA_ARGS
  )

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

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

  exec mc-server-runner ${bootstrapArgs} "${mcServerRunnerArgs[@]}" java "${finalArgs[@]}"
fi
