zhliau / fika-headless-docker

Docker image to run SPT + Fika client headless, to provide a packaged dedicated client for raid hosting
Other
20 stars 7 forks source link

About

:new_moon_with_face: Run the FIKA dedicated client as a headless service, in a docker container! :new_moon_with_face:

πŸ§™ Features

πŸ‘» Dedicated Client

What is a Dedicated Client?

A dedicated client in the context of FIKA and SPT is essentially a separate instance of the game client that runs in a headless mode to host raids. It acts as a "silent player" that hosts the raid, allowing other players to join without one of them having to host the raid on their own machine.

Pros

Cons

πŸ“¦ Releases

The image build is triggered off git tags and hosted on ghcr. latest will always point to the latest version.

docker pull ghcr.io/zhliau/fika-headless-docker:latest

🚀 Running

I've only tested this on my linux hosts (Arch kernel 6.9.8 and Fedora 6.7.10). This won't work on Windows because of permission issues with WSL2. Probably will not work on ARM hosts either.

Tested with both SPT 3.8.3 and SPT 3.9.x and the associated Fika versions.

Requirements

Steps

  1. Prepare a Dedicated Client Installation

    • Copy SPT Installation: Locate your existing SPT installation folder (where you play SPTarkov + Fika) and create a copy of it in another location. This will serve as the dedicated client's installation. For example, if your SPT install is at D:\Games\SPT3.9, copy it to D:\Games\SPT3.9Dedicated. Or install a fresh SPT + Fika installation in a different directory.
  2. Install Fika Dedicated Client Plugin

    • Download Fika Dedicated Plugin: Download the Fika.Dedicated.dll plugin from the Fika Dedicated Releases.

    • Install the Plugin: Place the Fika.Dedicated.dll plugin into the dedicated client's BepInEx/plugins folder.

    • Ensure Fika Core Plugin is Installed: Verify that the Fika.Core.dll plugin is also present in the dedicated client's BepInEx/plugins folder.

  3. Generate the Dedicated Client Profile and Launch Script

    • Stop the SPT Server: If your SPT + Fika server is running, close it.

    • Edit Fika Configuration: Open the fika.jsonc file located at <SPT server folder>/user/mods/fika-server/assets/configs/fika.jsonc in a text editor.

      • Find the "dedicated" section and set "amount" to 1:
      "dedicated": {
         "profiles": {
             "amount": 1 // the amount of dedicated profiles to generate automatically
         },
         "scripts": {
             "generate": true, // generate the launch script
             "forceIp": "" // set to your dedicated client's IP address if needed
         }
      }
    • Start the SPT Server: Launch the SPT + Fika server (e.g. SPT.Server.exe or fika-spt-server-docker container) and wait until it fully loads. It should generate the dedicated client profile and launch script. Look for a message like Created 1 dedicated client profiles! in the server logs.

    • Retrieve Profile ID: The profile ID is the filename (excluding the .json extension) of the profile generated in the server's user/profiles directory. For example, if the profile is named 670c0b1a00014a7192a983f9.json, the profile ID is 670c0b1a00014a7192a983f9.

  4. Configure the Dedicated Client

    • Edit Fika Core Configuration: Open the com.fika.core.cfg file located in the dedicated client's BepInEx/config/ directory.

      • Update values for Force Bind IP and/or Force IP. It might be sufficient to set Force Bind IP to Disabled, and to set Force IP to the IP of the host interface. If you are running a VPN, then this is your VPN IP.
      ## Force Bind IP
      # Set to Disabled
      Force Bind IP = Disabled
      
      ## Force IP
      # Set to the IP address of your host interface (e.g., your LAN IP or VPN IP)
      Force IP = your.host.interface.ip
  5. Run the Docker Image

    • Mount the Fika Client Directory: Ensure that your dedicated client installation directory is mounted to /opt/tarkov in the container. If you are running the dedicated client on a server, you will need to copy and transfer the dedicated client installation to the server (~40GB).

    • Set Environment Variables: When running the docker image, set the following environment variables:

      • PROFILE_ID to the profile ID obtained in step 3.

      • SERVER_URL to your server's URL or IP address.

      • SERVER_PORT to your server's port (usually 6969).

      • Optionally, set USE_MODSYNC to true if you are using Corter-ModSync for plugin synchronization.

    • Run the Docker Container:

      docker run --name fika_dedicated \
      -v /path/to/fika:/opt/tarkov \
      -e PROFILE_ID=blah \
      -e SERVER_URL=your.spt.server.ip \
      -e SERVER_PORT=6969 \
      -p 25565:25565/udp \
      ghcr.io/zhliau/fika-headless-docker:latest

      With docker-compose file:

      services:
      fika_dedicated:
       image: ghcr.io/zhliau/fika-headless-docker:latest
       container_name: fika_dedi
       volumes:
         - /host/path/to/fika:/opt/tarkov
       environment:
         - PROFILE_ID=adadadadadadaadadadad
         - SERVER_URL=your.spt.server.ip
         - SERVER_PORT=6969
         - USE_MODSYNC=true # If you want to use modsync on this dedicated client
       ports:
         - 25565:25565/udp

      If you are running the SPT server as a service in the same docker-compose stack, you can make use of docker-compose's network DNS to resolve the SPT server from the dedicated client:

      services:
      fika:
       # See https://github.com/zhliau/fika-spt-server-docker
       image: ghcr.io/zhliau/fika-spt-server-docker:latest
       volumes:
         - /host/path/to/serverfiles:/opt/server
       ports:
         - 6969:6969
      fika_dedicated:
       image: ghcr.io/zhliau/fika-headless-docker:latest
       # ...
       environment:
         # ...
         # Use service DNS name instead of IP
         - SERVER_URL=fika
         - SERVER_PORT=6969
       # ...
  6. Verify the Dedicated Client is Running

    • Check Server Logs: Look for messages in the server logs indicating that the dedicated client has connected. Note that this may take a few minutes to happen (~5 minutes).

    • Start a Raid Using Dedicated Host:

      • Launch your SPT + Fika client, log in, and go to the raid selection screen.

      • Click on Host Raid and ensure that the Use Dedicated Host option is available (not greyed out).

      • Proceed to host a raid using the dedicated client.

Corter-Modsync support

This image supports the unique plugin updater process that Corter-ModSync employs to update client plugins. To enable support:

The start script will then:

[!NOTE] Enabling USE_MODSYNC does NOT mean that the dedicated client will periodically restart to check for updates to plugins. If you wish to do this, you must build it via a periodic restarter script or a cron job. You can mount the docker socket into a docker:cli image and run a simple bash while loop or something. See the example docker-compose.yml in this repo for details

🌐 Environment variables

Required

Env var Description
PROFILE_ID ProfileID of the dedicated client you created in step 1
SERVER_URL Server URL, or the name of the service that runs the Fika server if you have it in the same docker-compose stack
SERVER_PORT Server port, usually 6969

Optional

Env var Description
USE_DGPU If set to true, enable passing a GPU resource into the container with nvidia-container-toolkit. Make sure you have the required dependencies installed for your host
DISABLE_NODYNAMICAI If set to true, removes the -noDynamicAI parameter when starting the client, allowing the use of Fika's dynamic AI feature. Can help with dedicated client performance if you notice server FPS dropping below 30
USE_MODSYNC If set to true, enables support for Corter-ModSync 0.8.1+ and the external updater. On container start, the dedicated client will close and start the updater the modsync plugin detects changes. On completion, the script will start the dedicated client up again
ENABLE_LOG_PURGE If set to true, automatically purge the EFT Logs/ directory every 00:00 UTC, to clear out large logfiles due to logspam.
AUTO_RESTART_ON_RAID_END If set to true, auto restart the client on raid end, freeing all memory that isn't cleared properly on raid end
ESYNC If set to true, enable wine esync, to use eventfd based synchronization instead of wineserver. This can improve client performance. Check compatibility by ulimit -Hn. If this value is less than 524288, you need to increase your system's process file descriptor limit. See this troubleshooting tip.
FSYNC If set to true, enable wine fsync, to use futex based synchronization instead of wineserver. This can dramatically improve client performance. Takes precedence over ESYNC. Requires linux kernel version >= 5.16. Check compatibility via kernel syscall availability with cat /proc/kallsyms \| grep futex_waitv.

Debug

Env var Description
USE_GRAPHICS If set to true, disables the -nographics parameter when starting the dedicated client. This will significantly increase resource usage.
DISABLE_BATCHMODE If set to true, disable the -batchmode parameter when starting the client. This will significantly increase resource usage.
XVFB_DEBUG If set to true, enables debug output for xvfb (the virtual framebuffer)
SAVE_LOG_ON_EXIT If set to true, save a copy of the BepInEx LogOutput.log as LogOutput-$timestamp.log on client exit to preserve logs from previous client runs, since this file is truncated each time the client starts

🧰 Troubleshooting

Container immediately exits

Crashing with stacktrace in container, permissions errors, wine unable to find EscapeFromTarkov.exe, or wine throwing a page fault on read access to 0000000000000000 exception?

Stuck right after BepInEx preloader finished

fika_dedi  | [Message:   BepInEx] Preloader finished
fika_dedi  | (Filename: C:\buildslave\unity\build\Runtime/Export/Debug/Debug.bindings.h Line: 39)
fika_dedi  |
fika_dedi  | Fallback handler could not load library Z:/opt/tarkov/EscapeFromTarkov_Data/Mono/data-00007D86E24EA790.dll

Crash with assertion in virtual.c

../src-wine/dlls/ntdll/unix/virtual.c:1907: create_view: Assertion `!((UINT_PTR)base & page_mask)' failed.

If the dedicated client container crashes with this error, this usually means your max memory map count is too low.

Container stalls at wine: RLIMIT_NICE is <=20

This happens sometimes on first boot or when the container is force-recreated e.g. by docker-compose up --force-recreate. I have no idea why it happens, but to solve it you can

My container memory usage keeps going up until I run out of memory

Server output shows Cannot read properties of undefined (reading 'info')

I'm using ESYNC, but my client crashes

πŸ’» Development

Building

Run the build script, optionally setting a VERSION env var to tag the image. The image is tagged fika-dedicated:latest, or whatever version is provided in the env var.

# image tagged as fika-dedicated:0.1
$ VERSION=0.1 ./build

Using an Nvidia GPU in the container

If you want to pass in your host Nvidia GPU, make sure you have the following: