Skip to content

Commit

Permalink
feat(server): implement bedrock server type
Browse files Browse the repository at this point in the history
- Adds [bedrock](https://minecraft.gamepedia.com/Bedrock_Edition) server support for both CLI and lib
- Adds file preset for bedrock's `server.properties` format
- Adds integration suites for installing, configuring, and running bedrock servers
  • Loading branch information
loksonarius authored Jan 18, 2021
1 parent c43f267 commit b12828c
Show file tree
Hide file tree
Showing 20 changed files with 628 additions and 36 deletions.
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# mcsm - Minecraft Server Manager

:warning: *This project is too early in development to be of any practical use to
anyone* :warning:

![run-tests](https://github.com/loksonarius/mcsm/workflows/run-tests/badge.svg)
![check-commit](https://github.com/loksonarius/mcsm/workflows/check-commit/badge.svg)

Expand Down Expand Up @@ -55,7 +52,7 @@ install:
# source local plugins as well
- "/plugins/world-edit.jar"

run:
run: # configure memory consumption for Java-based servers
initialmemory: 8G
maxmemory: 8G

Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module github.com/loksonarius/mcsm

go 1.15

require gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
require (
golang.org/x/net v0.0.0-20201224014010-6772e930b67b
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
Expand Down
17 changes: 10 additions & 7 deletions integration/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
FROM alpine:3.12
FROM ubuntu:20.04

RUN apk -U --no-cache upgrade && \
apk -U --no-cache add \
bash \
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y \
curl \
jq \
openjdk11 \
openssl \
openjdk-11-jre \
unzip \
vim \
wget
wget \
&& \
apt-get clean &&\
rm -rf /var/lib/apt/lists/*

RUN mkdir /tests
WORKDIR /tests
Expand Down
9 changes: 9 additions & 0 deletions integration/suites/bedrock-config.suite/server.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
install:
kind: bedrock
version: "latest"

configs:
bedrock:
gamemode: creative
player-movement-distance-threshold: 0.9
6 changes: 6 additions & 0 deletions integration/suites/bedrock-config.suite/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# setup.sh

set -e

mcsm config > server.config
18 changes: 18 additions & 0 deletions integration/suites/bedrock-config.suite/verify.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# verify.sh

if [[ "$(cat server.config | jq -r .Install.Kind)" != "bedrock" ]]; then
echo "expected install kind to be bedrock" && exit 1
fi

if [[ "$(cat server.config | jq -r .Configs.vanilla)" != "null" ]]; then
echo "expected vanilla config to be null" && exit 1
fi

if [[ "$(cat server.config | jq -r .Configs.bedrock.gamemode)" != "creative" ]]; then
echo "expected gamemode to be creative" && exit 1
fi

if [[ "$(cat server.config | jq -r '.Configs.bedrock."player-movement-distance-threshold"')" != "0.9" ]]; then
echo "expected player movement distance threshold to be 0.9" && exit 1
fi
14 changes: 14 additions & 0 deletions integration/suites/bedrock-install.suite/server.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
install:
kind: bedrock
version: "latest"

run:
initialmemory: 1G
maxmemory: 1G

configs:
eula:
vanilla:
spigot:
bukkit:
6 changes: 6 additions & 0 deletions integration/suites/bedrock-install.suite/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# setup.sh

set -e

mcsm install server.yaml
6 changes: 6 additions & 0 deletions integration/suites/bedrock-install.suite/verify.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
# verify.sh

if [[ ! -f "bedrock_server" ]]; then
echo "expected bedrock_server to be present" && exit 1
fi
13 changes: 13 additions & 0 deletions integration/suites/bedrock-run.suite/server.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
install:
kind: bedrock
version: "latest"

run:
initialmemory: 1G
maxmemory: 1G

configs:
bedrock:
gamemode: creative
level-name: test-world
22 changes: 22 additions & 0 deletions integration/suites/bedrock-run.suite/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# setup.sh

set -e

mcsm install server.yaml

function runAndStopServer {
SERVER_STARTUP_DONE_MESSAGE='Server started.'
mcsm run server.yaml 2>&1 > server.out &
PID="${!}"
while ! grep "${SERVER_STARTUP_DONE_MESSAGE}" server.out; do
sleep 3
echo "Waiting for server to finish startup"
done

echo "Server startup complete -- stopping now"
kill "${PID}" && sleep 2
}
export -f runAndStopServer

timeout 90 bash -c runAndStopServer
39 changes: 39 additions & 0 deletions integration/suites/bedrock-run.suite/verify.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# verify.sh

EXPECTED_DIRS="\
resource_packs
minecraftpe
worlds
"

for d in ${EXPECTED_DIRS}; do
if [[ ! -d "${d}" ]]; then
echo "expected '${d}' to be present" && exit 1
fi
done

EXPECTED_FILES="\
server.properties
bedrock_server
whitelist.json
permissions.json
"

for f in ${EXPECTED_FILES}; do
if [[ ! -f "${f}" ]]; then
echo "expected '${f}' to be present" && exit 3
fi
done

if ! grep 'Difficulty: 1 EASY' server.out; then
echo 'Expected easy difficulty setting log' && exit 4
fi

if ! grep 'Game mode: 1 Creative' server.out; then
echo 'Expected creative game mode setting log' && exit 5
fi

if ! grep 'Level Name: test-world' server.out; then
echo 'Expected test-level level name setting log' && exit 6
fi
3 changes: 0 additions & 3 deletions integration/suites/paper-config.suite/verify.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
#!/usr/bin/env bash
# verify.sh

#!/usr/bin/env bash
# verify.sh

if [[ "$(cat server.config | jq -r .Install.Kind)" != "paper" ]]; then
echo "expected install kind to be vanilla" && exit 1
fi
Expand Down
1 change: 0 additions & 1 deletion integration/suites/paper-install.suite/verify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,3 @@ for f in ${EXPECTED_FILES}; do
echo "expected '${f}' to be present" && exit 2
fi
done

118 changes: 118 additions & 0 deletions pkg/config/presets/bedrock_server_properties.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package presets

import (
"fmt"
"io/ioutil"
"path/filepath"

"github.com/loksonarius/mcsm/pkg/config"
"github.com/loksonarius/mcsm/pkg/config/properties"
)

type BedrockServerProperties struct {
ServerName string `properties:"key:server-name,default:Dedicated Server"`
Gamemode string `properties:"key:gamemode,default:survival"`
Difficulty string `properties:"key:difficulty,default:easy"`
AllowCheats bool `properties:"key:allow-cheats,default:false"`
MaxPlayers uint `properties:"key:max-players,default:10"`
OnlineMode bool `properties:"key:online-mode,default:true"`
Whitelist bool `properties:"key:white-list,default:false"`
ServerPort uint `properties:"key:server-port,default:19132"`
ServerPortV6 uint `properties:"key:server-portv6,default:19133"`
ViewDistance uint `properties:"key:view-distance,default:32"`
TickDistance uint `properties:"key:tick-distance,default:4"`
PlayerIdleTimeout uint `properties:"key:player-idle-timeout,default:30"`
MaxThreads uint `properties:"key:max-threads,default:8"`
LevelName string `properties:"key:level-name,default:Bedrock level"`
LevelSeed string `properties:"key:level-seed,default:"`
DefaultPlayerPermissionLevel string `properties:"key:default-player-permission-level,default:member"`
TexturepackRequired bool `properties:"key:texturepack-required,default:false"`
ContentLogFileEnabled bool `properties:"key:content-log-file-enabled,default:false"`
CompressionThreshold uint `properties:"key:compression-threshold,default:1"`
ServerAuthoritativeMovement string `properties:"key:server-authoritative-movement,default:server-auth"`
PlayerMovementScoreThreshold uint `properties:"key:player-movement-score-threshold,default:20"`
PlayerMovementDistanceThreshold float64 `properties:"key:player-movement-distance-threshold,default:0.3"`
PlayerMovementDurationThresholdInMs uint `properties:"key:player-movement-duration-threshold-in-ms,default:500"`
CorrectPlayerMovement bool `properties:"key:correct-player-movement,default:false"`
}

func BedrockServerPropertiesFromConfig(configs map[string]config.ConfigDict) config.ConfigFile {
cfg := config.ConfigDict{}
if c, ok := configs["bedrock"]; ok {
cfg = c
}

var s BedrockServerProperties
properties.Unmarshal(cfg, &s)
return &s
}

func (p *BedrockServerProperties) Path() string {
return "server.properties"
}

func (p *BedrockServerProperties) Validate() error {
e := func(s string, v ...interface{}) error {
return fmt.Errorf(s, v...)
}

switch gm := p.Gamemode; gm {
case "survival", "creative", "adventure":
default:
return e("gamemode %s not valid", gm)
}

switch d := p.Difficulty; d {
case "peaceful", "easy", "normal", "hard":
default:
return e("difficulty %s not valid", d)
}

ports := []struct {
n string
v uint
}{
{"server-port", p.ServerPort},
{"server-portv6", p.ServerPortV6},
}
for _, p := range ports {
if p.v < 1 || p.v > 65535 {
return e("port %s not in range [1,65535]", p.n)
}
}

if p.TickDistance < 4 || p.TickDistance > 12 {
return e("tick distance outside of range [4,12]")
}

if p.CompressionThreshold > 65535 {
return e("compression threshold outside of range [0,65535]")
}

switch pl := p.DefaultPlayerPermissionLevel; pl {
case "visitor", "member", "operator":
default:
return e("default player permission level %s not valid", pl)
}

switch sam := p.ServerAuthoritativeMovement; sam {
case "client-auth", "server-auth":
default:
return e("server auth movement %s not valid", sam)
}

return nil
}

func (p *BedrockServerProperties) Render() []byte {
return properties.Marshal(p)
}

func (p *BedrockServerProperties) Write() error {
path, err := filepath.Abs(p.Path())
if err != nil {
return err
}

return ioutil.WriteFile(path, p.Render(), 0644)
}
14 changes: 14 additions & 0 deletions pkg/config/properties/properties.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ func Marshal(i interface{}) []byte {
case reflect.Uint, reflect.Uint8, reflect.Uint16,
reflect.Uint32, reflect.Uint64:
value = fmt.Sprintf("%d", field.Uint())
case reflect.Float32, reflect.Float64:
value = fmt.Sprintf("%.4f", field.Float())
case reflect.Bool:
value = fmt.Sprintf("%t", field.Bool())
default:
Expand Down Expand Up @@ -116,6 +118,18 @@ func Unmarshal(dict config.ConfigDict, target interface{}) {
default:
field.SetUint(uint64(value.(uint)))
}
case reflect.Float32, reflect.Float64:
if !valueFound {
if v, err := strconv.ParseFloat(def, 64); err == nil {
value = v
}
}
switch value.(type) {
case float64:
field.SetFloat(float64(value.(float64)))
default:
field.SetFloat(float64(value.(float32)))
}
case reflect.Bool:
if !valueFound {
if v, err := strconv.ParseBool(def); err == nil {
Expand Down
Loading

0 comments on commit b12828c

Please sign in to comment.