Add full image builds + build script

This commit is contained in:
Corey 2025-05-03 15:18:25 -04:00
parent 42ce91d8e5
commit 0617211e22
Signed by: coco
GPG Key ID: 051138DA6AEE3E60
12 changed files with 201 additions and 233 deletions

View File

@ -1,9 +0,0 @@
# Nothing tracked by LSB git should be in here.
# Keeps git hash clean during build.
server/build
server/log/*.log
server/losmeshes/**
server/navmeshes/**
server/settings/*.lua
server/sql/backups/*.sql
server/tools/config.yaml

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "server"]
path = server
url = https://github.com/landsandboat/server.git

View File

@ -3,60 +3,52 @@
<h1 align="center">LSB Server - Docker</h1>
</p>
<p align="center">Containerized version of <a href="https://github.com/LandSandBoat/server">LandSandBoat</a> server emulator.</p>
<p align="center">
Containerized version of <a href="https://github.com/LandSandBoat/server">LandSandBoat</a> server emulator.<br>
Pre-configured build and runtime environments for a fast and easy install.
</p>
Pre-configured build and runtime environments for a fast and easy install. Supports live editing on the host machine for a seamless experience.
## Using
## Usage
See `docker-compose.yml` for example usage.
`docker compose up --build --detach`
The following environment variables are required to connect to a database:
```
MARIADB_DATABASE or XI_NETWORK_SQL_DATABASE
MARIADB_USER or XI_NETWORK_SQL_LOGIN
MARIADB_PASSWORD or XI_NETWORK_SQL_PASSWORD
```
That's it! All the setup is handled for you. Check out the [official LSB documentation](https://github.com/LandSandBoat/server/wiki/Post-Install-Guide) for more information. Some adjustments will need to be made while working with [Docker](https://docs.docker.com/reference/). LSD specific information will be added here over time.
If you're setting up a database using the MariaDB image, one of `MARIADB_ROOT_PASSWORD_HASH`, `MARIADB_ROOT_PASSWORD`, `MARIADB_ALLOW_EMPTY_ROOT_PASSWORD`, or `MARIADB_RANDOM_ROOT_PASSWORD` (or equivalents, including `*_FILE`), is also required (along with the `MARIADB_` variants of the above variables).
You should create and mount an empty `config.yaml` file so that dbtool can use express updates when new images are pulled.
## Building
`./build.sh -t ubuntu`
This will build the latest LSB server image based on Ubuntu. Navmeshes and Losmeshes are not included in this image.
`./build.sh -t meshes`
This will build the latest navmeshes and losmeshes from their upstream repositories. This may not match the exact versions LSB uses (Eden maintains losmeshes). After building the meshes image, run the image with the following command:
`docker run --rm -v losmeshes:/losmeshes -v navmeshes:/navmeshes ximeshes:latest`
This will copy the losmeshes and navmeshes into a local volume that can be mounted into the server image. You can delete the ximeshes image after the volumes are created.
----
- `docker compose stop` / `docker compose start`
- Use these to stop/start the server when rebuilding isn't necessary.
- `docker compose build`
- Builds the image. With caching, this basically just rebuilds the executables if needed. Use `--no-cache` for a full clean build.
- `docker compose down`
- Shuts down and removes the containers.
----
## Updating
- `git submodule update --remote --merge`
- This will pull the latest changes from upstream LSB. Run `docker compose up --build --detach` again to rebuild and start/restart the containers.
- You can also just change to the server directory and treat it as its own repo (change branch, fork, apply patches, etc.)
## Alpine
Alpine Linux is a minimal image that comes in at about half the size of the Ubuntu image (excluding bind mounted files/directories). The Alpine build is experimental and unsupported by LSB. It uses a newer version of GCC and the [musl libc](https://wiki.musl-libc.org/functional-differences-from-glibc.html). Use at your own risk.
`docker compose -f ./docker-compose.yml -f ./docker-compose.alpine.yml up --build --detach`
Alpine Linux is a minimal image that is smaller than the Ubuntu image. The Alpine build is experimental and unsupported by LSB. It uses a newer version of GCC and the [musl libc](https://wiki.musl-libc.org/functional-differences-from-glibc.html). Use at your own risk.
## Notes
- The image is built with a default user and group named `xiadmin` with UID and GID `1000`.
- You can supply the build args `UNAME`, `UGROUP`, `UID`, `GID` to change these.
- `docker compose build --build-arg UID="$(id -u)" --build-arg GID="$(id -g)" --build-arg UNAME="$(whoami)"`
- If using WSL with Windows, make sure [the project is stored in the WSL file system](https://learn.microsoft.com/en-us/windows/wsl/filesystems#file-storage-and-performance-across-file-systems) for best performance.
- You may experience issues related to the system clock. Using `sudo hwclock --hctosys` in WSL may help.
- Secure database passwords and a default user and database name are generated into `config/.env` to simplify the setup process.
- **Optional** - You can manually create this _before_ building the containers with these variables:
```
MARIADB_DATABASE=
MARIADB_USER=
MARIADB_PASSWORD=
MARIADB_ROOT_PASSWORD=
```
Otherwise, the default user is `xiadmin`, the default database is `xidb`, and the password is randomly generated.
- If `config/.env` is automatically generated, a random initial password for the root user will be generated and printed to stdout (check your build log!)
- Because environment variables are used, the server settings `SQL_LOGIN`, `SQL_PASSWORD`, and `SQL_DATABASE` (all located in network.lua) are not used.
- Because LSB uses Git during the build and update process, changes to the Git metadata will trigger an image rebuild, but with caching this shouldn't be significant.
- `docker build --build-arg UID="$(id -u)" --build-arg GID="$(id -g)" --build-arg UNAME="$(whoami)" -f docker/ubuntu.Dockerfile .`
- `build.sh` has options to build a specific origin/branch other than LandSandBoat/base.
----

53
build.sh Executable file
View File

@ -0,0 +1,53 @@
#!/bin/bash
Help()
{
echo "Builds LSB images."
echo
echo "Syntax: build.sh [-t ubuntu|alpine|meshes] [-o <origin>] [-b <branch>]"
echo "options:"
echo "t Target image to build."
echo "o Source origin. (default: LandSandBoat)"
echo "b Source branch. (default: base)"
echo
}
TARGET=""
ORIGIN="LandSandBoat"
BRANCH="base"
TAG="latest"
while getopts ":t:o:b:" option; do
case $option in
t)
TARGET=$OPTARG
;;
o)
ORIGIN=$OPTARG
;;
b)
BRANCH=$OPTARG
TAG=$BRANCH
;;
\?)
Help
exit
;;
esac
done
if [[ -n $TARGET ]]; then
if [[ $TARGET == "meshes" ]]; then
TAG="ximeshes:latest"
else
if [[ $TARGET == "alpine" ]]; then
TAG="$TAG-alpine"
fi
TAG=$(echo "$ORIGIN-server:$TAG" | tr '[:upper:]' '[:lower:]')
fi
echo "Building $TAG..."
docker build -t $TAG --build-arg ORIGIN=$ORIGIN --build-arg BRANCH=$BRANCH -f docker/$TARGET.Dockerfile .
else
Help
fi

View File

@ -1,18 +0,0 @@
services:
dbtool:
image: lsd_service:alpine
build:
context: .
dockerfile: docker/alpine.Dockerfile
connect:
image: lsd_service:alpine
search:
image: lsd_service:alpine
world:
image: lsd_service:alpine
map:
image: lsd_service:alpine

View File

@ -1,32 +1,22 @@
x-common: &common # Configs used by more than one service.
image: lsd_service:ubuntu
pull_policy: never
x-common: &common
image: landsandboat-server:latest
env_file: ./config/.env
environment:
XI_NETWORK_HTTP_HOST: 0.0.0.0
XI_NETWORK_ZMQ_IP: world
XI_NETWORK_SQL_HOST: database
# Add settings here or in .env: XI_{file}_{setting}
volumes:
- ./.git:/.git
- ./config:/config
- ./server/.git:/server/.git
- ./server/log:/server/log
- ./server/losmeshes:/server/losmeshes
- ./server/modules:/server/modules
- ./server/navmeshes:/server/navmeshes
- ./server/scripts:/server/scripts
- ./server/settings:/server/settings
- ./server/sql:/server/sql
- ./server/tools:/server/tools
- ./config/config.yaml:/server/tools/config.yaml # Enables dbtool express updates. Create this (empty) file on the host before running.
- losmeshes:/server/losmeshes # Initialize before running:
- navmeshes:/server/navmeshes # docker run --rm -v losmeshes:/losmeshes -v navmeshes:/navmeshes ximeshes:latest
services:
database:
image: mariadb
entrypoint: ["/mariadb-entrypoint.sh"]
command: ["mariadbd"]
restart: always
env_file: ./config/.env
volumes:
- ./config:/config
- ./mariadb-entrypoint.sh:/mariadb-entrypoint.sh # Custom entrypoint to generate .env.
- database:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
@ -35,13 +25,9 @@ services:
timeout: 5s
retries: 3
dbtool:
<<: *common # Import common configs.
build:
context: .
dockerfile: docker/ubuntu.Dockerfile # This service builds the image shared by all LSB services.
database-update:
<<: *common
command: ["python3", "/server/tools/dbtool.py", "update"]
restart: no
depends_on:
database:
condition: service_healthy
@ -58,7 +44,7 @@ services:
database:
condition: service_healthy
restart: true
dbtool:
database-update:
condition: service_completed_successfully
search:
@ -71,7 +57,7 @@ services:
database:
condition: service_healthy
restart: true
dbtool:
database-update:
condition: service_completed_successfully
world:
@ -84,7 +70,7 @@ services:
database:
condition: service_healthy
restart: true
dbtool:
database-update:
condition: service_completed_successfully
map:
@ -97,10 +83,14 @@ services:
database:
condition: service_healthy
restart: true
dbtool:
database-update:
condition: service_completed_successfully
world:
condition: service_started
volumes:
database:
losmeshes:
external: true
navmeshes:
external: true

View File

@ -1,19 +1,9 @@
# syntax=docker/dockerfile:1-labs
##############
# Base stage #
##############
########
# Base #
########
FROM alpine:latest AS base
ARG UNAME=xiadmin
ARG UGROUP=xiadmin
ARG UID=1000
ARG GID=1000
RUN addgroup --gid $GID $UGROUP && \
adduser --uid $UID $UNAME --ingroup $UGROUP --home /xiadmin --disabled-password
# Install runtime dependencies at the base level.
# Install runtime dependencies.
RUN --mount=type=cache,target=/var/cache/apk,sharing=locked \
apk --update-cache add \
binutils \
@ -30,9 +20,17 @@ RUN --mount=type=cache,target=/var/cache/apk,sharing=locked \
RUN git config --system --add safe.directory /server
ENV PATH=/xiadmin/.local/bin:$PATH
###############
# Build stage #
###############
ARG UNAME=xiadmin
ARG UGROUP=xiadmin
ARG UID=1000
ARG GID=1000
RUN addgroup --gid $GID $UGROUP && \
adduser --uid $UID $UNAME --ingroup $UGROUP --home /xiadmin --disabled-password
#########
# Build #
#########
FROM base AS build
# Install build dependencies.
@ -55,16 +53,13 @@ RUN apk --update-cache add \
USER $UNAME
WORKDIR /server
# Install Python dependencies here, copied into runtime stage.
RUN --mount=type=bind,source=server/tools/requirements.txt,target=/tmp/requirements.txt \
--mount=type=cache,target=/xiadmin/.cache/pip,id=pip-alpine,uid=$UID,gid=$GID \
pip3 install --break-system-packages --user --ignore-installed --requirement /tmp/requirements.txt
ARG ORIGIN='LandSandBoat'
ARG BRANCH='base'
# Exclude changes to git metadata, scripts, and sql not needed for build.
# Excluded here instead of dockerignore so they can be bind mounted during build.
# Saves from copying everything whenever scripts/sql change.
# https://docs.docker.com/reference/dockerfile/#copy---exclude (docker/dockerfile:1.7-labs)
COPY --chown=$UNAME:$UGROUP --exclude=.git --exclude=scripts --exclude=sql server /server
# Download the latest release. We don't actually need the tarball but it helps with caching.
ADD --chown=$UNAME:$UGROUP https://api.github.com/repos/$ORIGIN/server/tarball/$BRANCH /server
RUN rm /server/$BRANCH && \
git clone --filter=tree:0 --branch=$BRANCH https://github.com/$ORIGIN/server.git /server
# --- PATCH LSB ---
RUN LSB_FILE="/server/cmake/FindMariaDBCPP.cmake" && \
@ -79,14 +74,13 @@ RUN LSB_FILE="/server/cmake/FindMariaDBCPP.cmake" && \
fi;
# --- End Patch ---
# Cache the build. Bind mounts to save copy time and keep clean git hash.
# Install Python dependencies.
RUN --mount=type=cache,target=/xiadmin/.cache/pip,id=$ORIGIN-$BRANCH-pip-alpine,uid=$UID,gid=$GID \
pip3 install --break-system-packages --user --ignore-installed --requirement /server/tools/requirements.txt
ENV CCACHE_DIR=/xiadmin/.ccache
RUN --mount=type=cache,target=/xiadmin/build,id=build-alpine,uid=$UID,gid=$GID \
--mount=type=cache,target=/xiadmin/.ccache,id=ccache-alpine,uid=$UID,gid=$GID \
--mount=type=bind,source=.git,target=/.git \
--mount=type=bind,source=server/.git,target=/server/.git \
--mount=type=bind,source=server/scripts,target=/server/scripts \
--mount=type=bind,source=server/sql,target=/server/sql \
RUN --mount=type=cache,target=/xiadmin/build,id=$ORIGIN-$BRANCH-build-alpine,uid=$UID,gid=$GID \
--mount=type=cache,target=/xiadmin/.ccache,id=$ORIGIN-$BRANCH-ccache-alpine,uid=$UID,gid=$GID \
# --- CACHE ---
cp -p /xiadmin/build/version.cpp /server/src/common/ 2> /dev/null; \
cp -p /xiadmin/build/xi_* /server/ 2> /dev/null; \
@ -115,19 +109,24 @@ RUN --mount=type=cache,target=/xiadmin/build,id=build-alpine,uid=$UID,gid=$GID \
cp -p /server/src/common/version.cpp /xiadmin/build/
# --- End ---
#################
# Runtime stage #
#################
###########
# Service #
###########
FROM base AS service
USER $UNAME
WORKDIR /server
COPY server/res/compress.dat server/res/decompress.dat /server/res/
# Copy installed Python dependencies and built executables from build stage.
COPY --from=build /xiadmin/.local /xiadmin/.local
COPY --from=build /server/xi_* /server/
COPY --chown=$UNAME:$UGROUP --from=build /xiadmin/.local /xiadmin/.local
COPY --chown=$UNAME:$UGROUP --from=build /server/res/compress.dat /server/res/decompress.dat /server/res/
COPY --chown=$UNAME:$UGROUP --from=build /server/scripts /server/scripts
COPY --chown=$UNAME:$UGROUP --from=build /server/sql /server/sql
COPY --chown=$UNAME:$UGROUP --from=build /server/tools /server/tools
COPY --chown=$UNAME:$UGROUP --from=build /server/modules /server/modules
COPY --chown=$UNAME:$UGROUP --from=build /server/settings /server/settings
COPY --chown=$UNAME:$UGROUP --from=build /server/.git /server/.git
COPY --chown=$UNAME:$UGROUP --from=build /server/xi_* /server/
COPY --chmod=0755 entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/bin/ash"]

6
docker/meshes.Dockerfile Normal file
View File

@ -0,0 +1,6 @@
FROM busybox:latest
ADD https://github.com/EdenServer/losmeshes.git /losmeshes
ADD https://github.com/LandSandBoat/xiNavmeshes.git /navmeshes
VOLUME /navmeshes /losmeshes

View File

@ -1,25 +1,14 @@
# syntax=docker/dockerfile:1-labs
##############
# Base stage #
##############
########
# Base #
########
FROM ubuntu:latest AS base
ARG UNAME=xiadmin
ARG UGROUP=xiadmin
ARG UID=1000
ARG GID=1000
ARG DEBIAN_FRONTEND=noninteractive
RUN rm -f /etc/apt/apt.conf.d/docker-clean && \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' \
> /etc/apt/apt.conf.d/keep-cache
RUN userdel --remove ubuntu && \
groupadd --gid $GID $UNAME && \
useradd --uid $UID $UNAME --gid $UGROUP --home-dir /xiadmin --create-home --skel /dev/null
# Install runtime dependencies at the base level.
# Install runtime dependencies.
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update && apt-get install --assume-yes --no-install-recommends --quiet \
@ -36,9 +25,18 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
RUN git config --system --add safe.directory /server
ENV PATH=/xiadmin/.local/bin:$PATH
###############
# Build stage #
###############
ARG UNAME=xiadmin
ARG UGROUP=xiadmin
ARG UID=1000
ARG GID=1000
RUN userdel --remove ubuntu && \
groupadd --gid $GID $UNAME && \
useradd --uid $UID $UNAME --gid $UGROUP --home-dir /xiadmin --create-home
#########
# Build #
#########
FROM base AS build
# Install build dependencies.
@ -47,7 +45,7 @@ RUN apt-get update && apt-get install --assume-yes --no-install-recommends --qui
build-essential \
ccache \
cmake \
g++ \
g++-14 \
libluajit-5.1-dev \
libmariadb-dev-compat \
libssl-dev \
@ -61,25 +59,23 @@ RUN apt-get update && apt-get install --assume-yes --no-install-recommends --qui
USER $UNAME
WORKDIR /server
# Install Python dependencies here, copied into runtime stage.
RUN --mount=type=bind,source=server/tools/requirements.txt,target=/tmp/requirements.txt \
--mount=type=cache,target=/xiadmin/.cache/pip,uid=$UID,gid=$GID \
pip3 install --break-system-packages --user --ignore-installed --requirement /tmp/requirements.txt
ARG ORIGIN='LandSandBoat'
ARG BRANCH='base'
# Exclude changes to git metadata, scripts, and sql not needed for build.
# Excluded here instead of dockerignore so they can be bind mounted during build.
# Saves from copying everything whenever scripts/sql change.
# https://docs.docker.com/reference/dockerfile/#copy---exclude (docker/dockerfile:1.7-labs)
COPY --chown=$UNAME:$UGROUP --exclude=.git --exclude=scripts --exclude=sql server /server
# Download the latest release. We don't actually need the tarball but it helps with caching.
ADD --chown=$UNAME:$UGROUP https://api.github.com/repos/$ORIGIN/server/tarball/$BRANCH /server
RUN rm /server/$BRANCH && \
git clone --filter=tree:0 --branch=$BRANCH https://github.com/$ORIGIN/server.git /server
# Cache the build. Bind mounts to save copy time and keep clean git hash.
# Install Python dependencies.
RUN --mount=type=cache,target=/xiadmin/.cache/pip,id=$ORIGIN-$BRANCH-pip,uid=$UID,gid=$GID \
pip3 install --break-system-packages --user --ignore-installed --requirement /server/tools/requirements.txt
ENV CC=/usr/bin/gcc-14
ENV CXX=/usr/bin/g++-14
ENV CCACHE_DIR=/xiadmin/.ccache
RUN --mount=type=cache,target=/xiadmin/build,uid=$UID,gid=$GID \
--mount=type=cache,target=/xiadmin/.ccache,uid=$UID,gid=$GID \
--mount=type=bind,source=.git,target=/.git \
--mount=type=bind,source=server/.git,target=/server/.git \
--mount=type=bind,source=server/scripts,target=/server/scripts \
--mount=type=bind,source=server/sql,target=/server/sql \
RUN --mount=type=cache,target=/xiadmin/build,id=$ORIGIN-$BRANCH-build,uid=$UID,gid=$GID \
--mount=type=cache,target=/xiadmin/.ccache,id=$ORIGIN-$BRANCH-ccache,uid=$UID,gid=$GID \
# --- CACHE ---
cp -p /xiadmin/build/version.cpp /server/src/common/ 2> /dev/null; \
cp -p /xiadmin/build/xi_* /server/ 2> /dev/null; \
@ -91,19 +87,24 @@ RUN --mount=type=cache,target=/xiadmin/build,uid=$UID,gid=$GID \
cp -p /server/src/common/version.cpp /xiadmin/build/
# --- End ---
#################
# Runtime stage #
#################
###########
# Service #
###########
FROM base AS service
USER $UNAME
WORKDIR /server
COPY server/res/compress.dat server/res/decompress.dat /server/res/
# Copy installed Python dependencies and built executables from build stage.
COPY --from=build /xiadmin/.local /xiadmin/.local
COPY --from=build /server/xi_* /server/
COPY --chown=$UNAME:$UGROUP --from=build /xiadmin/.local /xiadmin/.local
COPY --chown=$UNAME:$UGROUP --from=build /server/res/compress.dat /server/res/decompress.dat /server/res/
COPY --chown=$UNAME:$UGROUP --from=build /server/scripts /server/scripts
COPY --chown=$UNAME:$UGROUP --from=build /server/sql /server/sql
COPY --chown=$UNAME:$UGROUP --from=build /server/tools /server/tools
COPY --chown=$UNAME:$UGROUP --from=build /server/modules /server/modules
COPY --chown=$UNAME:$UGROUP --from=build /server/settings /server/settings
COPY --chown=$UNAME:$UGROUP --from=build /server/.git /server/.git
COPY --chown=$UNAME:$UGROUP --from=build /server/xi_* /server/
COPY --chmod=0755 entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/bin/bash"]

View File

@ -1,18 +1,14 @@
#!/bin/sh
set -e
echo "Loading configuration from /config/.env..."
if [ -f "/config/.env" ]; then
set -a
. "/config/.env"
set +a
unset MARIADB_ROOT_PASSWORD
else
echo "Warning: /config/.env not found."
fi
export XI_NETWORK_SQL_PASSWORD="$MARIADB_PASSWORD" && unset MARIADB_PASSWORD
export XI_NETWORK_SQL_LOGIN="$MARIADB_USER" && unset MARIADB_USER
export XI_NETWORK_SQL_DATABASE="$MARIADB_DATABASE" && unset MARIADB_DATABASE
: ${XI_NETWORK_SQL_PASSWORD:="$MARIADB_PASSWORD"} && unset MARIADB_PASSWORD
: ${XI_NETWORK_SQL_LOGIN:="$MARIADB_USER"} && unset MARIADB_USER
: ${XI_NETWORK_SQL_DATABASE:="$MARIADB_DATABASE"} && unset MARIADB_DATABASE
export XI_NETWORK_SQL_PASSWORD
export XI_NETWORK_SQL_LOGIN
export XI_NETWORK_SQL_DATABASE
exec "$@"

View File

@ -1,38 +0,0 @@
#!/bin/bash
set -e
ENV_FILE="/config/.env"
echo "Running custom entrypoint..."
# Set default values if the .env file doesn't exist.
if [ ! -e "$ENV_FILE" ]; then
echo "$ENV_FILE not found, generating one..."
MARIADB_DATABASE=xidb
MARIADB_USER=xiadmin
# Generate random passwords. (MARIADB_RANDOM_ROOT_PASSWORD)
# https://github.com/MariaDB/mariadb-docker/blob/87a043a031e8c56ba66a0ad06e633417ae75ee1e/docker-entrypoint.sh#L378-L382
MARIADB_PASSWORD=$(pwgen --numerals --capitalize --symbols --remove-chars="'\\" -1 32)
export MARIADB_RANDOM_ROOT_PASSWORD=1
# Write the variables to the .env file.
cat <<-EOF > "$ENV_FILE"
MARIADB_DATABASE=$MARIADB_DATABASE
MARIADB_USER=$MARIADB_USER
MARIADB_PASSWORD='$MARIADB_PASSWORD'
EOF
chown mysql:mysql "$ENV_FILE"
fi
# Load environment variables.
if [ -f "$ENV_FILE" ]; then
echo "Loading environment variables from $ENV_FILE..."
set -a # Automatically export all sourced variables.
source "$ENV_FILE"
set +a
fi
# Execute the original MariaDB entrypoint.
echo "Handing over to official MariaDB entrypoint..."
exec /usr/local/bin/docker-entrypoint.sh "$@"

1
server

@ -1 +0,0 @@
Subproject commit f283f72fdbf83f718b250032411a0bb0430ec775