## Configuration

### Locations

Minecraft Java configuration: `$XDG_CONFIG_HOME/net.minecraft.JavaServer/minecraft`

Minecraft Fabric configuration: `$XDG_CONFIG_HOME/net.fabricmc.FabricServer/minecraft`

Minecraft Bedrock configuration: `$XDG_CONFIG_HOME/net.minecraft.BedrockServer/minecraft`

Geyser and MCXboxBroadcast read their configuration relative to the working
directory from which their wrapper jar is launched.

Minecraft Mojang binaries: `$XDG_DATA_HOME/net.minecraft.JavaServer`
and `$XDG_DATA_HOME/net.minecraft.BedrockServer`

Fabric binaries: `$XDG_DATA_HOME/net.fabricmc.FabricServer`

Geyser binaries: `$XDG_DATA_HOME/org.geysermc.GeyserStandalone`

MCXboxBroadcast binaries: `$XDG_DATA_HOME/com.rtm516.MCXboxBroadcastStandalone`

systemd user service standard input: `$XDG_RUNTIME_DIR/minecraft-server-java.stdin`
and `$XDG_RUNTIME_DIR/minecraft-server-fabric.stdin`
and `$XDG_RUNTIME_DIR/minecraft-server-bedrock.stdin`

### Environment Variables

`$MINECRAFT_SERVER_WORKING_DIRECTORY`

>>>
Sets the working directory used by the Minecraft server executable. Relative paths are resolved
from the wrapper's current directory. When unset, each package uses its XDG configuration
directory. Set it to `"."` to use the directory from which the wrapper was launched.
>>>

### systemd Service Configuration

To override the installed service use this command:

```shell
systemctl --user edit --full minecraft-server-java
```

or

```shell
systemctl --user edit --full minecraft-server-fabric
```

or

```shell
systemctl --user edit --full minecraft-server-bedrock
```

### Managing The systemd Service

See [with systemd](#with-systemd)

## Version Interpretation

Given the version format `x.y.z` used for the project. x is reserved for major changes in
the future. y increments for any changes made other than Minecraft version changes. z increments
whenever there is a Minecraft version change.

## Issue Reporting

Please [create an issue](https://gitlab.com/kaidelorenzo/minecraft-server-rs/-/issues/new). They
will be looked at and quickly fixed.

## Contribution

Contributions are welcome. However please run large changes through review by reaching out
first to make sure that it aligns with the vision of the project.

Only [signed commits](https://docs.gitlab.com/ee/user/project/repository/signed_commits/)
are allowed. Pull requests with unsigned commits will not be merged.

The project is written and maintained by me [Kai DeLorenzo](https://gitlab.com/kaidelorenzo),
so don't be intimidated and if there are things that you imagine would be better if different
then give a holler.

### Building

The local test entry points are:

```shell
jar-launchers/gradlew --gradle-user-home .gradle-user-home --project-dir jar-launchers test
cargo test --workspace
cargo test --package minecraft-server --test cli -- --ignored
```

The last command downloads and launches the locked upstream Minecraft Bedrock server and therefore
requires network access. The first two are the default test suites and do not download upstream
server binaries. Gradle stores its distribution and dependency cache in the ignored
`.gradle-user-home/` directory and may resolve those dependencies on the first run.

See [CI Design](docs/ci-design.md) for the pipeline architecture, cache strategy,
and related GitLab repository configuration.
See [Dependency Maintenance](docs/dependency-management.md) for the stable-version
policy and the commands used to update Rust, Kotlin, Gradle, and the Gradle Wrapper.

>>>
**NOTE**: It's recomended to do development and building in a container like
[distrobox](https://distrobox.it/) to isolate development from the host system and prevent
the need to clear clutter later. If only building test RPMs then using
[Mock](https://rpm-software-management.github.io/mock/) is a great way to isolate the
installation of build dependenceies. Copr has instructions for
[replicating builds locally](https://docs.pagure.org/copr.copr/user_documentation/reproducing_builds.html).
>>>

1.  Install development dependencies

    ```shell
    dnf install cargo java-latest-openjdk-headless openssl-devel rust-src rustfmt
    ```

2.  Clone the repo and use [cargo](https://doc.rust-lang.org/cargo/getting-started/first-steps.html)
    for development. There are included VSCodium [Run and Debug](.vscode/launch.json) configurations
    for use with the[rust-analyzer](https://open-vsx.org/extension/rust-lang/rust-analyzer) and
    [CodeLLDB](https://open-vsx.org/extension/vadimcn/vscode-lldb) extensions.
3.  Building a test RPM with Fedora sources and tito

    The RPM currently resolves the pinned Gradle distribution and Kotlin build
    dependencies online. Enable **Internet access during builds** for the Copr
    project or build, until these dependencies are provisioned for an offline
    build.

    1.  Install tito

        ```shell
        dnf install tito
        ```

    2.  Install the static RPM build dependencies:

        ```shell
        sudo dnf builddep minecraft-server.spec
        ```

    3.  Generate the dynamic build-requirements RPM:

        ```shell
        tito build --rpm
        ```

        or

        ```shell
        tito build --test --rpm
        ```

        The command exits after generating
        `/tmp/tito/minecraft-server-{version}-{release}.buildreqs.nosrc.rpm`
        when its dynamic build dependencies are not installed.
    4.  Install the dynamic build dependencies from that file:

        ```shell
        sudo dnf builddep /tmp/tito/minecraft-server-{version}-{release}.buildreqs.nosrc.rpm
        ```

    5.  Run the build command again to test the Fedora source build:

        ```shell
        tito build --rpm
        ```

        or

        ```shell
        tito build --test --rpm
        ```

#### Cleaning Up After development

These are instructions to delete/remove/uninstall files created during the development process

1.  Uninstall the Rust development dependencies with `dnf remove` installed during step 1
    of [Building](#building)
2.  Remove development dependencies installed via `dnf builddep`
    -   list your dnf history to find the instance when you installed the build dependencies

        ```shell
        dnf history list
        ```

    -   undo that installation to remove the build dependencies

        ```shell
        dnf history undo <transaction id>
        ```

3.  Delete the `XDG_CONFIG/DATA_HOME` directories used when running the programming

    >>>
    **NOTE**: Tests use temporary directories for the XDG directories so they don't need
    to be manually cleared
    >>>

4.  Delete the repo folder cloned in step 2 of [Building](#building)
5.  To remove programs/data installed from a development build of an RPM or from
    `cargo install` follow some other guide

### Release Process

0.  A recurring pipeline job checks for upstream server releases and creates a GitLab Release.
    The release webhook then triggers its Copr mirror build.
    This process is for releasing other changes.
1.  Review [Dependency Maintenance](docs/dependency-management.md) and update
    selected dependencies that satisfy its stability and Fedora/EPEL eligibility
    policy. The upstream dependency report is not an instruction to upgrade every
    listed crate.
2.  On the release candidate commit run

    ```shell
    tito build --test --rpm
    ```

3.  Version bump (they should all be the same)

    `.tito/packages/minecraft-server`

    `minecraft-server.spec`

    `minecraft-server/Cargo.toml`

    `Cargo.lock`
4.  Commit and create a pull request
5.  Wait for the pipeline to pass
6.  Merge the PR
7.  Create a new release with the tag `x.y.z` (it should match the version specified previously)
8.  Wait for the release-triggered Copr build to succeed
9.  COPR's build targets and project description are reconciled from the repository by CI

### General Architecture

As described earlier the purpose of the software is to wrap the Minecraft server executables with
code that will properly handle signals while maintaining as similar as possible an API to the
orginal executable. This makes it easy to write a systemd service to run a Minecraft service because
systemd uses signals to manage processes.

The `minecraft-server-java` and `minecraft-server-fabric` packages provide
top-level runnable jars for systemd-aware process handling. Each jar captures the
JVM and server arguments, then replaces its short-lived JVM with the packaged Rust
supervisor. The supervisors depend on the download-only `minecraft-java-server`
and `minecraft-fabric-server` packages, which own downloading and checksum
verification. The complete flow is:

```text
java <JVM options> -jar minecraft-server-{java,fabric}.jar <args>
  -> Rust systemd supervisor
  -> java <JVM options> -jar <download-wrapper.jar> <args>
  -> java <JVM options> -jar <version-locked.jar> <args>
```

Only the supervisor packages own the corresponding service and FIFO socket
units; the download-only packages do not install competing systemd units. The
scheduled version updater regenerates the RPM version metadata whenever a locked
Minecraft, Fabric, Geyser, or MCXboxBroadcast version changes.

All native wrapper jars share the Rust exec library supplied by the
`minecraft-server-wrapper-native` package. RPM dependencies install it
in the platform library directory, and the wrappers load it through Java's
standard `java.library.path` lookup. Source tests point that lookup at the locally
built library.

The Minecraft executable is run as a child process. That is straightforward enough. The hardest part
and I think the only section of code that requires explanation is the `SelfManage` trait. Everything
else is for downloading files, reading setting environment variables, loading settings, CLI
arguments, and launching the child process correctly.

The main process needs to be doing three things at the same tine

1.  Listening for commands sent to standard input and forwarding them to the Minecraft child process
2.  Listening for interprocess signals like `SIGTERM` and `SIGINT` then sending the appropriate
    "stop" message to the Minecraft child process
3.  Listening for when the child Minecraft executable process exits at which point the main process
    should also exit.

The crate mio let's us perform all of these in a non-blocking way. The last piece is correctly
handle errors and cleanup. This part probably has some bugs.

Mapping mio event handling to the main activities listed above

1.  We register a listener to stdin via mio

    ```rust
    poll.registry().register(
        &mut mio::unix::SourceFd(&parent_stdin.as_raw_fd()),
        STDIN_TOKEN,
        mio::Interest::READABLE,
    )?;
    ```

2.  Adding handlers for `SIGTERM` and `SIGINT`
3.  Handling the `SIGCHLD` signal should inform us when the child process exits

## Known Issues

None

## TO-DO

### New Rust Features

None

### Not actively being worked on

-   [ ] Support other platforms
-   [ ] Create a const function to generate the server.jar download URL at compile time
-   [ ] Migrate from [tempfile](https://packages.fedoraproject.org/pkgs/rust-tempfile/) to
    [assert_fs](https://packages.fedoraproject.org/pkgs/rust-assert_fs/). The problem is that there
    currently are not builds of assert_fs for EPEL.
-   [ ] Add more tests
-   [ ] Add additional variables to the spec file to prevent typos when making changes
-   [ ] Add better error handling (currently most errors just result in the programming exiting)
-   [ ] Improve the logging messages
-   [ ] Improve the manuals. Standardize the syntax used and flesh out the options and details
-   [ ] Add examples of using the environment variables
-   [ ] Support additional languages
-   [ ] Generate help documentation from the Minecraft binaries
-   [ ] Add an assertion of the expected contents of the bedrock zip file so that Mojang changes can
    be detected
-   [ ] The real way that the auto release process should work is that is should create a branch off
    of the tag of the last release commit the changes to that branch and create a new release from
    that branch. If we don't use that process then it's very easy for changes merged into main to
    get included with an auto release which is probably not what we want.

## FAQ

### What about the PID file `--pidFile`?

>>>
The `$MINECRAFT_SERVER_WORKING_DIRECTORY` environment variable and the default Minecraft
configuration directory affect relative paths passed to the `--pidFile` option for the
Minecraft Java server.

However note that the PID saved to the file with this option will be for the Java process
not for the Rust wrapper process. Depending on the goals of using the `--pidFile` option the
PID of the Rust process might be more useful. There is no built-in option to get that PID.
Use the operating system or other software to get the PID of the Rust process.
>>>

### Are there packages for other other operating systems?

>>>
Not currently. If built from source there should be no issue running on other Linux
based operating systems. Windows and macOS likely don't work unfortunately. There is a
chance the Java version will work.
>>>

### Does this provide auto updates to the latest Minecraft version?

>>>
No. Enable auto updates via dnf/rpm-ostree.

<https://dnf.readthedocs.io/en/latest/automatic.html>

<https://docs.fedoraproject.org/en-US/iot/applying-updates-UG/#_automatic_updates>

Or use dnf offline-upgrade. There isn't a package to use but here are some
[example system systemd unit files](example systemd unit files)
>>>

### Why doesn't `minecraft-server-bedrock` work on AArch64!?

>>>
Mojang only makes x86_64 builds for the Bedrock server. QEMU or Box64 might work but likely
very slowly

<https://www.qemu.org/docs/master/index.html>

<https://packages.fedoraproject.org/pkgs/qemu/qemu-user/>

<https://github.com/ptitSeb/box64>

<https://packages.fedoraproject.org/pkgs/box64/box64/>
>>>

## Inspiration/References

<https://github.com/sbstp/minecraftd>

<https://feedback.minecraft.net/hc/en-us/sections/360001186971-Release-Changelogs>

## Minecraft Server Setup Guide

0.  Assumes a prexisting server with Fedora Linux or AlmaLinux and a user account that
    can elevate to root privileges.
1.  Enable the Copr repo (see [Installation](#installation))
2.  Install the Minecraft Bedrock server and/or the Minecraft Java server (see [Installation]   (#installation))
3.  Enable and start the service (see [Installation](#installation))
4.  (Java only) approve the EULA at `~/net.minecraft.JavaServer/minecraft.eula.txt`
    -   Start the service again
5.  Enable the user service to start on boot

    ```shell
    loginctl enable-linger
    ```

6.  Open the appropriate ports with these firewall-cmd commands (assumes that firewalld is the
    firewall for the server)
    -   for Java

        ```shell
        # allow incomming tcp traffic on port 25565
        firewall-cmd --zone=public --add-port=25565/tcp
        # make it permanent (survive reboots)
        firewall-cmd --zone=public --permanent --add-port=25565/tcp
        # allow incomming udp traffic on port 25565
        firewall-cmd --zone=public --add-port=25565/udp
        # make it permanent (survive reboots)
        firewall-cmd --zone=public --permanent --add-port=25565/udp
        ```

    -   for Bedrock

        ```shell
        # allow incomming udp ipv4 traffic on port 19132
        firewall-cmd --zone=public --add-rich-rule='rule family=ipv4 port port=19132 protocol=udp accept'
        # make it permanent (survive reboots)
        firewall-cmd --zone=public --permanent --add-rich-rule='rule family=ipv4 port port=19132 protocol=udp accept'
        # allow incomming udp ipv6 traffic on port 19133
        firewall-cmd --zone=public --add-rich-rule='rule family=ipv6 port port=19133 protocol=udp accept'
        # make it permanent (survive reboots)
        firewall-cmd --zone=public --permanent --add-rich-rule='rule family=ipv6 port port=19133 protocol=udp accept'
        ```

7.  Consider enabling zram for low ram computers if it doesn't come pre-installed:

    <https://github.com/systemd/zram-generator>
8.  Enable [automatic updates](#does-this-provide-auto-updates-to-the-latest-minecraft-version)
9.  As an alternative to rebooting for updates just restart the service if
    it was updated. So, after running dnf upgrade run:

    ```shell
    systemctl --user daemon-reload
    systemctl --user reload-or-restart --marked
    ```

    <!-- markdownlint-disable MD030 -->
10. Test by pointing a Minecraft client to the public IP of the server. It should connect.
11. Setup SRV record to communicate the port for the client to connect to:

    <https://minecraft.wiki/w/Tutorials/Setting_up_a_server#The_SRV_record>
12. (Java only) Setup [Geyser](https://geysermc.org/) so that Bedrock players can join. An
    [unsupported example user service](example systemd unit files/geyser.service) is available
    for basic process supervision, but it does not guarantee graceful shutdown.
13. (Bedrock and Geyser only) Configure
    [MCXboxBroadcast Standalone](https://github.com/MCXboxBroadcast/Broadcaster)
    to let console friends join the server. The `mcxboxbroadcast-standalone` package
    provides the version-locked wrapper jar. An
    [unsupported example user service](example systemd unit files/mcxboxbroadcast-standalone.service)
    is available for basic process supervision, but it does not guarantee graceful shutdown.
14. Perform any desired configuration/modification of the server.
    <!-- markdownlint-enable MD030 -->
