diff --git a/docs/types-and-platforms/server-types/others.md b/docs/types-and-platforms/server-types/others.md index ca2ebced..813c326b 100644 --- a/docs/types-and-platforms/server-types/others.md +++ b/docs/types-and-platforms/server-types/others.md @@ -74,3 +74,68 @@ Alternatively, the final `-jar` invocation can be replaced by setting `CUSTOM_JA When using `docker run` make sure to quote the entire value since it has spaces in it, such as -e CUSTOM_JAR_EXEC="-cp worldedit.jar:Carpet-Server.jar net.minecraft.server.MinecraftServer" + +## Pumpkin + +A [Pumpkin](https://pumpkinmc.org/) server can be run by setting `TYPE` to `PUMPKIN`. + +Pumpkin is a Minecraft server written in Rust and is shipped as a single native executable instead of a Java jar. The image downloads the asset matching its architecture from the [latest release](https://github.com/Pumpkin-MC/Pumpkin/releases), so `VERSION` and the JVM memory and tuning variables do not apply. + +```shell +docker run -d --pull=always -v /path/on/host:/data \ + -p 25565:25565 -p 19132:19132/udp -e EULA=TRUE -e TYPE=PUMPKIN \ + --name mc itzg/minecraft-server +``` + +Configuration options with defaults: + +- `FORCE_REDOWNLOAD`=false + + Set to true to re-download the executable. + +Pumpkin is configured through `/data/pumpkin.toml`. The image creates that file on the first run and updates the settings below at each startup, leaving the remaining options in your file alone. Anything without a matching environment variable can be set by editing `pumpkin.toml` directly. + +| Variable | Pumpkin setting | +| :-------------------------------------------------- | :--------------------------------------------------------------------------------- | +| `MOTD` | `networking.java.motd` and `networking.bedrock.motd` | +| `MAX_PLAYERS` | `networking.java.max_players` and `networking.bedrock.max_players` | +| `ONLINE_MODE` | `networking.java.online_mode` and `networking.bedrock.online_mode` | +| `SERVER_PORT` | `networking.java.address` | +| `VIEW_DISTANCE` | `networking.java.view_distance` and `networking.bedrock.view_distance` | +| `SIMULATION_DISTANCE` | `networking.java.simulation_distance` and `networking.bedrock.simulation_distance` | +| `LEVEL` | `default_level_name` | +| `SEED` | `seed` | +| `MODE` | `default_gamemode` | +| `DIFFICULTY` | `default_difficulty` | +| `HARDCORE` | `hardcore` | +| `ALLOW_NETHER` | `allow_nether` | +| `FORCE_GAMEMODE` | `force_gamemode` | +| `PVP` | `pvp.enabled` | +| `OP_PERMISSION_LEVEL` | `op_permission_level` | +| `SPAWN_PROTECTION` | `spawn_protection` | +| `ACCEPTS_TRANSFERS` | `accepts_transfers` | +| `WHITELIST`, `WHITELIST_FILE` or `ENABLE_WHITELIST` | `white_list` | +| `ENFORCE_WHITELIST` | `enforce_whitelist` | +| `ENABLE_RCON` | `networking.rcon.enabled` | +| `RCON_PORT` | `networking.rcon.address` | +| `RCON_PASSWORD` | `networking.rcon.password` | +| `ENABLE_QUERY` | `networking.query.enabled` | +| `QUERY_PORT` | `networking.query.address` | +| `PREVENT_PROXY_CONNECTIONS` | `networking.java.authentication.prevent_proxy_connections` | +| `RESOURCE_PACK` | `resource_pack.java.url` and `resource_pack.java.enabled` | +| `RESOURCE_PACK_SHA1` | `resource_pack.java.sha1` | +| `RESOURCE_PACK_PROMPT` | `resource_pack.java.prompt_message` | +| `RESOURCE_PACK_ENFORCE` | `resource_pack.java.force` | +| `LOG_LEVEL` | `logging.level` | + +!!! note + + The first run adds every option Pumpkin knows about to `pumpkin.toml`, since Pumpkin fills in its own defaults for the settings the image does not manage. + +!!! note + + Bedrock Edition players join on UDP port `19132`, which Pumpkin serves itself from the same world. That port has to be published, such as with `-p 19132:19132/udp`, since the image only declares `25565` with `EXPOSE`. See [examples/pumpkin](https://github.com/itzg/docker-minecraft-server/tree/master/examples/pumpkin) for a complete compose file. + +!!! note + + Pumpkin is under heavy development, as the project itself states, so configuration and world formats can change between releases. Run it on a test server and keep backups. diff --git a/examples/pumpkin/compose.yml b/examples/pumpkin/compose.yml new file mode 100644 index 00000000..04b89e62 --- /dev/null +++ b/examples/pumpkin/compose.yml @@ -0,0 +1,14 @@ +services: + mc: + image: itzg/minecraft-server + tty: true + stdin_open: true + environment: + EULA: "true" + TYPE: PUMPKIN + ports: + - "25565:25565" + - "19132:19132/udp" + volumes: + - ./data:/data + restart: unless-stopped diff --git a/files/pumpkin-config-patch.json b/files/pumpkin-config-patch.json new file mode 100644 index 00000000..4e221c92 --- /dev/null +++ b/files/pumpkin-config-patch.json @@ -0,0 +1,247 @@ +{ + "patches": [ + { + "file": "/data/pumpkin.toml", + "ops": [ + { + "$set": { + "path": "$.seed", + "value": "${SEED}" + } + }, + { + "$set": { + "path": "$.default_level_name", + "value": "${LEVEL}" + } + }, + { + "$set": { + "path": "$.default_gamemode", + "value": "${PUMPKIN_GAMEMODE}" + } + }, + { + "$set": { + "path": "$.default_difficulty", + "value": "${PUMPKIN_DIFFICULTY}" + } + }, + { + "$set": { + "path": "$.op_permission_level", + "value": "${PUMPKIN_OP_PERMISSION_LEVEL}", + "value-type": "int" + } + }, + { + "$set": { + "path": "$.spawn_protection", + "value": "${PUMPKIN_SPAWN_PROTECTION}", + "value-type": "int" + } + }, + { + "$set": { + "path": "$.hardcore", + "value": "${PUMPKIN_HARDCORE}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.allow_nether", + "value": "${PUMPKIN_ALLOW_NETHER}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.force_gamemode", + "value": "${PUMPKIN_FORCE_GAMEMODE}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.white_list", + "value": "${PUMPKIN_WHITE_LIST}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.enforce_whitelist", + "value": "${PUMPKIN_ENFORCE_WHITELIST}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.accepts_transfers", + "value": "${PUMPKIN_ACCEPTS_TRANSFERS}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.logging.level", + "value": "${LOG_LEVEL}" + } + }, + { + "$set": { + "path": "$.networking.java.address", + "value": "${PUMPKIN_JAVA_ADDRESS}" + } + }, + { + "$set": { + "path": "$.networking.java.online_mode", + "value": "${PUMPKIN_ONLINE_MODE}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.networking.java.max_players", + "value": "${PUMPKIN_MAX_PLAYERS}", + "value-type": "int" + } + }, + { + "$set": { + "path": "$.networking.java.view_distance", + "value": "${PUMPKIN_VIEW_DISTANCE}", + "value-type": "int" + } + }, + { + "$set": { + "path": "$.networking.java.simulation_distance", + "value": "${PUMPKIN_SIMULATION_DISTANCE}", + "value-type": "int" + } + }, + { + "$set": { + "path": "$.networking.java.motd", + "value": "${MOTD}" + } + }, + { + "$set": { + "path": "$.networking.java.authentication.prevent_proxy_connections", + "value": "${PUMPKIN_PREVENT_PROXY_CONNECTIONS}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.networking.bedrock.online_mode", + "value": "${PUMPKIN_ONLINE_MODE}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.networking.bedrock.max_players", + "value": "${PUMPKIN_MAX_PLAYERS}", + "value-type": "int" + } + }, + { + "$set": { + "path": "$.networking.bedrock.view_distance", + "value": "${PUMPKIN_VIEW_DISTANCE}", + "value-type": "int" + } + }, + { + "$set": { + "path": "$.networking.bedrock.simulation_distance", + "value": "${PUMPKIN_SIMULATION_DISTANCE}", + "value-type": "int" + } + }, + { + "$set": { + "path": "$.networking.bedrock.motd", + "value": "${MOTD}" + } + }, + { + "$set": { + "path": "$.networking.query.enabled", + "value": "${PUMPKIN_ENABLE_QUERY}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.networking.query.address", + "value": "${PUMPKIN_QUERY_ADDRESS}" + } + }, + { + "$set": { + "path": "$.networking.rcon.enabled", + "value": "${PUMPKIN_ENABLE_RCON}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.networking.rcon.address", + "value": "${PUMPKIN_RCON_ADDRESS}" + } + }, + { + "$set": { + "path": "$.networking.rcon.password", + "value": "${RCON_PASSWORD}" + } + }, + { + "$set": { + "path": "$.pvp.enabled", + "value": "${PUMPKIN_PVP}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.resource_pack.java.enabled", + "value": "${PUMPKIN_RESOURCE_PACK_ENABLED}", + "value-type": "bool" + } + }, + { + "$set": { + "path": "$.resource_pack.java.url", + "value": "${RESOURCE_PACK}" + } + }, + { + "$set": { + "path": "$.resource_pack.java.sha1", + "value": "${RESOURCE_PACK_SHA1}" + } + }, + { + "$set": { + "path": "$.resource_pack.java.prompt_message", + "value": "${RESOURCE_PACK_PROMPT}" + } + }, + { + "$set": { + "path": "$.resource_pack.java.force", + "value": "${PUMPKIN_RESOURCE_PACK_ENFORCE}", + "value-type": "bool" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/files/pumpkin.toml b/files/pumpkin.toml new file mode 100644 index 00000000..c5a85e63 --- /dev/null +++ b/files/pumpkin.toml @@ -0,0 +1,52 @@ +seed = "" +default_difficulty = "Normal" +default_gamemode = "Survival" +op_permission_level = 4 +allow_nether = true +hardcore = false +force_gamemode = false +default_level_name = "world" +white_list = false +enforce_whitelist = false +accepts_transfers = true +spawn_protection = 16 + +[logging] +level = "info" + +[resource_pack.java] +enabled = false +url = "" +sha1 = "" +prompt_message = "" +force = false + +[networking.query] +enabled = false +address = "0.0.0.0:25565" + +[networking.rcon] +enabled = false +address = "0.0.0.0:25575" +password = "" + +[networking.java] +address = "0.0.0.0:25565" +online_mode = true +max_players = 1000 +view_distance = 16 +simulation_distance = 10 +motd = "A blazingly fast Pumpkin server!" + +[networking.java.authentication] +prevent_proxy_connections = false + +[networking.bedrock] +online_mode = true +max_players = 1000 +view_distance = 16 +simulation_distance = 10 +motd = "A blazingly fast Pumpkin server!" + +[pvp] +enabled = true diff --git a/scripts/start-configuration b/scripts/start-configuration index 48f47475..cc548c97 100755 --- a/scripts/start-configuration +++ b/scripts/start-configuration @@ -343,6 +343,10 @@ case "${TYPE^^}" in exec "$(dirname "$0")/start-deployNanoLimbo" "$@" ;; + PUMPKIN) + exec "$(dirname "$0")/start-deployPumpkin" "$@" + ;; + CRUCIBLE) exec "$(dirname "$0")/start-deployCrucible" "$@" ;; @@ -363,7 +367,7 @@ case "${TYPE^^}" in logError "Invalid TYPE: '$TYPE'" logError "Must be: VANILLA, FORGE, BUKKIT, SPIGOT, PAPER, FOLIA, PURPUR, FABRIC, QUILT," logError " SPONGEVANILLA, CUSTOM, MAGMA, MOHIST, GTNH, AIRPLANE, PUFFERFISH," - logError " CANYON, LIMBO, NANOLIMBO, CRUCIBLE, LEAF, YOUER, BANNER, NEOFORGE" + logError " CANYON, LIMBO, NANOLIMBO, PUMPKIN, CRUCIBLE, LEAF, YOUER, BANNER, NEOFORGE" exit 1 ;; diff --git a/scripts/start-deployPumpkin b/scripts/start-deployPumpkin new file mode 100755 index 00000000..485d59aa --- /dev/null +++ b/scripts/start-deployPumpkin @@ -0,0 +1,159 @@ +#!/bin/bash + +# shellcheck source=start-utils +. "${SCRIPTS:-$(dirname "$0")}/start-utils" +set -o pipefail +set -e +isDebugging && set -x + +# Pumpkin is released as a native executable per platform +case "$(uname -m)" in + x86_64 | amd64) + pumpkinArch=X64 + ;; + aarch64 | arm64) + pumpkinArch=ARM64 + ;; + *) + logError "Pumpkin does not provide a Linux build for $(uname -m)" + exit 1 + ;; +esac + +pumpkinAsset="pumpkin-${pumpkinArch}-Linux" +# The Alpine based image needs the musl linked build +if [[ $(getDistro) == alpine ]]; then + pumpkinAsset+="-musl" +fi + +if isTrue "${FORCE_REDOWNLOAD:-false}"; then + log "Forcing re-download of ${pumpkinAsset}" + rm -f "/data/${pumpkinAsset}" +fi + +if ! SERVER=$(mc-image-helper github download-latest-asset \ + --output-directory=/data \ + --name-pattern="^${pumpkinAsset}$" \ + Pumpkin-MC/Pumpkin + ); then + logError "Failed to download ${pumpkinAsset} from the latest Pumpkin release" + exit 1 +fi + +log "Using Pumpkin server executable ${SERVER}" +if [[ ! -x "${SERVER}" ]]; then + chmod +x "${SERVER}" +fi + +export SERVER +# Native executable, so the final exec skips the JVM handling +export FAMILY=NATIVE + +pumpkinConfig=/data/pumpkin.toml + +# Narrow booleans to the values that the patch bool conversion accepts +function exportBoolean() { + local target=$1 + local source=$2 + if [[ -v $source ]]; then + local value=false + if isTrue "${!source}"; then + value=true + fi + printf -v "$target" '%s' "$value" + export "$target" + fi +} + +function exportInteger() { + local target=$1 + local source=$2 + if [[ -v $source ]] && isNumeric "${!source}"; then + printf -v "$target" '%s' "${!source}" + export "$target" + fi +} + +function normalizeGamemode() { + case "${1,,}" in + 0 | su*) echo Survival ;; + 1 | c*) echo Creative ;; + 2 | a*) echo Adventure ;; + 3 | sp*) echo Spectator ;; + *) echo "$1" ;; + esac +} + +function normalizeDifficulty() { + case "${1,,}" in + 0 | p*) echo Peaceful ;; + 1 | e*) echo Easy ;; + 2 | n*) echo Normal ;; + 3 | h*) echo Hard ;; + *) echo "$1" ;; + esac +} + +exportBoolean PUMPKIN_ONLINE_MODE ONLINE_MODE +exportBoolean PUMPKIN_HARDCORE HARDCORE +exportBoolean PUMPKIN_ALLOW_NETHER ALLOW_NETHER +exportBoolean PUMPKIN_FORCE_GAMEMODE FORCE_GAMEMODE +exportBoolean PUMPKIN_ENFORCE_WHITELIST ENFORCE_WHITELIST +exportBoolean PUMPKIN_ACCEPTS_TRANSFERS ACCEPTS_TRANSFERS +exportBoolean PUMPKIN_PREVENT_PROXY_CONNECTIONS PREVENT_PROXY_CONNECTIONS +exportBoolean PUMPKIN_PVP PVP +exportBoolean PUMPKIN_ENABLE_RCON ENABLE_RCON +exportBoolean PUMPKIN_ENABLE_QUERY ENABLE_QUERY +exportBoolean PUMPKIN_RESOURCE_PACK_ENFORCE RESOURCE_PACK_ENFORCE + +exportInteger PUMPKIN_MAX_PLAYERS MAX_PLAYERS +exportInteger PUMPKIN_VIEW_DISTANCE VIEW_DISTANCE +exportInteger PUMPKIN_SIMULATION_DISTANCE SIMULATION_DISTANCE +exportInteger PUMPKIN_OP_PERMISSION_LEVEL OP_PERMISSION_LEVEL +exportInteger PUMPKIN_SPAWN_PROTECTION SPAWN_PROTECTION + +if [[ -v MODE ]]; then + PUMPKIN_GAMEMODE=$(normalizeGamemode "$MODE") + export PUMPKIN_GAMEMODE +fi + +if [[ -v DIFFICULTY ]]; then + PUMPKIN_DIFFICULTY=$(normalizeDifficulty "$DIFFICULTY") + export PUMPKIN_DIFFICULTY +fi + +# The image enables the whitelist whenever one is provided +if [[ -v WHITELIST || -v WHITELIST_FILE ]]; then + export PUMPKIN_WHITE_LIST=true +else + exportBoolean PUMPKIN_WHITE_LIST ENABLE_WHITELIST +fi + +if [[ -v RESOURCE_PACK ]]; then + export PUMPKIN_RESOURCE_PACK_ENABLED=true +fi + +# Only override the listening addresses when a port was declared +if [[ -v SERVER_PORT ]]; then + export PUMPKIN_JAVA_ADDRESS="0.0.0.0:${SERVER_PORT}" +fi +if [[ -v RCON_PORT ]]; then + export PUMPKIN_RCON_ADDRESS="0.0.0.0:${RCON_PORT}" +fi +if [[ -v QUERY_PORT ]]; then + export PUMPKIN_QUERY_ADDRESS="0.0.0.0:${QUERY_PORT}" +fi + +if [[ ! -f "$pumpkinConfig" ]]; then + log "Creating initial Pumpkin configuration" + cp /image/pumpkin.toml "$pumpkinConfig" +fi + +log "Applying Pumpkin configuration from environment variables" +# Undeclared variables only warn, so only surface failures +if ! pumpkinPatchResult=$(mc-image-helper patch --patch-env-prefix "" /image/pumpkin-config-patch.json 2>&1); then + logWarning "Failed to apply one or more settings to ${pumpkinConfig}" + echo "$pumpkinPatchResult" | applyLogPrefix +fi + +exec "$(dirname "$0")/start-setupWorld" "$@" diff --git a/scripts/start-finalExec b/scripts/start-finalExec index 6d1c4352..d4ad46c6 100755 --- a/scripts/start-finalExec +++ b/scripts/start-finalExec @@ -91,7 +91,9 @@ 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 +if isFamily NATIVE; then + : # Native server, so there is no log4j to patch +elif 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 @@ -111,7 +113,9 @@ if ${useFallbackJvmFlag}; then JVM_OPTS="-Dlog4j2.formatMsgNoLookups=true ${JVM_OPTS}" fi -if versionLessThan 1.7; then +if isFamily NATIVE; then + : # Native server, so no Log4jPatcher agent +elif versionLessThan 1.7; then : # No patch required here. elif versionLessThan 1.18.1; then if isTrue "${SKIP_LOG4J_PATCHER:-false}"; then @@ -123,7 +127,9 @@ fi # Set up log4j2 configuration with templating support LOGFILE="${SERVER_DIR}/log4j2.xml" -if ${useGeneratedLogs}; then +if isFamily NATIVE; then + : # Native server, so no log4j configuration +elif ${useGeneratedLogs}; then # Set up log configuration defaults : "${LOG_LEVEL:=info}" : "${ROLLING_LOG_MAX_FILES:=1000}" @@ -195,12 +201,12 @@ else fi # Optional disable console -if versionLessThan 1.14 && [[ ${CONSOLE,,} = false ]]; then +if ! isFamily NATIVE && versionLessThan 1.14 && [[ ${CONSOLE,,} = false ]]; then EXTRA_ARGS+=" --noconsole" fi # Optional disable GUI for headless servers -if [[ ${GUI,,} = false ]]; then +if ! isFamily NATIVE && [[ ${GUI,,} = false ]]; then EXTRA_ARGS+=" nogui" fi @@ -472,7 +478,10 @@ function copyFilesForCurseForge() { cp -f ${baseDataDir}/eula.txt "${FTB_DIR}/" } -if versionLessThan 'b1.8'; then +if isFamily NATIVE; then + # Native server, always probed with the modern server list ping + rm -f "$mcHealthEnvPath" +elif versionLessThan 'b1.8'; then echo " DISABLE_HEALTHCHECK=true " > "$mcHealthEnvPath" @@ -601,6 +610,29 @@ elif [[ $SERVER =~ run.sh ]]; then set -x fi exec ${SERVER_RUNNER} "${mcServerRunnerArgs[@]}" --shell bash -- "${SERVER}" $EXTRA_ARGS +elif isFamily NATIVE; then + # Native server, so no JVM arguments + if [[ ! -x "$SERVER" ]]; then + log "Making $SERVER executable" + if ! chmod +x "$SERVER"; then + logError "Failed to make $SERVER executable" + exit 1 + fi + fi + + if isTrue "${SETUP_ONLY}"; then + echo "SETUP_ONLY: ${SERVER}" + exit + fi + + runPreStartScript + if isTrue "${DEBUG_EXEC}"; then + set -x + fi + + log "Starting the Minecraft server..." + # shellcheck disable=SC2086 + exec ${SERVER_RUNNER} ${bootstrapArgs} "${mcServerRunnerArgs[@]}" -- "${SERVER}" "$@" $EXTRA_ARGS else # If we have a bootstrap.txt file... feed that in to the server stdin if [ -f $bootstrapPath ]; then diff --git a/tests/setuponlytests/pumpkin/docker-compose.yml b/tests/setuponlytests/pumpkin/docker-compose.yml new file mode 100644 index 00000000..525f104e --- /dev/null +++ b/tests/setuponlytests/pumpkin/docker-compose.yml @@ -0,0 +1,21 @@ +services: + mc: + restart: "no" + image: ${IMAGE_TO_TEST:-itzg/minecraft-server} + environment: + EULA: "TRUE" + SETUP_ONLY: "TRUE" + TYPE: PUMPKIN + MOTD: "Pumpkin test MOTD" + MAX_PLAYERS: "25" + ONLINE_MODE: "false" + MODE: "creative" + DIFFICULTY: "hard" + LEVEL: "myworld" + VIEW_DISTANCE: "8" + PVP: "false" + ENABLE_QUERY: "true" + QUERY_PORT: "25566" + RCON_PORT: "25576" + volumes: + - ./data:/data diff --git a/tests/setuponlytests/pumpkin/verify.sh b/tests/setuponlytests/pumpkin/verify.sh new file mode 100644 index 00000000..48011a23 --- /dev/null +++ b/tests/setuponlytests/pumpkin/verify.sh @@ -0,0 +1,29 @@ +mc-image-helper assert fileExists "/data/pumpkin-*-Linux*" + +pumpkinToml=/data/pumpkin.toml + +assertPathEquals() { + local path=$1 + local expected=$2 + local actual + actual=$(mc-image-helper toml-path --file "${pumpkinToml}" "${path}") + if [[ ${actual} != "${expected}" ]]; then + echo "Expected ${path} to be '${expected}', but was '${actual}'" + exit 1 + fi +} + +assertPathEquals '$.networking.java.motd' "Pumpkin test MOTD" +assertPathEquals '$.networking.bedrock.motd' "Pumpkin test MOTD" +assertPathEquals '$.networking.java.max_players' "25" +assertPathEquals '$.networking.bedrock.max_players' "25" +assertPathEquals '$.networking.java.online_mode' "false" +assertPathEquals '$.networking.bedrock.online_mode' "false" +assertPathEquals '$.networking.java.view_distance' "8" +assertPathEquals '$.default_gamemode' "Creative" +assertPathEquals '$.default_difficulty' "Hard" +assertPathEquals '$.default_level_name' "myworld" +assertPathEquals '$.pvp.enabled' "false" +assertPathEquals '$.networking.query.enabled' "true" +assertPathEquals '$.networking.query.address' "0.0.0.0:25566" +assertPathEquals '$.networking.rcon.address' "0.0.0.0:25576"