├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── bug_report.md ├── logo.png └── workflows │ ├── docker.yml │ ├── linting.yml │ └── release.yml ├── Dockerfile ├── LICENSE ├── README.md ├── cluster ├── service.yaml ├── statefulset.yaml └── values.yaml ├── docker-compose.yml ├── healthcheck.sh ├── init.sh ├── run.sh ├── saveshare ├── README.md ├── config.go ├── docker-compose.yml ├── file.go ├── go.mod ├── go.sum └── main.go ├── server.log └── ssl ├── README.md └── docker-compose.yml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [ wolveix ] 2 | custom: https://paypal.me/wolveix 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve this project! 4 | title: Explain Your Issue 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | ### [Experiencing issues? Check our Troubleshooting FAQ wiki!](https://github.com/wolveix/satisfactory-server/wiki/Troubleshooting-FAQ) 10 | 11 | ### Describe the Bug 12 | 13 | A clear and concise description of what the bug is. 14 | 15 | ### Your Runtime Command or Docker Compose File 16 | 17 | Please censor anything sensitive. Your runtime command might look like this: 18 | 19 | ```shell 20 | docker run --name="satisfactory" -p 7777 -p 8888 -v ./satisfactory:/config wolveix/satisfactory-server:latest 21 | ``` 22 | 23 | ### Debug Output 24 | 25 | Run the container with `DEBUG=true` as an environment variable, and it'll print out the system specs requested below, as 26 | well as a bunch of information about your container (version, environment variables, etc.) 27 | 28 | ```shell 29 | OUTPUT HERE 30 | ``` 31 | 32 | ### System Specs (please complete the following information): 33 | 34 | If you're on Linux, just paste the following block as a single command, and paste the output here. 35 | 36 | ```shell 37 | echo "===== START ISSUE REPORT ===== 38 | OS: $(uname -a) 39 | CPU: $(lscpu | grep 'Model name:' | sed 's/Model name:[[:space:]]*//g') 40 | RAM: $(awk '/MemAvailable/ {printf( "%d\n", $2 / 1024000 )}' /proc/meminfo)GB/$(awk '/MemTotal/ {printf( "%d\n", $2 / 1024000 )}' /proc/meminfo)GB 41 | HDD: $(df -h | awk '$NF=="/"{printf "%dGB/%dGB (%s used)\n", $3,$2,$5}') 42 | ===== END ISSUE REPORT =====" 43 | ``` 44 | 45 | Alternatively, you can find the information manually. Here's what we're looking for: 46 | 47 | - OS: [e.g. Ubuntu 18.04 x86_64] (Linux: `uname -a`) 48 | - CPU: [e.g. AMD Ryzen 5 3600 6-Core Processor] (Linux: `lscpu`) 49 | - RAM: [e.g. 4GB/16GB] (Linux: `cat /proc/meminfo | grep Mem`) 50 | - HDD; [e.g. 22GB/251GB (9% used)] (Linux: `df -h`) 51 | 52 | ### Logs 53 | 54 | Please provide your **full** container logs. Do not link to an external site for them, just upload them as an attachment 55 | to your reply. 56 | 57 | ### Additional Context 58 | 59 | Add any other context about the problem here. 60 | -------------------------------------------------------------------------------- /.github/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wolveix/satisfactory-server/05fa6b4d82981e79616fa9819af89fc9ddbf6c4f/.github/logo.png -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | on: [ push ] 3 | 4 | jobs: 5 | push: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | 10 | - name: Login to DockerHub 11 | uses: docker/login-action@v3 12 | with: 13 | username: ${{ secrets.DOCKER_USERNAME }} 14 | password: ${{ secrets.DOCKER_PASSWORD }} 15 | 16 | - name: Login to GitHub Packages 17 | uses: docker/login-action@v3 18 | with: 19 | registry: ghcr.io 20 | username: ${{ github.actor }} 21 | password: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | - name: Docker meta 24 | id: meta 25 | uses: docker/metadata-action@v5 26 | with: 27 | # List of Docker images to use as base name for tags. 28 | images: | 29 | ${{ github.repository }} 30 | # Generate Docker tags based on the following events/attributes. 31 | tags: | 32 | type=raw,value=dev 33 | 34 | - name: Build and push to DockerHub 35 | id: docker_build 36 | uses: docker/build-push-action@v6 37 | with: 38 | push: true 39 | tags: | 40 | ghcr.io/${{ github.repository }}:dev 41 | ${{ steps.meta.outputs.tags }} -------------------------------------------------------------------------------- /.github/workflows/linting.yml: -------------------------------------------------------------------------------- 1 | name: Linting 2 | on: [ pull_request ] 3 | 4 | jobs: 5 | shellcheck: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout code 9 | uses: actions/checkout@v4 10 | 11 | - name: Run Shellcheck 12 | uses: azohra/shell-linter@latest 13 | with: 14 | severity: "error" 15 | dockerlint: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | 21 | - name: Validate Dockerfile 22 | uses: ghe-actions/dockerfile-validator@v1 23 | with: 24 | dockerfile: 'Dockerfile' 25 | lint: 'hadolint' -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | release: 4 | types: [ published ] 5 | 6 | jobs: 7 | release: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | 12 | - name: Login to DockerHub 13 | uses: docker/login-action@v3 14 | with: 15 | username: ${{ secrets.DOCKER_USERNAME }} 16 | password: ${{ secrets.DOCKER_PASSWORD }} 17 | 18 | - name: Login to GitHub Packages 19 | uses: docker/login-action@v3 20 | with: 21 | registry: ghcr.io 22 | username: ${{ github.actor }} 23 | password: ${{ secrets.GITHUB_TOKEN }} 24 | 25 | - name: Docker meta 26 | id: meta 27 | uses: docker/metadata-action@v5 28 | with: 29 | # List of Docker images to use as base name for tags. 30 | images: | 31 | ${{ github.repository }} 32 | ghcr.io/${{ github.repository }} 33 | # Generate Docker tags based on the following events/attributes. 34 | tags: | 35 | type=semver,pattern=v{{version}} 36 | type=semver,pattern=v{{major}}.{{minor}} 37 | type=semver,pattern=v{{major}} 38 | # Always generate latest tag on push. 39 | flavor: | 40 | latest=true 41 | 42 | - name: Build and push to DockerHub & GitHub Packages 43 | id: docker_build 44 | uses: docker/build-push-action@v6 45 | with: 46 | push: true 47 | tags: ${{ steps.meta.outputs.tags }} 48 | build-args: | 49 | VERSION=${{ github.event.release.tag_name }} -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM steamcmd/steamcmd:ubuntu-22 2 | 3 | ARG GID=1000 4 | ARG UID=1000 5 | 6 | ENV AUTOSAVENUM="5" \ 7 | DEBIAN_FRONTEND="noninteractive" \ 8 | DEBUG="false" \ 9 | DISABLESEASONALEVENTS="false" \ 10 | GAMECONFIGDIR="/config/gamefiles/FactoryGame/Saved" \ 11 | GAMESAVESDIR="/home/steam/.config/Epic/FactoryGame/Saved/SaveGames" \ 12 | LOG="false" \ 13 | MAXOBJECTS="2162688" \ 14 | MAXPLAYERS="4" \ 15 | MAXTICKRATE="30" \ 16 | MULTIHOME="::" \ 17 | PGID="1000" \ 18 | PUID="1000" \ 19 | SERVERGAMEPORT="7777" \ 20 | SERVERMESSAGINGPORT="8888" \ 21 | SERVERSTREAMING="true" \ 22 | SKIPUPDATE="false" \ 23 | STEAMAPPID="1690800" \ 24 | STEAMBETA="false" \ 25 | TIMEOUT="30" \ 26 | VMOVERRIDE="false" 27 | 28 | # hadolint ignore=DL3008 29 | RUN set -x \ 30 | && apt-get update \ 31 | && apt-get install -y gosu xdg-user-dirs curl jq tzdata --no-install-recommends \ 32 | && rm -rf /var/lib/apt/lists/* \ 33 | && groupadd -g ${GID} steam \ 34 | && useradd -u ${UID} -g ${GID} -ms /bin/bash steam \ 35 | && mkdir -p /home/steam/.local/share/Steam/ \ 36 | && cp -R /root/.local/share/Steam/steamcmd/ /home/steam/.local/share/Steam/steamcmd/ \ 37 | && chown -R ${UID}:${GID} /home/steam/.local/ \ 38 | && gosu nobody true 39 | 40 | RUN mkdir -p /config \ 41 | && chown steam:steam /config 42 | 43 | COPY init.sh / 44 | COPY --chown=steam:steam healthcheck.sh run.sh /home/steam/ 45 | 46 | RUN chmod +x /init.sh /home/steam/healthcheck.sh /home/steam/run.sh 47 | 48 | HEALTHCHECK --timeout=30s --start-period=300s CMD bash /home/steam/healthcheck.sh 49 | 50 | WORKDIR /config 51 | ARG VERSION="DEV" 52 | ENV VERSION=$VERSION 53 | LABEL version=$VERSION 54 | STOPSIGNAL SIGINT 55 | EXPOSE 7777/udp 7777/tcp 8888/tcp 56 | 57 | ENTRYPOINT [ "/init.sh" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Robert Thomas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Satisfactory Server 2 | 3 | ![Satisfactory](https://raw.githubusercontent.com/wolveix/satisfactory-server/main/.github/logo.png "Satisfactory logo") 4 | 5 | ![Release](https://img.shields.io/github/v/release/wolveix/satisfactory-server) 6 | ![Docker Pulls](https://img.shields.io/docker/pulls/wolveix/satisfactory-server) 7 | ![Docker Stars](https://img.shields.io/docker/stars/wolveix/satisfactory-server) 8 | ![Image Size](https://img.shields.io/docker/image-size/wolveix/satisfactory-server) 9 | 10 | This is a Dockerized version of the [Satisfactory](https://store.steampowered.com/app/526870/Satisfactory/) dedicated 11 | server. 12 | 13 | ### [Experiencing issues? Check our Troubleshooting FAQ wiki!](https://github.com/wolveix/satisfactory-server/wiki/Troubleshooting-FAQ) 14 | 15 | ### [Upgrading for Satisfactory 1.0](https://github.com/wolveix/satisfactory-server/wiki/Upgrading-for-1.0) 16 | 17 | ## Setup 18 | 19 | The server may run on less than 8GB of RAM, though 8GB - 16GB is still recommended per 20 | the [the official wiki](https://satisfactory.wiki.gg/wiki/Dedicated_servers#Requirements). You may need to increase the 21 | container's defined `--memory` restriction as you approach the late game (or if you're playing with many 4+ players) 22 | 23 | You'll need to bind a local directory to the Docker container's `/config` directory. This directory will hold the 24 | following directories: 25 | 26 | - `/backups` - the server will automatically backup your saves when the container first starts 27 | - `/gamefiles` - this is for the game's files. They're stored outside the container to avoid needing to redownload 28 | 8GB+ every time you want to rebuild the container 29 | - `/logs` - this holds Steam's logs, and contains a pointer to Satisfactory's logs (empties on startup unless 30 | `LOG=true`) 31 | - `/saved` - this contains the game's blueprints, saves, and server configuration 32 | 33 | Before running the server image, you should find your user ID that will be running the container. This isn't necessary 34 | in most cases, but it's good to find out regardless. If you're seeing `permission denied` errors, then this is probably 35 | why. Find your ID in `Linux` by running the `id` command. Then grab the user ID (usually something like `1000`) and pass 36 | it into the `-e PGID=1000` and `-e PUID=1000` environment variables. 37 | 38 | Run the Satisfactory server image like this (this is one command, make sure to copy all of it):
39 | 40 | ```bash 41 | docker run \ 42 | --detach \ 43 | --name=satisfactory-server \ 44 | --hostname satisfactory-server \ 45 | --restart unless-stopped \ 46 | --volume ./satisfactory-server:/config \ 47 | --env MAXPLAYERS=4 \ 48 | --env PGID=1000 \ 49 | --env PUID=1000 \ 50 | --env STEAMBETA=false \ 51 | --memory-reservation=4G \ 52 | --memory 8G \ 53 | --publish 7777:7777/tcp \ 54 | --publish 7777:7777/udp \ 55 | --publish 8888:8888/tcp \ 56 | wolveix/satisfactory-server:latest 57 | ``` 58 | 59 |
60 | Explanation of the command 61 | 62 | * `--detach` -> Starts the container detached from your terminal
63 | If you want to see the logs replace it with `--sig-proxy=false` 64 | * `--name` -> Gives the container a unqiue name 65 | * `--hostname` -> Changes the hostname of the container 66 | * `--restart unless-stopped` -> Automatically restarts the container unless the container was manually stopped 67 | * `--volume` -> Binds the Satisfactory config folder to the folder you specified 68 | Allows you to easily access your savegames 69 | * For the environment (`--env`) variables please 70 | see [here](https://github.com/wolveix/satisfactory-server#environment-variables) 71 | * `--memory-reservation=4G` -> Reserves 4GB RAM from the host for the container's use 72 | * `--memory 8G` -> Restricts the container to 8GB RAM 73 | * `--publish` -> Specifies the ports that the container exposes
74 | 75 |
76 | 77 | ### Docker Compose 78 | 79 | If you're using [Docker Compose](https://docs.docker.com/compose/): 80 | 81 | ```yaml 82 | services: 83 | satisfactory-server: 84 | container_name: 'satisfactory-server' 85 | hostname: 'satisfactory-server' 86 | image: 'wolveix/satisfactory-server:latest' 87 | ports: 88 | - '7777:7777/tcp' 89 | - '7777:7777/udp' 90 | - '8888:8888/tcp' 91 | volumes: 92 | - './satisfactory-server:/config' 93 | environment: 94 | - MAXPLAYERS=4 95 | - PGID=1000 96 | - PUID=1000 97 | - STEAMBETA=false 98 | restart: unless-stopped 99 | deploy: 100 | resources: 101 | limits: 102 | memory: 8G 103 | reservations: 104 | memory: 4G 105 | ``` 106 | 107 | ### Updating 108 | 109 | The game automatically updates when the container is started or restarted (unless you set `SKIPUPDATE=true`). 110 | 111 | To update the container image itself: 112 | 113 | #### Docker Run 114 | 115 | ```shell 116 | docker pull wolveix/satisfactory-server:latest 117 | docker stop satisfactory-server 118 | docker rm satisfactory-server 119 | docker run ... 120 | ``` 121 | 122 | #### Docker Compose 123 | 124 | ```shell 125 | docker compose pull 126 | docker compose up -d 127 | ``` 128 | 129 | ### SSL Certificate with Certbot (Optional) 130 | 131 | You can use Certbot with Let's Encrypt to issue a signed SSL certificate for your server. Without this, 132 | Satisfactory will use a self-signed SSL certificate, requiring players to manually confirm them when they initially 133 | connect. If you're experiencing connectivity issues since issuing a certificate, check the link below for known issues. 134 | 135 | [Learn more](https://github.com/wolveix/satisfactory-server/tree/main/ssl). 136 | 137 | ### Kubernetes 138 | 139 | If you are running a [Kubernetes](https://kubernetes.io) cluster, we do have 140 | a [service.yaml](https://github.com/wolveix/satisfactory-server/tree/main/cluster/service.yaml) 141 | and [statefulset.yaml](https://github.com/wolveix/satisfactory-server/tree/main/cluster/statefulset.yaml) available 142 | under the [cluster](https://github.com/wolveix/satisfactory-server/tree/main/cluster) directory of this repo, along with 143 | an example [values.yaml](https://github.com/wolveix/satisfactory-server/tree/main/cluster/values.yaml) file. 144 | 145 | If you are using [Helm](https://helm.sh), you can find charts for this repo on 146 | [ArtifactHUB](https://artifacthub.io/packages/search?ts_query_web=satisfactory&sort=relevance&page=1). The 147 | [k8s-at-home](https://github.com/k8s-at-home/charts) helm chart for Satisfactory can be installed with the below (please 148 | see `cluster/values.yaml` for more information). 149 | 150 | ```bash 151 | helm repo add k8s-at-home https://k8s-at-home.com/charts/ 152 | helm repo update 153 | helm install satisfactory k8s-at-home/satisfactory -f values.yaml 154 | ``` 155 | 156 | ## Environment Variables 157 | 158 | | Parameter | Default | Function | 159 | |-------------------------|:---------:|-----------------------------------------------------------| 160 | | `AUTOSAVENUM` | `5` | number of rotating autosave files | 161 | | `DEBUG` | `false` | for debugging the server | 162 | | `DISABLESEASONALEVENTS` | `false` | disable the FICSMAS event (you miserable bastard) | 163 | | `LOG` | `false` | disable Satisfactory log pruning | 164 | | `MAXOBJECTS` | `2162688` | set the object limit for your server | 165 | | `MAXPLAYERS` | `4` | set the player limit for your server | 166 | | `MAXTICKRATE` | `30` | set the maximum sim tick rate for your server | 167 | | `MULTIHOME` | `::` | set the server's listening interface (usually not needed) | 168 | | `PGID` | `1000` | set the group ID of the user the server will run as | 169 | | `PUID` | `1000` | set the user ID of the user the server will run as | 170 | | `SERVERGAMEPORT` | `7777` | set the game's server port | 171 | | `SERVERMESSAGINGPORT` | `8888` | set the game's messaging port (internally and externally) | 172 | | `SERVERSTREAMING` | `true` | toggle whether the game utilizes asset streaming | 173 | | `SKIPUPDATE` | `false` | avoid updating the game on container start/restart | 174 | | `STEAMBETA` | `false` | set experimental game version | 175 | | `TIMEOUT` | `30` | set client timeout (in seconds) | 176 | | `VMOVERRIDE` | `false` | skips the CPU model check (should not ordinarily be used) | 177 | 178 | ## Experimental Branch 179 | 180 | If you want to run a server for the Experimental version of the game, set the `STEAMBETA` environment variable to 181 | `true`. 182 | 183 | ## Modding 184 | 185 | Mod support is still a little rough around the edges, but they do now work. This Docker container functions the same as 186 | a standalone installation, so you can follow the excellent technical documentation from the 187 | community [here](https://docs.ficsit.app/satisfactory-modding/latest/ForUsers/DedicatedServerSetup.html). 188 | 189 | The container does **NOT** have an S/FTP server installed directly, as Docker images are intended to carry a single 190 | function/process. You can either SFTP into your host that houses the Satisfactory server (trivial to do if you're 191 | running Linux), or alternatively you can spin up an S/FTP server through the use of another Docker container using the 192 | Docker Compose example listed below: 193 | 194 | ```yaml 195 | services: 196 | # only needed for mods 197 | sftp-server: 198 | container_name: 'sftp-server' 199 | image: 'atmoz/sftp:latest' 200 | volumes: 201 | - './satisfactory-server:/home/your-ftp-user/satisfactory-server' 202 | ports: 203 | - '2222:22' 204 | # set the user and password, and the user's UID (this should match the PUID and PGID of the satisfactory-server container) 205 | command: 'your-ftp-user:your-ftp-password:1000' 206 | ``` 207 | 208 | With this, you'll be able to SFTP into your server and access your game files via 209 | `/home/your-ftp-user/satisfactory-server/gamefiles`. 210 | 211 | ## How to Improve the Multiplayer Experience 212 | 213 | The [Satisfactory Wiki](https://satisfactory.wiki.gg/wiki/Multiplayer#Engine.ini) recommends a few config tweaks for 214 | your client to 215 | really get the best out of multiplayer: 216 | 217 | - Press `WIN + R` 218 | - Enter `%localappdata%/FactoryGame/Saved/Config/WindowsNoEditor` 219 | - Copy the config data from the wiki into the respective files 220 | - Right-click each of the 3 config files (Engine.ini, Game.ini, Scalability.ini) 221 | - Go to Properties > tick Read-only under the attributes 222 | 223 | ## Running as Non-Root User 224 | 225 | By default, the container runs with root privileges but executes Satisfactory under `1000:1000`. If your host's user and 226 | group IDs are `1000:1000`, you can run the entire container as non-root using Docker's `--user` directive. For different 227 | user/group IDs, you'll need to clone and rebuild the image with your specific UID/GID: 228 | 229 | ### Building Non-Root Image 230 | 231 | 1. Clone the repository: 232 | 233 | ```shell 234 | git clone https://github.com/wolveix/satisfactory-server.git 235 | ``` 236 | 237 | 2. Create a docker-compose.yml file with your desired UID/GID as build args (note that the `PUID` and `PGID` environment 238 | variables will no longer be needed): 239 | 240 | ```yaml 241 | services: 242 | satisfactory-server: 243 | container_name: 'satisfactory-server' 244 | hostname: 'satisfactory-server' 245 | build: 246 | context: . 247 | args: 248 | UID: 1001 # Your desired UID 249 | GID: 1001 # Your desired GID 250 | user: "1001:1001" # Must match UID:GID above 251 | ports: 252 | - '7777:7777/tcp' 253 | - '7777:7777/udp' 254 | - '8888:8888/tcp' 255 | volumes: 256 | - './satisfactory-server:/config' 257 | environment: 258 | - MAXPLAYERS=4 259 | - STEAMBETA=false 260 | restart: unless-stopped 261 | deploy: 262 | resources: 263 | limits: 264 | memory: 8G 265 | reservations: 266 | memory: 4G 267 | ``` 268 | 269 | 3. Build and run the container: 270 | 271 | ```shell 272 | docker compose up -d 273 | ``` 274 | 275 | ## Known Issues 276 | 277 | - The container is run as `root` by default. You can provide your own user and group using Docker's `--user` directive; 278 | however, if your proposed user and group aren't `1000:1000`, you'll need to rebuild the image (as outlined above). 279 | - The server log will show various errors; most of which can be safely ignored. As long as the container continues to 280 | run and your log looks similar to the example log, the server should be functioning just 281 | fine: [example log](https://github.com/wolveix/satisfactory-server/blob/main/server.log) 282 | 283 | ## Star History 284 | 285 | [![Star History Chart](https://api.star-history.com/svg?repos=wolveix/satisfactory-server&type=Date)](https://star-history.com/#wolveix/satisfactory-server&Date) 286 | -------------------------------------------------------------------------------- /cluster/service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: satisfactory 6 | labels: 7 | app: satisfactory 8 | spec: 9 | type: LoadBalancer 10 | allocateLoadBalancerNodePorts: true 11 | externalTrafficPolicy: Cluster 12 | internalTrafficPolicy: Cluster 13 | # might be necessary for your cluster: 14 | # loadBalancerIP: 15 | ports: 16 | - name: "api" 17 | port: 7777 18 | protocol: TCP 19 | targetPort: 7777 20 | - name: "game" 21 | port: 7777 22 | protocol: UDP 23 | targetPort: 7777 24 | - name: "messaging" 25 | port: 8888 26 | protocol: TCP 27 | targetPort: 8888 28 | selector: 29 | app: satisfactory 30 | -------------------------------------------------------------------------------- /cluster/statefulset.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: StatefulSet 4 | metadata: 5 | name: satisfactory 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: satisfactory 10 | serviceName: "satisfactory" 11 | replicas: 1 12 | template: 13 | metadata: 14 | labels: 15 | app: satisfactory 16 | spec: 17 | containers: 18 | - name: satisfactory 19 | image: wolveix/satisfactory-server:latest 20 | env: 21 | - name: DEBUG 22 | value: "false" 23 | - name: MAXPLAYERS 24 | value: "8" 25 | - name: PGID 26 | value: "1000" 27 | - name: PUID 28 | value: "1000" 29 | - name: SKIPUPDATE 30 | value: "false" 31 | - name: STEAMBETA 32 | value: "false" 33 | ports: 34 | - name: "api" 35 | containerPort: 7777 36 | protocol: TCP 37 | - name: "game" 38 | containerPort: 7777 39 | protocol: UDP 40 | - name: "messaging" 41 | containerPort: 8888 42 | protocol: TCP 43 | volumeMounts: 44 | - name: satisfactory-config 45 | mountPath: /config 46 | - name: satisfactory-data 47 | mountPath: /config/gamefiles 48 | volumeClaimTemplates: 49 | - metadata: 50 | name: satisfactory-config 51 | spec: 52 | accessModes: [ "ReadWriteOnce" ] 53 | resources: 54 | requests: 55 | storage: 1Gi 56 | - metadata: 57 | name: satisfactory-data 58 | spec: 59 | accessModes: [ "ReadWriteOnce" ] 60 | resources: 61 | requests: 62 | storage: 20Gi 63 | -------------------------------------------------------------------------------- /cluster/values.yaml: -------------------------------------------------------------------------------- 1 | env: 2 | # Environmental variables as below can be passed in this yaml block 3 | AUTOPAUSE: "true" 4 | MAXPLAYERS: 3 5 | 6 | service: 7 | main: # Example setup for a LoadBalancer with an external IP 8 | # MetalLB for example could be used if a Loadbalancer is not provided by your provider 9 | type: LoadBalancer # Setting an external IP for simple port forwarding 10 | externalTrafficPolicy: Cluster 11 | loadBalancerIP: "192.168.2.200" # IP of the satisfactory server 12 | 13 | persistence: 14 | config: # Config/save data stored here 15 | enabled: true 16 | 17 | server-cache: # Game files stored here 18 | # This is seperated to allow for backing up only game/config data 19 | enabled: true -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | satisfactory-server: 3 | container_name: 'satisfactory-server' 4 | hostname: 'satisfactory-server' 5 | image: 'wolveix/satisfactory-server:latest' 6 | ports: 7 | - '7777:7777/tcp' 8 | - '7777:7777/udp' 9 | - '8888:8888/tcp' 10 | volumes: 11 | - './satisfactory-server:/config' 12 | environment: 13 | - MAXPLAYERS=4 14 | - PGID=1000 15 | - PUID=1000 16 | - STEAMBETA=false 17 | restart: unless-stopped 18 | deploy: 19 | resources: 20 | limits: 21 | memory: 8G 22 | reservations: 23 | memory: 4G -------------------------------------------------------------------------------- /healthcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -o pipefail 3 | 4 | curl -k -f -s -S -X POST "https://127.0.0.1:${SERVERGAMEPORT}/api/v1" \ 5 | -H "Content-Type: application/json" \ 6 | -d '{"function":"HealthCheck","data":{"clientCustomData":""}}' \ 7 | | jq -e '.data.health == "healthy"' 8 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | printf "===== Satisfactory Server %s =====\\nhttps://github.com/wolveix/satisfactory-server\\n\\n" "$VERSION" 6 | 7 | MSGERROR="\033[0;31mERROR:\033[0m" 8 | MSGWARNING="\033[0;33mWARNING:\033[0m" 9 | NUMCHECK='^[0-9]+$' 10 | RAMAVAILABLE=$(awk '/MemAvailable/ {printf( "%d\n", $2 / 1024000 )}' /proc/meminfo) 11 | 12 | export CURRENTGID=$(id -g) 13 | export CURRENTUID=$(id -u) 14 | export HOME="/home/steam" 15 | export STEAMGID=$(id -g steam) 16 | export STEAMUID=$(id -u steam) 17 | export USER="steam" 18 | 19 | if [[ "${DEBUG,,}" == "true" ]]; then 20 | printf "Debugging enabled (the container will exit after printing the debug info)\\n\\nPrinting environment variables:\\n" 21 | export 22 | 23 | echo " 24 | System info: 25 | OS: $(uname -a) 26 | CPU: $(lscpu | grep '^Model name:' | sed 's/Model name:[[:space:]]*//g') 27 | RAM: $(awk '/MemAvailable/ {printf( "%d\n", $2 / 1024000 )}' /proc/meminfo)GB/$(awk '/MemTotal/ {printf( "%d\n", $2 / 1024000 )}' /proc/meminfo)GB 28 | HDD: $(df -h | awk '$NF=="/"{printf "%dGB/%dGB (%s used)\n", $3,$2,$5}')" 29 | printf "\\nCurrent version:\\n%s" "${VERSION}" 30 | printf "\\nCurrent user:\\n%s" "$(id)" 31 | printf "\\nProposed user:\\nuid=%s(?) gid=%s(?) groups=%s(?)\\n" "$PUID" "$PGID" "$PGID" 32 | printf "\\nExiting...\\n" 33 | exit 1 34 | fi 35 | 36 | # check that the cpu isn't generic, as Satisfactory will normally crash 37 | if [[ "$VMOVERRIDE" == "true" ]]; then 38 | printf "${MSGWARNING} VMOVERRIDE is enabled, skipping CPU model check. Satisfactory might crash!\\n" 39 | else 40 | cpu_model=$(lscpu | grep 'Model name:' | sed 's/Model name:[[:space:]]*//g') 41 | if [[ "$cpu_model" == "Common KVM processor" || "$cpu_model" == *"QEMU"* ]]; then 42 | printf "${MSGERROR} Your CPU model is configured as \"${cpu_model}\", which will cause Satisfactory to crash.\\nIf you have control over your hypervisor (ESXi, Proxmox, etc.), you should be able to easily change this.\\nOtherwise contact your host/administrator for assistance.\\n" 43 | exit 1 44 | fi 45 | fi 46 | 47 | printf "Checking available memory: %sGB detected\\n" "$RAMAVAILABLE" 48 | if [[ "$RAMAVAILABLE" -lt 8 ]]; then 49 | printf "${MSGWARNING} You have less than the required 8GB minimum (%sGB detected) of available RAM to run the game server.\\nThe server will likely run fine, though may run into issues in the late game (or with 4+ players).\\n" "$RAMAVAILABLE" 50 | fi 51 | 52 | # prevent large logs from accumulating by default 53 | if [[ "${LOG,,}" != "true" ]]; then 54 | printf "Clearing old Satisfactory logs (set LOG=true to disable this)\\n" 55 | if [ -d "/config/gamefiles/FactoryGame/Saved/Logs" ] && [ -n "$(find /config/gamefiles/FactoryGame/Saved/Logs -type f -print -quit)" ]; then 56 | rm -r /config/gamefiles/FactoryGame/Saved/Logs/* || true 57 | fi 58 | fi 59 | 60 | if [[ "$CURRENTUID" -ne "0" ]]; then 61 | if [[ "$STEAMUID" -ne "$CURRENTUID" ]] || [[ "$STEAMGID" -ne $(id -g) ]]; then 62 | printf "${MSGERROR} Current user (%s:%s) is not root (0:0), and doesn't match the steam user/group (%s:%s).\\nTo run the container as non-root with a UID/GID that differs from the steam user, you must build the Docker image with the UID and GID build arguments set.\\n" "$CURRENTUID" "$CURRENTGID" "$STEAMUID" "$STEAMGID" 63 | exit 1 64 | fi 65 | 66 | printf "${MSGWARNING} Running as non-root user (%s:%s).\\n" "$CURRENTUID" "$CURRENTGID" 67 | fi 68 | 69 | if ! [[ "$PGID" =~ $NUMCHECK ]] ; then 70 | printf "${MSGWARNING} Invalid group id given: %s\\n" "$PGID" 71 | PGID="1000" 72 | elif [[ "$PGID" -eq 0 ]]; then 73 | printf "${MSGERROR} PGID/group cannot be 0 (root)\\n" 74 | exit 1 75 | fi 76 | 77 | if ! [[ "$PUID" =~ $NUMCHECK ]] ; then 78 | printf "${MSGWARNING} Invalid user id given: %s\\n" "$PUID" 79 | PUID="1000" 80 | elif [[ "$PUID" -eq 0 ]]; then 81 | printf "${MSGERROR} PUID/user cannot be 0 (root)\\n" 82 | exit 1 83 | fi 84 | 85 | if [[ "$CURRENTUID" -eq "0" ]]; then 86 | if [[ $(getent group $PGID | cut -d: -f1) ]]; then 87 | usermod -a -G "$PGID" steam 88 | else 89 | groupmod -g "$PGID" steam 90 | fi 91 | 92 | if [[ $(getent passwd ${PUID} | cut -d: -f1) ]]; then 93 | USER=$(getent passwd $PUID | cut -d: -f1) 94 | else 95 | usermod -u "$PUID" steam 96 | fi 97 | fi 98 | 99 | if [[ ! -w "/config" ]]; then 100 | echo "The current user does not have write permissions for /config" 101 | exit 1 102 | fi 103 | 104 | mkdir -p \ 105 | /config/backups \ 106 | /config/gamefiles \ 107 | /config/logs/steam \ 108 | /config/saved/blueprints \ 109 | /config/saved/server \ 110 | "${GAMECONFIGDIR}/Config/LinuxServer" \ 111 | "${GAMECONFIGDIR}/Logs" \ 112 | "${GAMESAVESDIR}/server" \ 113 | /home/steam/.steam/root \ 114 | /home/steam/.steam/steam \ 115 | || exit 1 116 | 117 | echo "Satisfactory logs can be found in /config/gamefiles/FactoryGame/Saved/Logs" > /config/logs/satisfactory-path.txt 118 | 119 | if [[ "$CURRENTUID" -eq "0" ]]; then 120 | chown -R "$PUID":"$PGID" /config /home/steam /tmp/dumps 121 | exec gosu "$USER" "/home/steam/run.sh" "$@" 122 | else 123 | # running within a rootless environment 124 | exec "/home/steam/run.sh" "$@" 125 | fi 126 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Port validation 6 | if ! [[ "$SERVERGAMEPORT" =~ $NUMCHECK ]]; then 7 | printf "Invalid server port given: %s\\n" "$SERVERGAMEPORT" 8 | SERVERGAMEPORT="7777" 9 | fi 10 | printf "Setting server port to %s\\n" "$SERVERGAMEPORT" 11 | 12 | if ! [[ "$SERVERMESSAGINGPORT" =~ $NUMCHECK ]]; then 13 | printf "Invalid messaging port given: %s\\n" "$SERVERMESSAGINGPORT" 14 | SERVERMESSAGINGPORT="8888" 15 | fi 16 | printf "Setting messaging port to %s\\n" "$SERVERMESSAGINGPORT" 17 | 18 | # Engine.ini settings. 19 | if ! [[ "$AUTOSAVENUM" =~ $NUMCHECK ]]; then 20 | printf "Invalid autosave number given: %s\\n" "$AUTOSAVENUM" 21 | AUTOSAVENUM="5" 22 | fi 23 | printf "Setting autosave number to %s\\n" "$AUTOSAVENUM" 24 | 25 | if ! [[ "$MAXOBJECTS" =~ $NUMCHECK ]]; then 26 | printf "Invalid max objects number given: %s\\n" "$MAXOBJECTS" 27 | MAXOBJECTS="2162688" 28 | fi 29 | printf "Setting max objects to %s\\n" "$MAXOBJECTS" 30 | 31 | if ! [[ "$MAXTICKRATE" =~ $NUMCHECK ]] ; then 32 | printf "Invalid max tick rate number given: %s\\n" "$MAXTICKRATE" 33 | MAXTICKRATE="30" 34 | fi 35 | printf "Setting max tick rate to %s\\n" "$MAXTICKRATE" 36 | 37 | [[ "${SERVERSTREAMING,,}" == "true" ]] && SERVERSTREAMING="1" || SERVERSTREAMING="0" 38 | printf "Setting server streaming to %s\\n" "$SERVERSTREAMING" 39 | 40 | if ! [[ "$TIMEOUT" =~ $NUMCHECK ]] ; then 41 | printf "Invalid timeout number given: %s\\n" "$TIMEOUT" 42 | TIMEOUT="30" 43 | fi 44 | printf "Setting timeout to %s\\n" "$TIMEOUT" 45 | 46 | # Game.ini settings. 47 | if ! [[ "$MAXPLAYERS" =~ $NUMCHECK ]] ; then 48 | printf "Invalid max players given: %s\\n" "$MAXPLAYERS" 49 | MAXPLAYERS="4" 50 | fi 51 | printf "Setting max players to %s\\n" "$MAXPLAYERS" 52 | 53 | # GameUserSettings.ini settings. 54 | if [[ "${DISABLESEASONALEVENTS,,}" == "true" ]]; then 55 | printf "Disabling seasonal events\\n" 56 | DISABLESEASONALEVENTS="-DisableSeasonalEvents" 57 | else 58 | DISABLESEASONALEVENTS="" 59 | fi 60 | 61 | # Validate and set multihome address for network connections (useful for v6-only networks). 62 | if [[ "$MULTIHOME" != "" ]]; then 63 | if [[ "$MULTIHOME" != "" ]] && [[ "$MULTIHOME" != "::" ]]; then 64 | # IPv4 regex matches addresses from 0.0.0.0 to 255.255.255.255. 65 | IPv4='^([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$' 66 | 67 | # IPv6 regex supports full and shortened formats like 2001:db8::1. 68 | IPv6='^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$' 69 | 70 | if [[ "$MULTIHOME" =~ $IPv4 ]]; then 71 | printf "Multihome will accept IPv4 connections only\n" 72 | elif [[ "$MULTIHOME" =~ $IPv6 ]]; then 73 | printf "Multihome will accept IPv6 connections only\n" 74 | else 75 | printf "Invalid multihome address: %s (defaulting to ::)\n" "$MULTIHOME" 76 | MULTIHOME="::" 77 | fi 78 | fi 79 | 80 | if [[ "$MULTIHOME" == "::" ]]; then 81 | printf "Multihome will accept IPv4 and IPv6 connections\n" 82 | fi 83 | 84 | printf "Setting multihome to %s\n" "$MULTIHOME" 85 | MULTIHOME="-multihome=$MULTIHOME" 86 | fi 87 | 88 | ini_args=( 89 | "-ini:Engine:[/Script/FactoryGame.FGSaveSession]:mNumRotatingAutosaves=$AUTOSAVENUM" 90 | "-ini:Engine:[/Script/Engine.GarbageCollectionSettings]:gc.MaxObjectsInEditor=$MAXOBJECTS" 91 | "-ini:Engine:[/Script/OnlineSubsystemUtils.IpNetDriver]:LanServerMaxTickRate=$MAXTICKRATE" 92 | "-ini:Engine:[/Script/OnlineSubsystemUtils.IpNetDriver]:NetServerMaxTickRate=$MAXTICKRATE" 93 | "-ini:Engine:[/Script/OnlineSubsystemUtils.IpNetDriver]:ConnectionTimeout=$TIMEOUT" 94 | "-ini:Engine:[/Script/OnlineSubsystemUtils.IpNetDriver]:InitialConnectTimeout=$TIMEOUT" 95 | "-ini:Engine:[ConsoleVariables]:wp.Runtime.EnableServerStreaming=$SERVERSTREAMING" 96 | "-ini:Game:[/Script/Engine.GameSession]:ConnectionTimeout=$TIMEOUT" 97 | "-ini:Game:[/Script/Engine.GameSession]:InitialConnectTimeout=$TIMEOUT" 98 | "-ini:Game:[/Script/Engine.GameSession]:MaxPlayers=$MAXPLAYERS" 99 | "-ini:GameUserSettings:[/Script/Engine.GameSession]:MaxPlayers=$MAXPLAYERS" 100 | "$DISABLESEASONALEVENTS" 101 | "$MULTIHOME" 102 | ) 103 | 104 | if [[ "${SKIPUPDATE,,}" != "false" ]] && [ ! -f "/config/gamefiles/FactoryServer.sh" ]; then 105 | printf "%s Skip update is set, but no game files exist. Updating anyway\\n" "${MSGWARNING}" 106 | SKIPUPDATE="false" 107 | fi 108 | 109 | if [[ "${SKIPUPDATE,,}" != "true" ]]; then 110 | if [[ "${STEAMBETA,,}" == "true" ]]; then 111 | printf "Experimental flag is set. Experimental will be downloaded instead of Early Access.\\n" 112 | STEAMBETAFLAG="experimental" 113 | else 114 | STEAMBETAFLAG="public" 115 | fi 116 | 117 | STORAGEAVAILABLE=$(stat -f -c "%a*%S" .) 118 | STORAGEAVAILABLE=$((STORAGEAVAILABLE/1024/1024/1024)) 119 | printf "Checking available storage: %sGB detected\\n" "$STORAGEAVAILABLE" 120 | 121 | if [[ "$STORAGEAVAILABLE" -lt 8 ]]; then 122 | printf "You have less than 8GB (%sGB detected) of available storage to download the game.\\nIf this is a fresh install, it will probably fail.\\n" "$STORAGEAVAILABLE" 123 | fi 124 | 125 | printf "\\nDownloading the latest version of the game...\\n" 126 | if [ -f "/config/gamefiles/steamapps/appmanifest_1690800.acf" ]; then 127 | printf "\\nRemoving the app manifest to force Steam to check for an update...\\n" 128 | rm "/config/gamefiles/steamapps/appmanifest_1690800.acf" || true 129 | fi 130 | steamcmd +force_install_dir /config/gamefiles +login anonymous +app_update "$STEAMAPPID" -beta "$STEAMBETAFLAG" validate +quit 131 | cp -r /home/steam/.steam/steam/logs/* "/config/logs/steam" || printf "Failed to store Steam logs\\n" 132 | else 133 | printf "Skipping update as flag is set\\n" 134 | fi 135 | 136 | printf "Launching game server\\n\\n" 137 | 138 | cp -r "/config/saved/server/." "/config/backups/" 139 | cp -r "${GAMESAVESDIR}/server/." "/config/backups" # Useful after the first run. 140 | rm -rf "$GAMESAVESDIR" 141 | ln -sf "/config/saved" "$GAMESAVESDIR" 142 | 143 | if [ ! -f "/config/gamefiles/FactoryServer.sh" ]; then 144 | printf "FactoryServer launch script is missing.\\n" 145 | exit 1 146 | fi 147 | 148 | cd /config/gamefiles || exit 1 149 | 150 | chmod +x FactoryServer.sh || true 151 | ./FactoryServer.sh -Port="$SERVERGAMEPORT" -ReliablePort="$SERVERMESSAGINGPORT" -ExternalReliablePort="$SERVERMESSAGINGPORT" "${ini_args[@]}" "$@" & 152 | 153 | sleep 2 154 | satisfactory_pid=$(ps --ppid ${!} o pid=) 155 | 156 | shutdown() { 157 | printf "\\nReceived SIGINT. Shutting down.\\n" 158 | kill -INT $satisfactory_pid 2>/dev/null 159 | } 160 | trap shutdown SIGINT SIGTERM 161 | 162 | wait -------------------------------------------------------------------------------- /saveshare/README.md: -------------------------------------------------------------------------------- 1 | # Satisfactory Save Sharing 2 | 3 | **_Note: Prior to 1.0's release, the group I played with had been relying on solely this for many months. 1.0's release 4 | drastically improved the stability of dedicated servers, removing the need for SaveShare._** 5 | 6 | The dedicated server for Satisfactory introduces a few unique bugs to the game, where multiplayer (through joining a 7 | friend) doesn't. This application introduces save sharing with friends. It's designed to function similarly to how the 8 | game Grounded handles saves. 9 | 10 | Everybody runs the client in the background; when the host's game saves, those files are uploaded to a remote SFTP 11 | server (deployed through the Docker Compose below), which the other clients pull from in realtime. This way, if the host 12 | leaves, anyone else can pick up from where they left off. 13 | 14 | ## Setup 15 | 16 | Download the last build from [here](https://github.com/wolveix/satisfactory-server/releases/tag/1.8.2). When you 17 | initially run it, it'll ask for the following information: 18 | 19 | - Server address (IP and port, e.g. `localhost:7790`) 20 | - Server password (the SFTP password) 21 | - Session name (this must be EXACTLY as it is formatted within Satisfactory) 22 | 23 | ### Docker Compose 24 | 25 | If you're using [Docker Compose](https://docs.docker.com/compose/): 26 | 27 | ```yaml 28 | services: 29 | satisfactory-saveshare: 30 | container_name: satisfactory-saveshare 31 | image: atmoz/sftp:latest 32 | volumes: 33 | - /opt/saveshare:/home/saveshare/upload 34 | ports: 35 | - "7790:22" 36 | command: saveshare:PASSWORD_HERE:1001 37 | ``` 38 | 39 | _Note: Do not change the username (`saveshare`) or the UID (`1001`). Only change the password._ 40 | 41 | ### Known Issues 42 | 43 | You can't delete blueprints, unless you manually stop everyone from running the application and everyone deletes the 44 | blueprint locally (and server-side) 45 | -------------------------------------------------------------------------------- /saveshare/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | 9 | "gopkg.in/yaml.v3" 10 | ) 11 | 12 | type Config struct { 13 | gamePath string 14 | path string 15 | BlueprintPath string `yaml:"blueprintPath"` 16 | SavePath string `yaml:"savePath"` 17 | ServerAddress string `yaml:"serverAddress"` 18 | ServerPassword string `yaml:"serverPassword"` 19 | SessionName string `yaml:"sessionName"` 20 | } 21 | 22 | func NewConfig(configDir string) (*Config, error) { 23 | cfg := Config{ 24 | path: configDir + slash + "config.yml", 25 | } 26 | 27 | // If the file doesn't exist, create an empty file. 28 | if _, err := os.Stat(cfg.path); os.IsNotExist(err) { 29 | if _, err = os.Create(cfg.path); err != nil { 30 | return nil, fmt.Errorf("could not create config file: %w", err) 31 | } 32 | } 33 | 34 | yamlData, err := os.ReadFile(cfg.path) 35 | if err != nil { 36 | return nil, fmt.Errorf("could not read config file: %w", err) 37 | } 38 | 39 | if err = yaml.Unmarshal(yamlData, &cfg); err != nil { 40 | return nil, fmt.Errorf("could not unmarshal config file: %w", err) 41 | } 42 | 43 | // Populate the blueprint and save paths. 44 | appDataPath, err := os.UserCacheDir() 45 | if err != nil { 46 | return nil, fmt.Errorf("could not get appdata path: %w", err) 47 | } 48 | 49 | cfg.gamePath = appDataPath + slash + "FactoryGame" + slash + "Saved" + slash + "SaveGames" + slash 50 | 51 | if cfg.SessionName != "" { 52 | cfg.BlueprintPath = cfg.gamePath + "blueprints" + slash + cfg.SessionName 53 | } 54 | 55 | if _, err = os.Stat(cfg.gamePath); os.IsNotExist(err) { 56 | return nil, fmt.Errorf("game path does not exist: %w", err) 57 | } 58 | 59 | // Determine the save path. 60 | if err = filepath.Walk(cfg.gamePath, func(path string, info os.FileInfo, err error) error { 61 | if info == nil { 62 | return errors.New("path does not exist") 63 | } 64 | 65 | if info.IsDir() { 66 | if cfg.SavePath == "" && path != cfg.gamePath { 67 | cfg.SavePath = path 68 | } 69 | return nil 70 | } 71 | 72 | return nil 73 | }); err != nil { 74 | return nil, fmt.Errorf("could not walk over path: %w", err) 75 | } 76 | 77 | if cfg.SavePath == "" { 78 | return nil, fmt.Errorf("could not find save path") 79 | } 80 | 81 | return &cfg, nil 82 | } 83 | 84 | func (c *Config) Save() error { 85 | yamlData, err := yaml.Marshal(c) 86 | if err != nil { 87 | return fmt.Errorf("could not marshal config file: %w", err) 88 | } 89 | 90 | if err = os.WriteFile(c.path, yamlData, 0o644); err != nil { 91 | return fmt.Errorf("could not write config file: %w", err) 92 | } 93 | 94 | return nil 95 | } 96 | -------------------------------------------------------------------------------- /saveshare/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | satisfactory-saveshare: 3 | container_name: satisfactory-saveshare 4 | image: atmoz/sftp:latest 5 | volumes: 6 | - /opt/saveshare:/home/saveshare/upload 7 | ports: 8 | - "7790:22" 9 | command: saveshare:PASSWORD_HERE:1001 10 | -------------------------------------------------------------------------------- /saveshare/file.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | 8 | "github.com/pkg/sftp" 9 | ) 10 | 11 | func downloadFile(sftpClient *sftp.Client, remotePath, localPath string) error { 12 | remoteFile, err := sftpClient.Open(remotePath) 13 | if err != nil { 14 | return fmt.Errorf("error opening remote file: %v: %w", remotePath, err) 15 | } 16 | defer remoteFile.Close() 17 | 18 | localFile, err := os.Create(localPath) 19 | if err != nil { 20 | return fmt.Errorf("error creating local file: %w", err) 21 | } 22 | defer localFile.Close() 23 | 24 | if _, err = io.Copy(localFile, remoteFile); err != nil { 25 | return fmt.Errorf("error copying file: %w", err) 26 | } 27 | 28 | fileInfo, err := remoteFile.Stat() 29 | if err != nil { 30 | return fmt.Errorf("error getting remote file info: %w", err) 31 | } 32 | 33 | if err = os.Chtimes(localPath, fileInfo.ModTime(), fileInfo.ModTime()); err != nil { 34 | return fmt.Errorf("error setting local file mod time: %w", err) 35 | } 36 | 37 | return nil 38 | } 39 | 40 | func uploadFile(sftpClient *sftp.Client, localPath, remotePath string) error { 41 | localFile, err := os.Open(localPath) 42 | if err != nil { 43 | return fmt.Errorf("error opening local file: %w", err) 44 | } 45 | defer localFile.Close() 46 | 47 | remoteFile, err := sftpClient.Create(remotePath) 48 | if err != nil { 49 | return fmt.Errorf("error creating remote file: %w", err) 50 | } 51 | defer remoteFile.Close() 52 | 53 | if _, err = io.Copy(remoteFile, localFile); err != nil { 54 | return fmt.Errorf("error copying file: %w", err) 55 | } 56 | 57 | fileInfo, err := localFile.Stat() 58 | if err != nil { 59 | return fmt.Errorf("error getting local file info: %w", err) 60 | } 61 | 62 | if err = sftpClient.Chtimes(remotePath, fileInfo.ModTime(), fileInfo.ModTime()); err != nil { 63 | return fmt.Errorf("error setting remote file mod time: %w", err) 64 | } 65 | 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /saveshare/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/wolveix/satisfactory-server/saveshare 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.1 6 | 7 | require ( 8 | github.com/pkg/sftp v1.13.9 9 | github.com/rs/zerolog v1.34.0 10 | golang.org/x/crypto v0.37.0 11 | gopkg.in/yaml.v3 v3.0.1 12 | ) 13 | 14 | require ( 15 | github.com/kr/fs v0.1.0 // indirect 16 | github.com/mattn/go-colorable v0.1.14 // indirect 17 | github.com/mattn/go-isatty v0.0.20 // indirect 18 | golang.org/x/sys v0.32.0 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /saveshare/go.sum: -------------------------------------------------------------------------------- 1 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 6 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 7 | github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= 8 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 9 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 10 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 11 | github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= 12 | github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 13 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 14 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 15 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 16 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 17 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 18 | github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM= 19 | github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY= 20 | github.com/pkg/sftp v1.13.9 h1:4NGkvGudBL7GteO3m6qnaQ4pC0Kvf0onSVc9gR3EWBw= 21 | github.com/pkg/sftp v1.13.9/go.mod h1:OBN7bVXdstkFFN/gdnHPUb5TE8eb8G1Rp9wCItqjkkA= 22 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 23 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 24 | github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 25 | github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= 26 | github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= 27 | github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= 28 | github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= 29 | github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= 30 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 31 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 32 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 33 | github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 34 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 35 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 36 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 37 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 38 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= 39 | golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 40 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 41 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 42 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 43 | golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= 44 | golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= 45 | golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= 46 | golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= 47 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 48 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 49 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 50 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 51 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 52 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 53 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 54 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 55 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 56 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 57 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 58 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 59 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 60 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 61 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 62 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 63 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= 64 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 65 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 66 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 67 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 68 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 69 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 70 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 71 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 72 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 73 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 74 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 75 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 76 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 77 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 78 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 79 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 80 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 81 | golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= 82 | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 83 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= 84 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 85 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 86 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 87 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 88 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 89 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 90 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= 91 | golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= 92 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 93 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= 94 | golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 95 | golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= 96 | golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= 97 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 98 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 99 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 100 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 101 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 102 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 103 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 104 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 105 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 106 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 107 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 108 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 109 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 110 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 111 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 112 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 113 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 114 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 115 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 116 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 117 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 118 | -------------------------------------------------------------------------------- /saveshare/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | "time" 9 | 10 | "github.com/pkg/sftp" 11 | "github.com/rs/zerolog" 12 | "golang.org/x/crypto/ssh" 13 | ) 14 | 15 | var ( 16 | logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}).With().Timestamp().Logger().Level(zerolog.InfoLevel) 17 | logfile = "" 18 | slash = string(os.PathSeparator) 19 | ) 20 | 21 | func main() { 22 | configDir, err := os.UserConfigDir() 23 | if err != nil { 24 | logger.Fatal().Err(err).Msg("Failed to get user config directory") 25 | } 26 | 27 | configDir = filepath.Clean(configDir + slash + "FactoryGameSaveShare") 28 | logfile = configDir + slash + "log.txt" 29 | 30 | if err = os.MkdirAll(configDir, 0o755); err != nil { 31 | logger.Fatal().Err(err).Msg("Failed to create config directory") 32 | } 33 | 34 | // Create the log file if it doesn't already exist. 35 | if _, err = os.Stat(logfile); os.IsNotExist(err) { 36 | if _, err = os.Create(logfile); err != nil { 37 | logger.Fatal().Err(err).Msg("Failed to create log file") 38 | } 39 | } 40 | 41 | logFile, err := os.OpenFile(logfile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o600) 42 | if err != nil { 43 | logger.Fatal().Err(err).Msg("Failed to open log file") 44 | } 45 | 46 | // Replace the default logger with one that writes a file and to the console. 47 | logger = zerolog.New(zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}, logFile)).With().Timestamp().Logger().Level(zerolog.InfoLevel) 48 | 49 | logger.Info().Msg("Satisfactory Save Share Client v1.8.2") 50 | logger.Info().Msg("https://github.com/wolveix/satisfactory-server/saveshare") 51 | logger.Info().Msg("Initializing config...") 52 | 53 | cfg, err := NewConfig(configDir) 54 | if err != nil { 55 | logger.Fatal().Err(err).Msg("Failed to load config") 56 | return 57 | } 58 | 59 | if cfg.SessionName == "" { 60 | fmt.Printf("Please input your session name: ") 61 | 62 | if _, err = fmt.Scanln(&cfg.SessionName); err != nil { 63 | fmt.Printf("\nFailed to read session name: %v\n", err) 64 | return 65 | } 66 | 67 | if err = cfg.Save(); err != nil { 68 | logger.Fatal().Err(err).Msg("Failed to save config") 69 | } 70 | 71 | cfg.BlueprintPath = cfg.gamePath + slash + "blueprints" + slash + cfg.SessionName 72 | } 73 | 74 | if cfg.ServerAddress == "" { 75 | fmt.Printf("Please input your server address: ") 76 | 77 | if _, err = fmt.Scanln(&cfg.ServerAddress); err != nil { 78 | fmt.Printf("\nFailed to read server address: %v\n", err) 79 | return 80 | } 81 | 82 | if err = cfg.Save(); err != nil { 83 | logger.Fatal().Err(err).Msg("Failed to save config") 84 | } 85 | } 86 | 87 | if cfg.ServerPassword == "" { 88 | fmt.Printf("Please input your server password: ") 89 | 90 | if _, err = fmt.Scanln(&cfg.ServerPassword); err != nil { 91 | fmt.Printf("\nFailed to read server password: %v\n", err) 92 | return 93 | } 94 | 95 | if err = cfg.Save(); err != nil { 96 | logger.Fatal().Err(err).Msg("Failed to save config") 97 | } 98 | } 99 | 100 | logger.Info().Msg("Config loaded successfully!") 101 | 102 | // Establish an SSH connection to the server. 103 | sshConfig := &ssh.ClientConfig{ 104 | User: "saveshare", 105 | Auth: []ssh.AuthMethod{ 106 | ssh.Password(cfg.ServerPassword), 107 | }, 108 | HostKeyCallback: ssh.InsecureIgnoreHostKey(), 109 | } 110 | 111 | sshClient, err := ssh.Dial("tcp", cfg.ServerAddress, sshConfig) 112 | if err != nil { 113 | logger.Fatal().Err(err).Msg("Error connecting to server") 114 | } 115 | defer sshClient.Close() 116 | 117 | // Establish an SFTP session. 118 | sftpClient, err := sftp.NewClient(sshClient) 119 | if err != nil { 120 | logger.Fatal().Err(err).Msg("Error creating SFTP client") 121 | return 122 | } 123 | defer sftpClient.Close() 124 | 125 | logger.Info().Msg("Connected to server!") 126 | 127 | remotePath := "upload/" + cfg.SessionName 128 | 129 | // Create the remote directories if they don't already exist. 130 | if err = sftpClient.MkdirAll(remotePath + "/blueprints"); err != nil { 131 | fmt.Printf("Error creating remote blueprints directory: %v\n", err) 132 | return 133 | } 134 | 135 | if err = sftpClient.MkdirAll(remotePath + "/saves"); err != nil { 136 | fmt.Printf("Error creating remote saves directory: %v\n", err) 137 | return 138 | } 139 | 140 | for { 141 | logger.Info().Msg("Syncing...") 142 | 143 | if err = syncLocalUpdates(sftpClient, cfg.BlueprintPath, remotePath+"/blueprints", ""); err != nil { 144 | logger.Error().Err(err).Msg("Unexpected error while checking for blueprint updates") 145 | } 146 | 147 | if err = syncLocalUpdates(sftpClient, cfg.SavePath, remotePath+"/saves", cfg.SessionName); err != nil { 148 | logger.Error().Err(err).Msg("Unexpected error while checking for save updates") 149 | } 150 | 151 | if err = syncRemoteUpdates(sftpClient, cfg.BlueprintPath, remotePath+"/blueprints"); err != nil { 152 | logger.Error().Err(err).Msg("Unexpected error while checking for blueprint updates") 153 | } 154 | 155 | if err = syncRemoteUpdates(sftpClient, cfg.SavePath, remotePath+"/saves"); err != nil { 156 | logger.Error().Err(err).Msg("Unexpected error while checking for save updates") 157 | } 158 | 159 | time.Sleep(2 * time.Minute) 160 | } 161 | } 162 | 163 | // syncLocalUpdates walks through the local directories and syncs files to/from the remote server 164 | func syncLocalUpdates(sftpClient *sftp.Client, localPath string, remotePath string, sessionName string) error { 165 | return filepath.Walk(localPath, func(localFilePath string, info os.FileInfo, err error) error { 166 | if err != nil { 167 | return err 168 | } 169 | 170 | if info.IsDir() { 171 | return nil 172 | } 173 | 174 | remoteFilePath := remotePath + "/" + info.Name() 175 | 176 | // Save files are named like: "SESSIONNAME_autosave_0.sav". 177 | if sessionName != "" { 178 | if !strings.HasPrefix(info.Name(), sessionName) { 179 | return nil 180 | } 181 | } 182 | 183 | // Check the file exists on the remote server. 184 | remoteFile, err := sftpClient.Stat(remoteFilePath) 185 | if err != nil { 186 | if os.IsNotExist(err) { 187 | logger.Info().Msg("Uploading " + localFilePath + " to " + remoteFilePath) 188 | return uploadFile(sftpClient, localFilePath, remoteFilePath) 189 | } else { 190 | return fmt.Errorf("error checking remote file: %w", err) 191 | } 192 | } 193 | 194 | // Compare local and remote file timestamps. 195 | localModTime := info.ModTime().Truncate(1 * time.Second) 196 | remoteModTime := remoteFile.ModTime().Truncate(1 * time.Second) 197 | 198 | if localModTime == remoteModTime { 199 | return nil 200 | } 201 | 202 | if localModTime.After(remoteModTime) { 203 | // Local file is newer, so upload it. 204 | logger.Info().Msg("Uploading " + localFilePath + " to " + remoteFilePath) 205 | return uploadFile(sftpClient, localFilePath, remoteFilePath) 206 | } else { 207 | // Remote file is newer, so download it. 208 | logger.Info().Msg("Downloading " + remoteFilePath + " to " + localFilePath) 209 | return downloadFile(sftpClient, remoteFilePath, localFilePath) 210 | } 211 | }) 212 | } 213 | 214 | func syncRemoteUpdates(sftpClient *sftp.Client, localPath string, remotePath string) error { 215 | // Get a list of files and directories from the remote directory. 216 | remoteFiles, err := sftpClient.ReadDir(remotePath) 217 | if err != nil { 218 | return fmt.Errorf("error reading remote directory: %w", err) 219 | } 220 | 221 | // Iterate through remote files and directories. 222 | for _, remoteFile := range remoteFiles { 223 | if remoteFile.IsDir() { 224 | return nil 225 | } 226 | 227 | localFilePath := localPath + slash + remoteFile.Name() 228 | remoteFilePath := remotePath + "/" + remoteFile.Name() 229 | 230 | // Check if the file exists locally. 231 | localFile, err := os.Stat(localFilePath) 232 | if err != nil { 233 | if os.IsNotExist(err) { 234 | // Download the remote file. 235 | logger.Info().Msg("Downloading " + remoteFilePath + " to " + localFilePath) 236 | if err = downloadFile(sftpClient, remoteFilePath, localFilePath); err != nil { 237 | return err 238 | } 239 | 240 | continue 241 | } 242 | 243 | return fmt.Errorf("error checking local file: %w", err) 244 | } 245 | 246 | // Compare remote and local file timestamps. 247 | localModTime := localFile.ModTime().Truncate(1 * time.Second) 248 | remoteModTime := remoteFile.ModTime().Truncate(1 * time.Second) 249 | 250 | // Compare timestamps and synchronize as needed. 251 | if localModTime == remoteModTime { 252 | continue 253 | } 254 | 255 | if remoteModTime.After(localModTime) { 256 | // Remote file is newer, download it. 257 | logger.Info().Msg("Downloading " + remoteFilePath + " to " + localFilePath) 258 | if err = downloadFile(sftpClient, remoteFilePath, localFilePath); err != nil { 259 | return err 260 | } 261 | } else { 262 | // Local file is newer, upload it. 263 | logger.Info().Msg("Uploading " + localFilePath + " to " + remoteFilePath) 264 | if err = uploadFile(sftpClient, localFilePath, remoteFilePath); err != nil { 265 | return err 266 | } 267 | } 268 | } 269 | 270 | return nil 271 | } 272 | -------------------------------------------------------------------------------- /server.log: -------------------------------------------------------------------------------- 1 | ===== Satisfactory Server v1.8.1 ===== 2 | https://github.com/wolveix/satisfactory-server 3 | 4 | Checking available memory: 38GB detected 5 | Setting autosave number to 5 6 | Setting max objects to 2162688 7 | Setting max tick rate to 30 8 | Setting server streaming to 1 9 | Setting timeout to 30 10 | Checking available storage: 76GB detected 11 | 12 | Downloading the latest version of the game... 13 | Redirecting stderr to '/home/steam/.steam/steam/logs/stderr.txt' 14 | ILocalize::AddFile() failed to load file "public/steambootstrapper_english.txt". 15 | [ 0%] Checking for available update... 16 | [----] Downloading update (0 of 48,910 KB)... 17 | [ 0%] Downloading update (0 of 48,910 KB)... 18 | [ 0%] Downloading update (0 of 48,910 KB)... 19 | [ 0%] Downloading update (3,106 of 48,910 KB)... 20 | [ 6%] Downloading update (6,877 of 48,910 KB)... 21 | [ 14%] Downloading update (9,656 of 48,910 KB)... 22 | [ 19%] Downloading update (12,152 of 48,910 KB)... 23 | [ 24%] Downloading update (14,891 of 48,910 KB)... 24 | [ 30%] Downloading update (17,380 of 48,910 KB)... 25 | [ 35%] Downloading update (19,875 of 48,910 KB)... 26 | [ 40%] Downloading update (22,627 of 48,910 KB)... 27 | [ 46%] Downloading update (25,109 of 48,910 KB)... 28 | [ 51%] Downloading update (27,598 of 48,910 KB)... 29 | [ 56%] Downloading update (30,296 of 48,910 KB)... 30 | [ 61%] Downloading update (32,766 of 48,910 KB)... 31 | [ 66%] Downloading update (35,241 of 48,910 KB)... 32 | [ 72%] Downloading update (37,988 of 48,910 KB)... 33 | [ 77%] Downloading update (40,488 of 48,910 KB)... 34 | [ 82%] Downloading update (42,336 of 48,910 KB)... 35 | [ 86%] Downloading update (43,710 of 48,910 KB)... 36 | [ 89%] Downloading update (44,954 of 48,910 KB)... 37 | [ 91%] Downloading update (46,199 of 48,910 KB)... 38 | [ 94%] Downloading update (47,564 of 48,910 KB)... 39 | [ 97%] Downloading update (48,800 of 48,910 KB)... 40 | [ 99%] Downloading update (48,910 of 48,910 KB)... 41 | [100%] Download Complete. 42 | [----] Applying update... 43 | [----] Extracting package... 44 | [----] Extracting package... 45 | [----] Extracting package... 46 | [----] Extracting package... 47 | [----] Installing update... 48 | [----] Installing update... 49 | [----] Installing update... 50 | [----] Installing update... 51 | [----] Installing update... 52 | [----] Installing update... 53 | [----] Installing update... 54 | [----] Installing update... 55 | [----] Installing update... 56 | [----] Cleaning up... 57 | [----] Update complete, launching... 58 | Redirecting stderr to '/home/steam/.steam/steam/logs/stderr.txt' 59 | Logging directory: '/home/steam/.steam/steam/logs' 60 | [ 0%] Checking for available updates... 61 | [----] Verifying installation... 62 | [ 0%] Downloading update... 63 | [ 0%] Checking for available updates... 64 | [----] Download complete. 65 | [----] Extracting package... 66 | [----] Extracting package... 67 | [----] Extracting package... 68 | [----] Extracting package... 69 | [----] Installing update... 70 | [----] Installing update... 71 | [----] Installing update... 72 | [----] Installing update... 73 | [----] Installing update... 74 | [----] Installing update... 75 | [----] Installing update... 76 | [----] Installing update... 77 | [----] Installing update... 78 | [----] Cleaning up... 79 | [----] Update complete, launching Steamcmd... 80 | steamcmd.sh[37]: Restarting steamcmd by request... 81 | UpdateUI: skip show logoRedirecting stderr to '/home/steam/.steam/steam/logs/stderr.txt' 82 | Logging directory: '/home/steam/.steam/steam/logs' 83 | [ 0%] Checking for available updates... 84 | [----] Verifying installation... 85 | UpdateUI: skip show logoSteam Console Client (c) Valve Corporation - version 1726088194 86 | -- type 'quit' to exit -- 87 | Loading Steam API...OK 88 | 89 | Connecting anonymously to Steam Public...OK 90 | Waiting for client config...OK 91 | Waiting for user info...OK 92 | Update state (0x3) reconfiguring, progress: 0.00 (0 / 0) 93 | Update state (0x11) preallocating, progress: 74.47 (3204820681 / 4303569976) 94 | Update state (0x61) downloading, progress: 2.55 (109931692 / 4303569976) 95 | Update state (0x61) downloading, progress: 20.56 (884725305 / 4303569976) 96 | Update state (0x61) downloading, progress: 36.07 (1552352473 / 4303569976) 97 | Update state (0x61) downloading, progress: 50.65 (2179756179 / 4303569976) 98 | Update state (0x61) downloading, progress: 65.36 (2812863411 / 4303569976) 99 | Update state (0x61) downloading, progress: 80.68 (3472049822 / 4303569976) 100 | Update state (0x61) downloading, progress: 88.07 (3790212476 / 4303569976) 101 | Update state (0x61) downloading, progress: 93.70 (4032433532 / 4303569976) 102 | Update state (0x61) downloading, progress: 99.25 (4271484367 / 4303569976) 103 | Update state (0x81) verifying update, progress: 24.25 (1043434135 / 4303569976) 104 | Update state (0x81) verifying update, progress: 59.43 (2557552804 / 4303569976) 105 | Update state (0x81) verifying update, progress: 94.02 (4046092394 / 4303569976) 106 | Work thread 'CContentUpdateContext::m_pCPUTh' is marked exited, but we could not immediately join prior to deleting -- proceeding without join 107 | Success! App '1690800' fully installed. 108 | Launching game server 109 | 110 | Project file not found: /config/gamefiles/FactoryGame/FactoryGame.uproject 111 | 5.3.2-365306+++FactoryGame+rel-main-1.0.0 1009 3 112 | Disabling core dumps. 113 | LogInit: Display: Running engine for game: FactoryGame 114 | LogInit: Display: Project file not found: /config/gamefiles/FactoryGame/FactoryGame.uproject 115 | LogInit: Display: Attempting to find via project info helper. 116 | LogUProjectInfo: Found projects: 117 | LogPakFile: Initializing PakPlatformFile 118 | LogIoDispatcher: Display: Reading toc: ../../../FactoryGame/Content/Paks/global.utoc 119 | LogIoDispatcher: Display: Toc signature hash: 83989862C6199616CC2D9564D32FE64C93DCA681 120 | LogIoDispatcher: Display: Mounting container '../../../FactoryGame/Content/Paks/global.utoc' in location slot 0 121 | LogPakFile: Display: Initialized I/O dispatcher file backend. Mounted the global container: ../../../FactoryGame/Content/Paks/global.utoc 122 | LogPakFile: Display: Found Pak file ../../../FactoryGame/Content/Paks/FactoryGame-LinuxServer.pak attempting to mount. 123 | LogPakFile: Display: Mounting pak file ../../../FactoryGame/Content/Paks/FactoryGame-LinuxServer.pak. 124 | LogIoDispatcher: Display: Reading toc: ../../../FactoryGame/Content/Paks/FactoryGame-LinuxServer.utoc 125 | LogIoDispatcher: Display: Toc signature hash: EF49DC5F59B6CD0160DCE0B142695B01F4C4A5B0 126 | LogIoDispatcher: Display: Mounting container '../../../FactoryGame/Content/Paks/FactoryGame-LinuxServer.utoc' in location slot 0 127 | LogPakFile: Display: Mounted IoStore container "../../../FactoryGame/Content/Paks/FactoryGame-LinuxServer.utoc" 128 | LogPakFile: Display: Mounted Pak file '../../../FactoryGame/Content/Paks/FactoryGame-LinuxServer.pak', mount point: '../../../' 129 | LogICUInternationalization: ICU TimeZone Detection - Raw Offset: +0:00, Platform Override: '' 130 | LogInit: Session CrashGUID >==================================================== 131 | Session CrashGUID > UECC-Linux-F41E128A7474423DA8566CD5C8AC9B21 132 | Session CrashGUID >==================================================== 133 | LogConfig: Using compiled CustomConfig FactoryServer 134 | LogConfig: Warning: Found a deprecated ini section name in ../../../FactoryGame/Config/DefaultEngine.ini. Search for [/Script/Engine.TextureLODSettings] and replace with [GlobalDefaults DeviceProfile] 135 | LogAssetRegistry: Premade AssetRegistry loaded from '../../../FactoryGame/AssetRegistry.bin' 136 | LogConfig: Warning: Found a deprecated ini section name in ../../../FactoryGame/Config/DefaultDeviceProfiles.ini. Search for [/Script/Engine.TextureLODSettings] and replace with [GlobalDefaults DeviceProfile] 137 | LogStreaming: Warning: Failed to read file '/config/gamefiles/Cloud/IoStoreOnDemand.ini' error. 138 | LogPluginManager: Mounting Engine plugin Paper2D 139 | LogPluginManager: Mounting Engine plugin AISupport 140 | LogPluginManager: Mounting Engine plugin EnvironmentQueryEditor 141 | LogPluginManager: Mounting Engine plugin ACLPlugin 142 | LogPluginManager: Mounting Engine plugin AnimationData 143 | LogPluginManager: Mounting Engine plugin ControlRigSpline 144 | LogPluginManager: Mounting Engine plugin ControlRig 145 | LogPluginManager: Mounting Engine plugin DeformerGraph 146 | LogPluginManager: Mounting Engine plugin IKRig 147 | LogPluginManager: Mounting Engine plugin Bridge 148 | LogPluginManager: Mounting Engine plugin CameraShakePreviewer 149 | LogPluginManager: Mounting Engine plugin GameplayCameras 150 | LogPluginManager: Mounting Engine plugin LensDistortion 151 | LogPluginManager: Mounting Engine plugin OodleNetwork 152 | LogPluginManager: Mounting Engine plugin DSTelemetry 153 | LogPluginManager: Mounting Engine plugin AnimationSharing 154 | LogPluginManager: Mounting Engine plugin PluginUtils 155 | LogPluginManager: Mounting Engine plugin UObjectPlugin 156 | LogPluginManager: Mounting Engine plugin AssetManagerEditor 157 | LogPluginManager: Mounting Engine plugin BlueprintHeaderView 158 | LogPluginManager: Mounting Engine plugin EditorScriptingUtilities 159 | LogPluginManager: Mounting Engine plugin FacialAnimation 160 | LogPluginManager: Mounting Engine plugin GeometryMode 161 | LogPluginManager: Mounting Engine plugin LightMixer 162 | LogPluginManager: Mounting Engine plugin ObjectMixer 163 | LogPluginManager: Mounting Engine plugin SequencerAnimTools 164 | LogPluginManager: Mounting Engine plugin SpeedTreeImporter 165 | LogPluginManager: Mounting Engine plugin UVEditor 166 | LogPluginManager: Mounting Engine plugin EnhancedInput 167 | LogPluginManager: Found config from plugin[EnhancedInput] Input 168 | LogPluginManager: Mounting Engine plugin DatasmithContent 169 | LogPluginManager: Mounting Engine plugin GLTFExporter 170 | LogPluginManager: Mounting Engine plugin VariantManagerContent 171 | LogPluginManager: Mounting Engine plugin VariantManager 172 | LogPluginManager: Mounting Engine plugin AutomationUtils 173 | LogPluginManager: Mounting Engine plugin BackChannel 174 | LogPluginManager: Mounting Engine plugin BlueprintStats 175 | LogPluginManager: Mounting Engine plugin ChaosCaching 176 | LogPluginManager: Mounting Engine plugin ChaosClothEditor 177 | LogPluginManager: Mounting Engine plugin ChaosCloth 178 | LogPluginManager: Mounting Engine plugin ChaosEditor 179 | LogPluginManager: Mounting Engine plugin ChaosNiagara 180 | LogPluginManager: Mounting Engine plugin ChaosSolverPlugin 181 | LogPluginManager: Mounting Engine plugin ChaosUserDataPT 182 | LogPluginManager: Mounting Engine plugin ChaosVehiclesPlugin 183 | LogPluginManager: Mounting Engine plugin CharacterAI 184 | LogPluginManager: Mounting Engine plugin ControlFlows 185 | LogPluginManager: Mounting Engine plugin Dataflow 186 | LogPluginManager: Mounting Engine plugin Fracture 187 | LogPluginManager: Mounting Engine plugin FullBodyIK 188 | LogPluginManager: Mounting Engine plugin GeometryCollectionPlugin 189 | LogPluginManager: Mounting Engine plugin GeometryScripting 190 | LogPluginManager: Mounting Engine plugin Landmass 191 | LogPluginManager: Mounting Engine plugin LocalizableMessage 192 | LogPluginManager: Mounting Engine plugin PlanarCut 193 | LogPluginManager: Mounting Engine plugin PlatformCrypto 194 | LogPluginManager: Mounting Engine plugin ProxyLODPlugin 195 | LogPluginManager: Mounting Engine plugin PythonScriptPlugin 196 | LogPluginManager: Mounting Engine plugin RawInput 197 | LogPluginManager: Found config from plugin[RawInput] Input 198 | LogPluginManager: Mounting Engine plugin StructUtils 199 | LogPluginManager: Mounting Engine plugin ToolPresets 200 | LogPluginManager: Mounting Engine plugin Niagara 201 | LogPluginManager: Mounting Engine plugin Fab 202 | LogPluginManager: Mounting Engine plugin AlembicImporter 203 | LogPluginManager: Mounting Engine plugin InterchangeEditor 204 | LogPluginManager: Mounting Engine plugin Interchange 205 | LogPluginManager: Mounting Engine plugin ImgMedia 206 | LogPluginManager: Mounting Engine plugin MediaCompositing 207 | LogPluginManager: Mounting Engine plugin MediaPlate 208 | LogPluginManager: Mounting Engine plugin MfMedia 209 | LogPluginManager: Mounting Engine plugin MeshPainting 210 | LogPluginManager: Mounting Engine plugin TcpMessaging 211 | LogPluginManager: Mounting Engine plugin UdpMessaging 212 | LogPluginManager: Mounting Engine plugin ActorSequence 213 | LogPluginManager: Mounting Engine plugin LevelSequenceEditor 214 | LogPluginManager: Mounting Engine plugin SequencerScripting 215 | LogPluginManager: Mounting Engine plugin TemplateSequence 216 | LogPluginManager: Mounting Engine plugin OnlineBase 217 | LogPluginManager: Mounting Engine plugin OnlineFramework 218 | LogPluginManager: Mounting Engine plugin OnlineServicesNull 219 | LogPluginManager: Found config from plugin[OnlineServicesNull] Engine 220 | LogPluginManager: Mounting Engine plugin OnlineServicesOSSAdapter 221 | LogPluginManager: Mounting Engine plugin OnlineServices 222 | LogPluginManager: Mounting Engine plugin OnlineSubsystemNull 223 | LogPluginManager: Mounting Engine plugin OnlineSubsystemUtils 224 | LogPluginManager: Mounting Engine plugin OnlineSubsystem 225 | LogPluginManager: Mounting Engine plugin LauncherChunkInstaller 226 | LogPluginManager: Mounting Engine plugin ActorLayerUtilities 227 | LogPluginManager: Mounting Engine plugin AndroidFileServer 228 | LogPluginManager: Mounting Engine plugin ApexDestruction 229 | LogPluginManager: Mounting Engine plugin AppleImageUtils 230 | LogPluginManager: Mounting Engine plugin AssetTags 231 | LogPluginManager: Mounting Engine plugin AudioCapture 232 | LogPluginManager: Mounting Engine plugin AudioSynesthesia 233 | LogPluginManager: Mounting Engine plugin AudioWidgets 234 | LogPluginManager: Mounting Engine plugin CableComponent 235 | LogPluginManager: Mounting Engine plugin ChunkDownloader 236 | LogPluginManager: Mounting Engine plugin ComputeFramework 237 | LogPluginManager: Mounting Engine plugin CustomMeshComponent 238 | LogPluginManager: Mounting Engine plugin ExampleDeviceProfileSelector 239 | LogPluginManager: Mounting Engine plugin GeometryCache 240 | LogPluginManager: Mounting Engine plugin GeometryProcessing 241 | LogPluginManager: Mounting Engine plugin GooglePAD 242 | LogPluginManager: Mounting Engine plugin HairStrands 243 | LogPluginManager: Mounting Engine plugin LinuxDeviceProfileSelector 244 | LogPluginManager: Mounting Engine plugin MeshModelingToolset 245 | LogPluginManager: Mounting Engine plugin Metasound 246 | LogPluginManager: Mounting Engine plugin ModelViewViewModel 247 | LogPluginManager: Mounting Engine plugin MsQuic 248 | LogPluginManager: Mounting Engine plugin DTLSHandlerComponent 249 | LogPluginManager: Mounting Engine plugin ProceduralMeshComponent 250 | LogPluginManager: Mounting Engine plugin PropertyAccessEditor 251 | LogPluginManager: Mounting Engine plugin ReplicationGraph 252 | LogPluginManager: Mounting Engine plugin ResonanceAudio 253 | LogPluginManager: Mounting Engine plugin RigVM 254 | LogPluginManager: Mounting Engine plugin SignificanceManager 255 | LogPluginManager: Mounting Engine plugin SoundFields 256 | LogPluginManager: Mounting Engine plugin Synthesis 257 | LogPluginManager: Mounting Engine plugin WaveTable 258 | LogPluginManager: Mounting Engine plugin WindowsDeviceProfileSelector 259 | LogPluginManager: Mounting Engine plugin EditorTests 260 | LogPluginManager: Mounting Engine plugin FbxAutomationTestBuilder 261 | LogPluginManager: Mounting Engine plugin FunctionalTestingEditor 262 | LogPluginManager: Mounting Engine plugin InterchangeTests 263 | LogPluginManager: Mounting Engine plugin RuntimeTests 264 | LogPluginManager: Mounting Engine plugin TraceUtilities 265 | LogPluginManager: Mounting Engine plugin Takes 266 | LogPluginManager: Mounting Engine plugin WwiseNiagara 267 | LogPluginManager: Mounting Engine plugin Wwise 268 | LogPluginManager: Mounting Project plugin AbstractInstance 269 | LogPluginManager: Mounting Project plugin EditorTools 270 | LogPluginManager: Mounting Project plugin GameplayEvents 271 | LogPluginManager: Mounting Project plugin InstancedSplines 272 | LogPluginManager: Mounting Project plugin OnlineIntegration 273 | LogPluginManager: Mounting Project plugin SignificanceISPC 274 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/2D/Paper2D/Content/' mounted to '/Paper2D/' 275 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Animation/ACLPlugin/Content/' mounted to '/ACLPlugin/' 276 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Animation/ControlRigSpline/Content/' mounted to '/ControlRigSpline/' 277 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Animation/ControlRig/Content/' mounted to '/ControlRig/' 278 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Animation/DeformerGraph/Content/' mounted to '/DeformerGraph/' 279 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Animation/IKRig/Content/' mounted to '/IKRig/' 280 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Bridge/Content/' mounted to '/Bridge/' 281 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Compositing/LensDistortion/Content/' mounted to '/LensDistortion/' 282 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Developer/AnimationSharing/Content/' mounted to '/AnimationSharing/' 283 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/BlueprintHeaderView/Content/' mounted to '/BlueprintHeaderView/' 284 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/GeometryMode/Content/' mounted to '/GeometryMode/' 285 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/ObjectMixer/LightMixer/Content/' mounted to '/LightMixer/' 286 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/ObjectMixer/ObjectMixer/Content/' mounted to '/ObjectMixer/' 287 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/SpeedTreeImporter/Content/' mounted to '/SpeedTreeImporter/' 288 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/UVEditor/Content/' mounted to '/UVEditor/' 289 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Enterprise/DatasmithContent/Content/' mounted to '/DatasmithContent/' 290 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Enterprise/GLTFExporter/Content/' mounted to '/GLTFExporter/' 291 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ChaosCaching/Content/' mounted to '/ChaosCaching/' 292 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ChaosClothEditor/Content/' mounted to '/ChaosClothEditor/' 293 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ChaosNiagara/Content/' mounted to '/ChaosNiagara/' 294 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ChaosSolverPlugin/Content/' mounted to '/ChaosSolverPlugin/' 295 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ChaosVehiclesPlugin/Content/' mounted to '/ChaosVehiclesPlugin/' 296 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ControlFlows/Content/' mounted to '/ControlFlows/' 297 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/Dataflow/Content/' mounted to '/Dataflow/' 298 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/FullBodyIK/Content/' mounted to '/FullBodyIK/' 299 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/GeometryCollectionPlugin/Content/' mounted to '/GeometryCollectionPlugin/' 300 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/GeometryScripting/Content/' mounted to '/GeometryScripting/' 301 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/Landmass/Content/' mounted to '/Landmass/' 302 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/PythonScriptPlugin/Content/' mounted to '/PythonScriptPlugin/' 303 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ToolPresets/Content/' mounted to '/ToolPresets/' 304 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/FX/Niagara/Content/' mounted to '/Niagara/' 305 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Fab/Content/' mounted to '/Fab/' 306 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Interchange/Runtime/Content/' mounted to '/Interchange/' 307 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Media/MediaCompositing/Content/' mounted to '/MediaCompositing/' 308 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Media/MediaPlate/Content/' mounted to '/MediaPlate/' 309 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/MovieScene/SequencerScripting/Content/' mounted to '/SequencerScripting/' 310 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/AudioSynesthesia/Content/' mounted to '/AudioSynesthesia/' 311 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/AudioWidgets/Content/' mounted to '/AudioWidgets/' 312 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/GeometryProcessing/Content/' mounted to '/GeometryProcessing/' 313 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/HairStrands/Content/' mounted to '/HairStrands/' 314 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/MeshModelingToolset/Content/' mounted to '/MeshModelingToolset/' 315 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/Metasound/Content/' mounted to '/Metasound/' 316 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/ResonanceAudio/Content/' mounted to '/ResonanceAudio/' 317 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/RigVM/Content/' mounted to '/RigVM/' 318 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/Synthesis/Content/' mounted to '/Synthesis/' 319 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/WaveTable/Content/' mounted to '/WaveTable/' 320 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Tests/EditorTests/Content/' mounted to '/EditorTests/' 321 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Tests/RuntimeTests/Content/' mounted to '/RuntimeTests/' 322 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/TraceUtilities/Content/' mounted to '/TraceUtilities/' 323 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/VirtualProduction/Takes/Content/' mounted to '/Takes/' 324 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/WwiseNiagara/Content/' mounted to '/WwiseNiagara/' 325 | LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Wwise/Content/' mounted to '/Wwise/' 326 | LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/AbstractInstance/Content/' mounted to '/AbstractInstance/' 327 | LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/EditorTools/Content/' mounted to '/EditorTools/' 328 | LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/GameplayEvents/Content/' mounted to '/GameplayEvents/' 329 | LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/InstancedSplines/Content/' mounted to '/InstancedSplines/' 330 | LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/Online/OnlineIntegration/Content/' mounted to '/OnlineIntegration/' 331 | LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/SignificanceISPC/Content/' mounted to '/SignificanceISPC/' 332 | LogHAL: Log category LogOnlineSession verbosity has been raised to Verbose. 333 | LogInit: Using libcurl 8.4.0 334 | LogInit: - built for x86_64-unknown-linux-gnu 335 | LogInit: - supports SSL with OpenSSL/1.1.1t 336 | LogInit: - supports HTTP deflate (compression) using libz 1.2.13 337 | LogInit: - other features: 338 | LogInit: CURL_VERSION_SSL 339 | LogInit: CURL_VERSION_LIBZ 340 | LogInit: CURL_VERSION_IPV6 341 | LogInit: CURL_VERSION_ASYNCHDNS 342 | LogInit: CURL_VERSION_LARGEFILE 343 | LogInit: CURL_VERSION_TLSAUTH_SRP 344 | LogInit: CurlRequestOptions (configurable via config and command line): 345 | LogInit: - bVerifyPeer = true - Libcurl will verify peer certificate 346 | LogInit: - bUseHttpProxy = false - Libcurl will NOT use HTTP proxy 347 | LogInit: - bDontReuseConnections = false - Libcurl will reuse connections 348 | LogInit: - MaxHostConnections = 16 - Libcurl will limit the number of connections to a host 349 | LogInit: - LocalHostAddr = Default 350 | LogInit: - BufferSize = 65536 351 | LogOnline: Warning: OSS: TryLoadSubsystemAndSetDefault: LoadSubsystemModule([None]) failed 352 | LogOnline: OSS: Created online subsystem instance for: NULL 353 | LogOnline: OSS: TryLoadSubsystemAndSetDefault: Loaded subsystem for type [NULL] 354 | LogInit: ExecutableName: FactoryServer-Linux-Shipping 355 | LogInit: Build: ++FactoryGame+rel-main-1.0.0-CL-365306 356 | LogInit: Engine Version: 5.3.2-365306+++FactoryGame+rel-main-1.0.0 357 | LogInit: Compatible Engine Version: 5.3.2-365306+++FactoryGame+rel-main-1.0.0 358 | LogInit: Net CL: 365306 359 | LogInit: OS: Ubuntu 22.04.4 LTS (6.2.16-10-pve), CPU: AMD Ryzen 9 5900X 12-Core Processor, GPU: NVIDIA PCI-id: 10de-1401 (1462-3201) 360 | LogInit: Compiled (64-bit): Sep 9 2024 11:32:40 361 | LogInit: Architecture: x64 362 | LogInit: Compiled with Clang: 15.0.1 (https://github.com/llvm/llvm-project b73d2c8c720a8c8e6e73b11be4e27afa6cb75bdf) 363 | LogInit: Build Configuration: Shipping 364 | LogInit: Branch Name: ++FactoryGame+rel-main-1.0.0 365 | LogInit: Command Line: -Port=7777 -ini:Engine:[HTTPServer.Listeners]:DefaultBindAddress=any -ini:Engine:[Core.Log]:LogNet=Error -ini:Engine:[Core.Log]:LogNetTraffic=Warning -ini:Engine:[/Script/FactoryGame.FGSaveSession]:mNumRotatingAutosaves=5 -ini:Engine:[/Script/Engine.GarbageCollectionSettings]:gc.MaxObjectsInEditor=2162688 -ini:Engine:[/Script/OnlineSubsystemUtils.IpNetDriver]:LanServerMaxTickRate=30 -ini:Engine:[/Script/OnlineSubsystemUtils.IpNetDriver]:NetServerMaxTickRate=30 -ini:Engine:[/Script/OnlineSubsystemUtils.IpNetDriver]:ConnectionTimeout=30 -ini:Engine:[/Script/OnlineSubsystemUtils.IpNetDriver]:InitialConnectTimeout=30 -ini:Engine:[ConsoleVariables]:wp.Runtime.EnableServerStreaming=1 -ini:Game:[/Script/Engine.GameSession]:ConnectionTimeout=30 -ini:Game:[/Script/Engine.GameSession]:InitialConnectTimeout=30 -ini:Game:[/Script/Engine.GameSession]:MaxPlayers=6 -ini:GameUserSettings:[/Script/Engine.GameSession]:MaxPlayers=6 366 | LogInit: Base Directory: /config/gamefiles/Engine/Binaries/Linux/ 367 | LogInit: Allocator: binned2 368 | LogInit: Installed Engine Build: 0 369 | LogInit: This binary is optimized with LTO: no, PGO: no, instrumented for PGO data collection: no 370 | LogInit: Launcher File: /config/gamefiles/launcher_id 371 | LogInit: Launcher ID: 372 | LogInit: Launcher Artifact: 373 | LogInit: Presizing for max 2097152 objects, including 1 objects not considered by GC, pre-allocating 0 bytes for permanent pool. 374 | LogStreaming: Display: AsyncLoading2 - Created: Event Driven Loader: false, Async Loading Thread: true, Async Post Load: true 375 | LogStreaming: Display: AsyncLoading2 - Initialized 376 | LogInit: Object subsystem initialized 377 | [2024.09.12-11.27.37:619][ 0]LogLinux: Selected Device Profile: [LinuxServer] 378 | [2024.09.12-11.27.37:619][ 0]LogInit: Selected Device Profile: [LinuxServer] 379 | [2024.09.12-11.27.37:619][ 0]LogHAL: Display: Platform has ~ 63 GB [67312029696 / 68719476736 / 63], which maps to Largest [LargestMinGB=32, LargerMinGB=12, DefaultMinGB=8, SmallerMinGB=6, SmallestMinGB=0) 380 | [2024.09.12-11.27.37:619][ 0]LogDeviceProfileManager: Going up to parent DeviceProfile [Linux] 381 | [2024.09.12-11.27.37:619][ 0]LogDeviceProfileManager: Going up to parent DeviceProfile [] 382 | [2024.09.12-11.27.37:619][ 0]LogInit: Unix hardware info: 383 | [2024.09.12-11.27.37:619][ 0]LogInit: - we are the first instance of this executable 384 | [2024.09.12-11.27.37:619][ 0]LogInit: - this process' id (pid) is 175, parent process' id (ppid) is 1 385 | [2024.09.12-11.27.37:619][ 0]LogInit: - we are not running under debugger 386 | [2024.09.12-11.27.37:619][ 0]LogInit: - machine network name is 'satisfactory-server' 387 | [2024.09.12-11.27.37:619][ 0]LogInit: - user name is 'steam' (steam) 388 | [2024.09.12-11.27.37:619][ 0]LogInit: - we're logged in locally 389 | [2024.09.12-11.27.37:619][ 0]LogInit: - we're running without rendering 390 | [2024.09.12-11.27.37:619][ 0]LogInit: - CPU: AuthenticAMD 'AMD Ryzen 9 5900X 12-Core Processor' (signature: 0xA20F10) 391 | [2024.09.12-11.27.37:619][ 0]LogInit: - Number of physical cores available for the process: 12 392 | [2024.09.12-11.27.37:619][ 0]LogInit: - Number of logical cores available for the process: 24 393 | [2024.09.12-11.27.37:619][ 0]LogInit: - GPU Brand Info: NVIDIA PCI-id: 10de-1401 (1462-3201) 394 | [2024.09.12-11.27.37:619][ 0]LogInit: - Memory allocator used: binned2 395 | [2024.09.12-11.27.37:619][ 0]LogInit: - This is a licensee build. 396 | [2024.09.12-11.27.37:619][ 0]LogCore: Benchmarking clocks: 397 | [2024.09.12-11.27.37:619][ 0]LogCore: - CLOCK_MONOTONIC (id=1) can sustain 44104295 (44104K, 44M) calls per second without zero deltas. 398 | [2024.09.12-11.27.37:619][ 0]LogCore: - CLOCK_MONOTONIC_RAW (id=4) can sustain 44120514 (44121K, 44M) calls per second without zero deltas. 399 | [2024.09.12-11.27.37:619][ 0]LogCore: - CLOCK_MONOTONIC_COARSE (id=6) can sustain 212015625 (212016K, 212M) calls per second with 99.999877% zero deltas. 400 | [2024.09.12-11.27.37:619][ 0]LogCore: Selected clock_id 4 (CLOCK_MONOTONIC_RAW) since it is the fastest support clock without zero deltas. 401 | [2024.09.12-11.27.37:619][ 0]LogInit: Unix-specific commandline switches: 402 | [2024.09.12-11.27.37:619][ 0]LogInit: -ansimalloc - use malloc()/free() from libc (useful for tools like valgrind and electric fence) 403 | [2024.09.12-11.27.37:619][ 0]LogInit: -jemalloc - use jemalloc for all memory allocation 404 | [2024.09.12-11.27.37:619][ 0]LogInit: -binnedmalloc - use binned malloc for all memory allocation 405 | [2024.09.12-11.27.37:619][ 0]LogInit: -filemapcachesize=NUMBER - set the size for case-sensitive file mapping cache 406 | [2024.09.12-11.27.37:619][ 0]LogInit: -useksm - uses kernel same-page mapping (KSM) for mapped memory (OFF) 407 | [2024.09.12-11.27.37:619][ 0]LogInit: -ksmmergeall - marks all mmap'd memory pages suitable for KSM (OFF) 408 | [2024.09.12-11.27.37:619][ 0]LogInit: -preloadmodulesymbols - Loads the main module symbols file into memory (OFF) 409 | [2024.09.12-11.27.37:619][ 0]LogInit: -sigdfl=SIGNAL - Allows a specific signal to be set to its default handler rather then ignoring the signal 410 | [2024.09.12-11.27.37:619][ 0]LogInit: -crashhandlerstacksize - Allows setting crash handler stack sizes (204800) 411 | [2024.09.12-11.27.37:619][ 0]LogInit: -noexclusivelockonwrite - disables marking files created by the engine as exclusive locked while the engine has them opened 412 | [2024.09.12-11.27.37:619][ 0]LogInit: -httpproxy=ADDRESS:PORT - redirects HTTP requests to a proxy (only supported if compiled with libcurl) 413 | [2024.09.12-11.27.37:619][ 0]LogInit: -reuseconn - allow libcurl to reuse HTTP connections (only matters if compiled with libcurl) 414 | [2024.09.12-11.27.37:619][ 0]LogInit: -virtmemkb=NUMBER - sets process virtual memory (address space) limit (overrides VirtualMemoryLimitInKB value from .ini) 415 | [2024.09.12-11.27.37:619][ 0]LogInit: -allowsyscallfilterfile=PATH_TO_FILE - sets up a system call filter allow list. any invalid syscall in this list *will* cause a crash 416 | [2024.09.12-11.27.37:619][ 0]LogInit: - Physical RAM available (not considering process quota): 63 GB (64193 MB, 65734404 KB, 67312029696 bytes) 417 | [2024.09.12-11.27.37:619][ 0]LogInit: - VirtualMemoryAllocator pools will grow at scale 1.4 418 | [2024.09.12-11.27.37:619][ 0]LogInit: - MemoryRangeDecommit() will will evict the memory from RAM (re-run with -novmapoolevict to change) 419 | [2024.09.12-11.27.37:619][ 0]LogInit: - PageSize 4096 420 | [2024.09.12-11.27.37:619][ 0]LogInit: - BinnedPageSize 65536 421 | [2024.09.12-11.27.37:621][ 0]LogInit: Physics initialised using underlying interface: Chaos 422 | [2024.09.12-11.27.37:621][ 0]LogInit: Overriding language with engine language configuration option (en-US-Posix). 423 | [2024.09.12-11.27.37:621][ 0]LogInit: Overriding language with engine locale configuration option (en-US-Posix). 424 | [2024.09.12-11.27.37:621][ 0]LogTextLocalizationManager: No specific localization for 'en-US-Posix' exists, so 'en' will be used for the language. 425 | [2024.09.12-11.27.37:649][ 0]LogConfig: Warning: Found a deprecated ini section name in ../../../FactoryGame/Config/DefaultEngine.ini. Search for [/Script/Engine.TextureLODSettings] and replace with [GlobalDefaults DeviceProfile] 426 | [2024.09.12-11.27.37:651][ 0]LogRendererCore: Ray tracing is disabled. Reason: disabled through project setting (r.RayTracing=0). 427 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/2D/Paper2D/Content/' mounted to '/Paper2D/' 428 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Animation/ACLPlugin/Content/' mounted to '/ACLPlugin/' 429 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Animation/ControlRigSpline/Content/' mounted to '/ControlRigSpline/' 430 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Animation/ControlRig/Content/' mounted to '/ControlRig/' 431 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Animation/DeformerGraph/Content/' mounted to '/DeformerGraph/' 432 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Animation/IKRig/Content/' mounted to '/IKRig/' 433 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Bridge/Content/' mounted to '/Bridge/' 434 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Compositing/LensDistortion/Content/' mounted to '/LensDistortion/' 435 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Developer/AnimationSharing/Content/' mounted to '/AnimationSharing/' 436 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/BlueprintHeaderView/Content/' mounted to '/BlueprintHeaderView/' 437 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/GeometryMode/Content/' mounted to '/GeometryMode/' 438 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/ObjectMixer/LightMixer/Content/' mounted to '/LightMixer/' 439 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/ObjectMixer/ObjectMixer/Content/' mounted to '/ObjectMixer/' 440 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/SpeedTreeImporter/Content/' mounted to '/SpeedTreeImporter/' 441 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Editor/UVEditor/Content/' mounted to '/UVEditor/' 442 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Enterprise/DatasmithContent/Content/' mounted to '/DatasmithContent/' 443 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Enterprise/GLTFExporter/Content/' mounted to '/GLTFExporter/' 444 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ChaosCaching/Content/' mounted to '/ChaosCaching/' 445 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ChaosClothEditor/Content/' mounted to '/ChaosClothEditor/' 446 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ChaosNiagara/Content/' mounted to '/ChaosNiagara/' 447 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ChaosSolverPlugin/Content/' mounted to '/ChaosSolverPlugin/' 448 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ChaosVehiclesPlugin/Content/' mounted to '/ChaosVehiclesPlugin/' 449 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ControlFlows/Content/' mounted to '/ControlFlows/' 450 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/Dataflow/Content/' mounted to '/Dataflow/' 451 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/FullBodyIK/Content/' mounted to '/FullBodyIK/' 452 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/GeometryCollectionPlugin/Content/' mounted to '/GeometryCollectionPlugin/' 453 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/GeometryScripting/Content/' mounted to '/GeometryScripting/' 454 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/Landmass/Content/' mounted to '/Landmass/' 455 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/PythonScriptPlugin/Content/' mounted to '/PythonScriptPlugin/' 456 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Experimental/ToolPresets/Content/' mounted to '/ToolPresets/' 457 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/FX/Niagara/Content/' mounted to '/Niagara/' 458 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Fab/Content/' mounted to '/Fab/' 459 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Interchange/Runtime/Content/' mounted to '/Interchange/' 460 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Media/MediaCompositing/Content/' mounted to '/MediaCompositing/' 461 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Media/MediaPlate/Content/' mounted to '/MediaPlate/' 462 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/MovieScene/SequencerScripting/Content/' mounted to '/SequencerScripting/' 463 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/AudioSynesthesia/Content/' mounted to '/AudioSynesthesia/' 464 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/AudioWidgets/Content/' mounted to '/AudioWidgets/' 465 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/GeometryProcessing/Content/' mounted to '/GeometryProcessing/' 466 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/HairStrands/Content/' mounted to '/HairStrands/' 467 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/MeshModelingToolset/Content/' mounted to '/MeshModelingToolset/' 468 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/Metasound/Content/' mounted to '/Metasound/' 469 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/ResonanceAudio/Content/' mounted to '/ResonanceAudio/' 470 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/RigVM/Content/' mounted to '/RigVM/' 471 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/Synthesis/Content/' mounted to '/Synthesis/' 472 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Runtime/WaveTable/Content/' mounted to '/WaveTable/' 473 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Tests/EditorTests/Content/' mounted to '/EditorTests/' 474 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Tests/RuntimeTests/Content/' mounted to '/RuntimeTests/' 475 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/TraceUtilities/Content/' mounted to '/TraceUtilities/' 476 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/VirtualProduction/Takes/Content/' mounted to '/Takes/' 477 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/WwiseNiagara/Content/' mounted to '/WwiseNiagara/' 478 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../Plugins/Wwise/Content/' mounted to '/Wwise/' 479 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/AbstractInstance/Content/' mounted to '/AbstractInstance/' 480 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/EditorTools/Content/' mounted to '/EditorTools/' 481 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/GameplayEvents/Content/' mounted to '/GameplayEvents/' 482 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/InstancedSplines/Content/' mounted to '/InstancedSplines/' 483 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/Online/OnlineIntegration/Content/' mounted to '/OnlineIntegration/' 484 | [2024.09.12-11.27.37:656][ 0]LogPackageName: Display: FPackageName: Mount point added: '../../../FactoryGame/Plugins/SignificanceISPC/Content/' mounted to '/SignificanceISPC/' 485 | [2024.09.12-11.27.37:656][ 0]LogInit: Overriding language with engine language configuration option (en-US-Posix). 486 | [2024.09.12-11.27.37:656][ 0]LogInit: Overriding language with engine locale configuration option (en-US-Posix). 487 | [2024.09.12-11.27.37:656][ 0]LogTextLocalizationManager: No localization for 'en-US-Posix' exists, so 'en-US' will be used for the language. 488 | [2024.09.12-11.27.37:656][ 0]LogTextLocalizationManager: No localization for 'en-US-Posix' exists, so 'en-US' will be used for the locale. 489 | [2024.09.12-11.27.37:656][ 0]LogTextLocalizationResource: LocRes '../../../FactoryGame/Content/Localization/Game/en-US/Game.locres' could not be opened for reading! 490 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../FactoryGame/Content/Localization/InputKeys/en-US/InputKeys.locres' could not be opened for reading! 491 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../FactoryGame/Content/Localization/Narrative/en-US/Narrative.locres' could not be opened for reading! 492 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../FactoryGame/Content/Localization/AllStringTables/en-US/AllStringTables.locres' could not be opened for reading! 493 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../Engine/Content/Localization/Engine/en-US/Engine.locres' could not be opened for reading! 494 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../Engine/Plugins/Online/OnlineFramework/Content/Localization/OnlineFramework/en-US/OnlineFramework.locres' could not be opened for reading! 495 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../Engine/Plugins/Online/OnlineSubsystemUtils/Content/Localization/OnlineSubsystemUtils/en-US/OnlineSubsystemUtils.locres' could not be opened for reading! 496 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../Engine/Plugins/Online/OnlineSubsystem/Content/Localization/OnlineSubsystem/en-US/OnlineSubsystem.locres' could not be opened for reading! 497 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../FactoryGame/Content/Localization/Game/en/Game.locres' could not be opened for reading! 498 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../FactoryGame/Content/Localization/InputKeys/en/InputKeys.locres' could not be opened for reading! 499 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../FactoryGame/Content/Localization/Narrative/en/Narrative.locres' could not be opened for reading! 500 | [2024.09.12-11.27.37:657][ 0]LogTextLocalizationResource: LocRes '../../../FactoryGame/Content/Localization/AllStringTables/en/AllStringTables.locres' could not be opened for reading! 501 | [2024.09.12-11.27.37:658][ 0]LogAssetRegistry: FAssetRegistry took 0.0001 seconds to start up 502 | [2024.09.12-11.27.37:698][ 0]LogStreaming: Display: FlushAsyncLoading(1): 1 QueuedPackages, 0 AsyncPackages 503 | [2024.09.12-11.27.37:699][ 0]LogLinux: Selected Device Profile: [LinuxServer] 504 | [2024.09.12-11.27.37:699][ 0]LogDeviceProfileManager: Available device profiles: 505 | [2024.09.12-11.27.37:699][ 0]LogDeviceProfileManager: [0x7f60a1eaff20][0x7f6131dc9250 66] Linux, 506 | [2024.09.12-11.27.37:699][ 0]LogDeviceProfileManager: [0x7f60a1eafd60][0x7f6131dc6dc0 66] LinuxServer, 507 | [2024.09.12-11.27.37:699][ 0]LogDeviceProfileManager: Active device profile: [0x7f60a1eafd60][0x7f6131dc6dc0 66] LinuxServer 508 | [2024.09.12-11.27.37:703][ 0]LogTextureEncodingSettings: Display: Texture Encode Speed: Final (cook). 509 | [2024.09.12-11.27.37:703][ 0]LogTextureEncodingSettings: Display: Oodle Texture Encode Speed settings: Fast: RDO Off Lambda=0, Effort=Normal Final: RDO Off Lambda=0, Effort=Normal 510 | [2024.09.12-11.27.37:703][ 0]LogTextureEncodingSettings: Display: Shared linear texture encoding: Disabled 511 | [2024.09.12-11.27.37:729][ 0]LogPackageLocalizationCache: Processed 60 localized package path(s) for 2 prioritized culture(s) in 0.000109 seconds 512 | [2024.09.12-11.27.37:834][ 0]LogAudio: Display: Registering Engine Module Parameter Interfaces... 513 | [2024.09.12-11.27.37:836][ 0]LogMetasoundEngine: MetaSound Engine Initialized 514 | [2024.09.12-11.27.37:842][ 0]LogWwiseNiagara: Display: Initializing default Niagara. 515 | [2024.09.12-11.27.37:865][ 0]LogStringTable: Warning: Failed to find string table entry for 'Menus_UI' 'Players/Messages/YouGotKicked'. Did you forget to add a string table redirector? 516 | [2024.09.12-11.27.37:865][ 0]LogConsoleManager: Warning: Console object named 'SigMan.server.tickrate' already exists but is being registered again, but we weren't expected it to be! (FConsoleManager::AddConsoleObject) 517 | [2024.09.12-11.27.37:865][ 0]LogConsoleManager: Warning: Console object named 'SigMan.server.tickrate' already exists but is being registered again, but we weren't expected it to be! (FConsoleManager::AddConsoleObject) 518 | [2024.09.12-11.27.37:865][ 0]LogStringTable: Warning: Failed to find string table entry for 'Menus_UI' 'Sessions/JoinSession/Header'. Did you forget to add a string table redirector? 519 | [2024.09.12-11.27.37:865][ 0]LogStringTable: Warning: Failed to find string table entry for 'Menus_UI' 'Sessions/JoinSession/Body'. Did you forget to add a string table redirector? 520 | [2024.09.12-11.27.37:865][ 0]LogStringTable: Warning: Failed to find string table entry for 'Messages_UI' 'Warnings/JoinSession/FailedToDestroyOldSession'. Did you forget to add a string table redirector? 521 | [2024.09.12-11.27.37:865][ 0]LogStringTable: Warning: Failed to find string table entry for 'Messages_UI' 'Warnings/JoinSession/FailedToLookupFriendsId'. Did you forget to add a string table redirector? 522 | [2024.09.12-11.27.37:865][ 0]LogStringTable: Warning: Failed to find string table entry for 'Messages_UI' 'Warnings/JoinSession/FailedToLookupHostId'. Did you forget to add a string table redirector? 523 | [2024.09.12-11.27.37:865][ 0]LogStringTable: Warning: Failed to find string table entry for 'Messages_UI' 'Warnings/JoinSession/FailedToJoinSession'. Did you forget to add a string table redirector? 524 | [2024.09.12-11.27.37:865][ 0]LogConsoleManager: Warning: Console object named 'SigMan.server.tickrate' already exists but is being registered again, but we weren't expected it to be! (FConsoleManager::AddConsoleObject) 525 | [2024.09.12-11.27.37:865][ 0]LogStringTable: Warning: Failed to find string table entry for 'Menus_UI' 'Sessions/Errors/OfflineFallback/aTitle'. Did you forget to add a string table redirector? 526 | [2024.09.12-11.27.37:865][ 0]LogStringTable: Warning: Failed to find string table entry for 'Menus_UI' 'Sessions/Errors/OfflineFallback/Body'. Did you forget to add a string table redirector? 527 | [2024.09.12-11.27.37:865][ 0]LogConsoleManager: Warning: Console object named 'SigMan.server.tickrate' already exists but is being registered again, but we weren't expected it to be! (FConsoleManager::AddConsoleObject) 528 | [2024.09.12-11.27.37:898][ 0]LogStats: UGameplayTagsManager::InitializeManager - 0.001 s 529 | [2024.09.12-11.27.37:926][ 0]LogLocaTools: Loading up all available csv stringtables at: Localization/StringTables 530 | [2024.09.12-11.27.37:926][ 0]LogLocaTools: Loading up stringtable as Architecture_Data at: ../../../FactoryGame/Content/Localization/StringTables/Architecture_Data.csv 531 | [2024.09.12-11.27.37:926][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Architecture_Data.csv 532 | [2024.09.12-11.27.37:926][ 0]LogLocaTools: Loading up stringtable as AWESOME_Data at: ../../../FactoryGame/Content/Localization/StringTables/AWESOME_Data.csv 533 | [2024.09.12-11.27.37:926][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/AWESOME_Data.csv 534 | [2024.09.12-11.27.37:926][ 0]LogLocaTools: Loading up stringtable as AWESOME_UI at: ../../../FactoryGame/Content/Localization/StringTables/AWESOME_UI.csv 535 | [2024.09.12-11.27.37:926][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/AWESOME_UI.csv 536 | [2024.09.12-11.27.37:926][ 0]LogLocaTools: Loading up stringtable as Buildables_Data at: ../../../FactoryGame/Content/Localization/StringTables/Buildables_Data.csv 537 | [2024.09.12-11.27.37:926][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Buildables_Data.csv 538 | [2024.09.12-11.27.37:927][ 0]LogLocaTools: Loading up stringtable as Buildables_UI at: ../../../FactoryGame/Content/Localization/StringTables/Buildables_UI.csv 539 | [2024.09.12-11.27.37:927][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Buildables_UI.csv 540 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable as Codex_UI at: ../../../FactoryGame/Content/Localization/StringTables/Codex_UI.csv 541 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Codex_UI.csv 542 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable as Credits_UI at: ../../../FactoryGame/Content/Localization/StringTables/Credits_UI.csv 543 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Credits_UI.csv 544 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable as Customization_Data at: ../../../FactoryGame/Content/Localization/StringTables/Customization_Data.csv 545 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Customization_Data.csv 546 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable as Customization_UI at: ../../../FactoryGame/Content/Localization/StringTables/Customization_UI.csv 547 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Customization_UI.csv 548 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable as Emotes_Data at: ../../../FactoryGame/Content/Localization/StringTables/Emotes_Data.csv 549 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Emotes_Data.csv 550 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable as Equipment_Data at: ../../../FactoryGame/Content/Localization/StringTables/Equipment_Data.csv 551 | [2024.09.12-11.27.37:928][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Equipment_Data.csv 552 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable as FICSMAS_Data at: ../../../FactoryGame/Content/Localization/StringTables/FICSMAS_Data.csv 553 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/FICSMAS_Data.csv 554 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable as FICSMAS_UI at: ../../../FactoryGame/Content/Localization/StringTables/FICSMAS_UI.csv 555 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/FICSMAS_UI.csv 556 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable as Gamepads_UI at: ../../../FactoryGame/Content/Localization/StringTables/Gamepads_UI.csv 557 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Gamepads_UI.csv 558 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable as GamepadsMappings_UI at: ../../../FactoryGame/Content/Localization/StringTables/GamepadsMappings_UI.csv 559 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/GamepadsMappings_UI.csv 560 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable as General_UI at: ../../../FactoryGame/Content/Localization/StringTables/General_UI.csv 561 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/General_UI.csv 562 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable as HUB_UI at: ../../../FactoryGame/Content/Localization/StringTables/HUB_UI.csv 563 | [2024.09.12-11.27.37:929][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/HUB_UI.csv 564 | [2024.09.12-11.27.37:930][ 0]LogLocaTools: Loading up stringtable as IconLibrary_Data at: ../../../FactoryGame/Content/Localization/StringTables/IconLibrary_Data.csv 565 | [2024.09.12-11.27.37:930][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/IconLibrary_Data.csv 566 | [2024.09.12-11.27.37:930][ 0]LogLocaTools: Loading up stringtable as Items_Data at: ../../../FactoryGame/Content/Localization/StringTables/Items_Data.csv 567 | [2024.09.12-11.27.37:930][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Items_Data.csv 568 | [2024.09.12-11.27.37:931][ 0]LogLocaTools: Loading up stringtable as MAM_Data at: ../../../FactoryGame/Content/Localization/StringTables/MAM_Data.csv 569 | [2024.09.12-11.27.37:931][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/MAM_Data.csv 570 | [2024.09.12-11.27.37:931][ 0]LogLocaTools: Loading up stringtable as MAM_UI at: ../../../FactoryGame/Content/Localization/StringTables/MAM_UI.csv 571 | [2024.09.12-11.27.37:931][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/MAM_UI.csv 572 | [2024.09.12-11.27.37:931][ 0]LogLocaTools: Loading up stringtable as Map_UI at: ../../../FactoryGame/Content/Localization/StringTables/Map_UI.csv 573 | [2024.09.12-11.27.37:931][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Map_UI.csv 574 | [2024.09.12-11.27.37:931][ 0]LogLocaTools: Loading up stringtable as Menus_UI at: ../../../FactoryGame/Content/Localization/StringTables/Menus_UI.csv 575 | [2024.09.12-11.27.37:931][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Menus_UI.csv 576 | [2024.09.12-11.27.37:933][ 0]LogLocaTools: Loading up stringtable as Messages_UI at: ../../../FactoryGame/Content/Localization/StringTables/Messages_UI.csv 577 | [2024.09.12-11.27.37:933][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Messages_UI.csv 578 | [2024.09.12-11.27.37:933][ 0]LogLocaTools: Loading up stringtable as Minigame_UI at: ../../../FactoryGame/Content/Localization/StringTables/Minigame_UI.csv 579 | [2024.09.12-11.27.37:933][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Minigame_UI.csv 580 | [2024.09.12-11.27.37:933][ 0]LogLocaTools: Loading up stringtable as Onboarding at: ../../../FactoryGame/Content/Localization/StringTables/Onboarding.csv 581 | [2024.09.12-11.27.37:933][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Onboarding.csv 582 | [2024.09.12-11.27.37:933][ 0]LogLocaTools: Loading up stringtable as Options_Data at: ../../../FactoryGame/Content/Localization/StringTables/Options_Data.csv 583 | [2024.09.12-11.27.37:933][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Options_Data.csv 584 | [2024.09.12-11.27.37:934][ 0]LogLocaTools: Loading up stringtable as OptionsInputs_Data at: ../../../FactoryGame/Content/Localization/StringTables/OptionsInputs_Data.csv 585 | [2024.09.12-11.27.37:934][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/OptionsInputs_Data.csv 586 | [2024.09.12-11.27.37:934][ 0]LogLocaTools: Loading up stringtable as Player_UI at: ../../../FactoryGame/Content/Localization/StringTables/Player_UI.csv 587 | [2024.09.12-11.27.37:934][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Player_UI.csv 588 | [2024.09.12-11.27.37:935][ 0]LogLocaTools: Loading up stringtable as Schematics_Data at: ../../../FactoryGame/Content/Localization/StringTables/Schematics_Data.csv 589 | [2024.09.12-11.27.37:935][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Schematics_Data.csv 590 | [2024.09.12-11.27.37:935][ 0]LogLocaTools: Loading up stringtable as Transportation_UI at: ../../../FactoryGame/Content/Localization/StringTables/Transportation_UI.csv 591 | [2024.09.12-11.27.37:935][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Transportation_UI.csv 592 | [2024.09.12-11.27.37:936][ 0]LogLocaTools: Loading up stringtable as Units_UI at: ../../../FactoryGame/Content/Localization/StringTables/Units_UI.csv 593 | [2024.09.12-11.27.37:936][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/Units_UI.csv 594 | [2024.09.12-11.27.37:936][ 0]LogLocaTools: Loading up stringtable as World_Data at: ../../../FactoryGame/Content/Localization/StringTables/World_Data.csv 595 | [2024.09.12-11.27.37:936][ 0]LogLocaTools: Loading up stringtable: Content/Localization/StringTables/World_Data.csv 596 | [2024.09.12-11.27.37:960][ 0]LogAudioCaptureCore: Display: No Audio Capture implementations found. Audio input will be silent. 597 | [2024.09.12-11.27.37:960][ 0]LogAudioCaptureCore: Display: No Audio Capture implementations found. Audio input will be silent. 598 | [2024.09.12-11.27.37:962][ 0]LogSlateStyle: Warning: Missing Resource from 'CoreStyle' Style: 'Unable to find Brush 'Sequencer.Timeline.VanillaScrubHandleDown'.' 599 | [2024.09.12-11.27.37:975][ 0]LogUObjectArray: 36776 objects as part of root set at end of initial load. 600 | [2024.09.12-11.27.37:975][ 0]LogUObjectArray: 1271 objects are not in the root set, but can never be destroyed because they are in the DisregardForGC set. 601 | [2024.09.12-11.27.37:975][ 0]LogUObjectAllocator: 11346544 out of 0 bytes used by permanent object pool. 602 | [2024.09.12-11.27.37:975][ 0]LogUObjectArray: CloseDisregardForGC: 36776/36776 objects in disregard for GC pool 603 | [2024.09.12-11.27.37:981][ 0]LogStreaming: Display: AsyncLoading2 - NotifyRegistrationComplete: Registered 33257 public script object entries (913.81 KB) 604 | [2024.09.12-11.27.37:981][ 0]LogStreaming: Display: AsyncLoading2 - Thread Started: true, IsInitialLoad: false 605 | [2024.09.12-11.27.37:982][ 0]LogEngine: Initializing Engine... 606 | [2024.09.12-11.27.37:982][ 0]LogStreaming: Warning: Failed to read file '../../../FactoryGame/Saved/DebugUI/Settings.data' error. 607 | [2024.09.12-11.27.37:990][ 0]LogAssetManager: Display: Ignoring PrimaryAssetType Profiling - Conflicts with Map - Asset: Map_UI-Profile 608 | [2024.09.12-11.27.37:992][ 0]LogInit: Initializing FReadOnlyCVARCache 609 | [2024.09.12-11.27.37:992][ 0]LogNetVersion: Set ProjectVersion to 0.0.1.0. Version Checksum will be recalculated on next use. 610 | [2024.09.12-11.27.37:992][ 0]LogInit: Texture streaming: Disabled 611 | [2024.09.12-11.27.37:994][ 0]LogUserSetting: Initializing user settings 612 | [2024.09.12-11.27.38:027][ 0]LogLoad: Loading WwiseSoundEngine module: WwiseSoundEngine 613 | [2024.09.12-11.27.38:027][ 0]LogWwiseSoundEngine: Display: Loading Wwise Sound Engine version 2022.1 614 | [2024.09.12-11.27.38:027][ 0]LogAkAudio: FAkAudioModule::StartupModule: Couldn't initialize FAkAudioDevice. AkAudioModule will not be fully initialized. 615 | [2024.09.12-11.27.38:032][ 0]LogChaos: FPhysicsSolverBase::AsyncDt:-1.000000 616 | [2024.09.12-11.27.38:033][ 0]LogOnlineSchema: Error: Invalid schema category lobby: Service descriptor id lobby not found. 617 | [2024.09.12-11.27.38:033][ 0]LogOnlineSchema: Error: Invalid schema category lobbymember: Service descriptor id lobbymember not found. 618 | [2024.09.12-11.27.38:033][ 0]LogOnlineServices: Error: [FLobbiesCommon::Initialize] Failed to initialize schema registry 619 | [2024.09.12-11.27.38:033][ 0]LogOnlineIntegration: Initialized 'NULL' online backend 620 | [2024.09.12-11.27.38:034][ 0]LogCommonUser: HandleNetworkConnectionStatusChanged(Context:Offline, ServiceName:, OldStatus:NotConnected, NewStatus:Connected) 621 | [2024.09.12-11.27.38:034][ 0]LogStreaming: Warning: Failed to read file '/home/steam/.config/Epic/FactoryGame/Saved/SaveGames/ServerSettings.7777.sav' error. 622 | [2024.09.12-11.27.38:034][ 0]LogHttpServerModule: Starting all listeners... 623 | [2024.09.12-11.27.38:034][ 0]LogHttpServerModule: All listeners started 624 | [2024.09.12-11.27.38:063][ 0]LogServer: Display: Generated a new Self-Signed certificate for the Server API (old certificate invalid or expired) 625 | [2024.09.12-11.27.38:063][ 0]LogServer: Warning: ============================================================== 626 | [2024.09.12-11.27.38:063][ 0]LogServer: Warning: Server API is running using a Self-Signed Certificate. 627 | [2024.09.12-11.27.38:063][ 0]LogServer: Warning: To verify the certificate integrity on the Client, make sure the following Fingerprint matches with the Client one: 628 | [2024.09.12-11.27.38:063][ 0]LogServer: Warning: SHA256:Re8Y8l7EB+699kPspYduzdfA9PewDxrATl2tZlL2kRQ= 629 | [2024.09.12-11.27.38:063][ 0]LogServer: Warning: ============================================================== 630 | [2024.09.12-11.27.38:063][ 0]LogHttpListener: Warning: HttpListener unable to set desired buffer size (524288): Limited to 425984 631 | [2024.09.12-11.27.38:063][ 0]LogHttpListener: Created new HttpListener on 0.0.0.0:7777 632 | [2024.09.12-11.27.38:063][ 0]LogServer: Display: Server API listening on '0.0.0.0:7777' (Standalone) 633 | [2024.09.12-11.27.38:063][ 0]LogGame: MaxNumBackupsaves: 25 634 | [2024.09.12-11.27.38:064][ 0]LogLoad: Loading WwiseSoundEngine module: WwiseSoundEngine 635 | -------------------------------------------------------------------------------- /ssl/README.md: -------------------------------------------------------------------------------- 1 | # SSL Certificate with Certbot 2 | 3 | The instructions below will help you to deploy a signed SSL certificate for your Satisfactory server. 4 | 5 | ## Docker Compose 6 | 7 | ```yaml 8 | services: 9 | satisfactory-server: 10 | container_name: 'satisfactory-server' 11 | hostname: 'satisfactory-server' 12 | image: 'wolveix/satisfactory-server:latest' 13 | ports: 14 | - '7777:7777/tcp' 15 | - '7777:7777/udp' 16 | - '8888:8888/tcp' 17 | volumes: 18 | - './satisfactory-server:/config' 19 | - './certs/live/${DOMAIN}/fullchain.pem:/config/gamefiles/FactoryGame/Certificates/cert_chain.pem' 20 | - './certs/live/${DOMAIN}/privkey.pem:/config/gamefiles/FactoryGame/Certificates/private_key.pem' 21 | environment: 22 | - MAXPLAYERS=4 23 | - PGID=1000 24 | - PUID=1000 25 | - STEAMBETA=false 26 | restart: unless-stopped 27 | depends_on: 28 | certbot: 29 | condition: service_completed_successfully 30 | deploy: 31 | resources: 32 | limits: 33 | memory: 8G 34 | reservations: 35 | memory: 4G 36 | 37 | certbot: 38 | image: certbot/certbot 39 | command: certonly --standalone --non-interactive --agree-tos -m ${CERTBOT_MAIL} -d ${DOMAIN} 40 | ports: 41 | - '80:80/tcp' 42 | volumes: 43 | - ./certs:/etc/letsencrypt 44 | environment: 45 | - CERTBOT_MAIL=certbot@domain.tld 46 | - DOMAIN=satisfactory.domain.tld 47 | ``` 48 | 49 | The `docker-compose.yml` file above should replace the `docker-compose.yml` file you already have configured. Adjust the 50 | `CERTBOT_MAIL` and `DOMAIN` environment variables under the `certbot` service to be a real email address, and the domain 51 | you'd like to issue the SSL certificate for. Ensure prior to running this that you've already created the necessary DNS 52 | record for your domain. If you don't certbot will fail, and you'll likely hit your rate limit and need to wait a while 53 | to try again (check the `certbot` container's logs for further information). 54 | 55 | **Ensure that you open/port forward for port `80/tcp`.** 56 | 57 | You can now launch the Docker Compose configuration in the same way you normally would. Do note that if Certbot fails, 58 | the game server will not start. 59 | 60 | ## Troubleshooting 61 | 62 | ### I can't reach the server with the new cert! 63 | 64 | If you could reach the server before configuring a signed SSL cert, ensure that you're not doing either of the 65 | following: 66 | - Using a wildcard cert: Satisfactory does not support them ([#354](https://github.com/wolveix/satisfactory-server/issues/354)) 67 | - Connecting to a hostname not specified in your cert: Satisfactory does not support this ([#354](https://github.com/wolveix/satisfactory-server/issues/354)) 68 | - Using your local IP. You cannot use your local IP, as it will not be included in your certificate. 69 | 70 | ### What if port 80 is already in-use with a reverse-proxy? 71 | 72 | Change the port for the certbot service (e.g. `7800:80/tcp`), and forward HTTP traffic from your reverse proxy through 73 | to your `certbot` container. 74 | 75 | Here are examples on how you can do this with Caddy and NGINX 76 | 77 | #### Caddy 78 | 79 | Modify your Caddyfile to include your given domain above. Ensure that you put `http://` **before** the domain, otherwise 80 | Caddy will _also_ request an SSL certificate for it. 81 | 82 | ``` 83 | http://satisfactory.domain.tld { 84 | reverse_proxy :7780 85 | } 86 | ``` 87 | 88 | 89 | #### NGINX 90 | 91 | Modify your NGINX configuration file to include the following virtual host: 92 | 93 | ``` 94 | server { 95 | listen 80; 96 | server_name satisfactory.domain.tld; 97 | 98 | location / { 99 | proxy_pass http://localhost:7780; 100 | } 101 | } 102 | ``` 103 | -------------------------------------------------------------------------------- /ssl/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | satisfactory-server: 3 | container_name: 'satisfactory-server' 4 | hostname: 'satisfactory-server' 5 | image: 'wolveix/satisfactory-server:latest' 6 | ports: 7 | - '7777:7777/tcp' 8 | - '7777:7777/udp' 9 | - '8888:8888/tcp' 10 | volumes: 11 | - './satisfactory-server:/config' 12 | - './certs/live/${DOMAIN}/fullchain.pem:/config/gamefiles/FactoryGame/Certificates/cert_chain.pem' 13 | - './certs/live/${DOMAIN}/privkey.pem:/config/gamefiles/FactoryGame/Certificates/private_key.pem' 14 | environment: 15 | - MAXPLAYERS=4 16 | - PGID=1000 17 | - PUID=1000 18 | - STEAMBETA=false 19 | restart: unless-stopped 20 | depends_on: 21 | certbot: 22 | condition: service_completed_successfully 23 | deploy: 24 | resources: 25 | limits: 26 | memory: 6G 27 | reservations: 28 | memory: 4G 29 | 30 | certbot: 31 | image: certbot/certbot 32 | command: certonly --standalone --non-interactive --agree-tos -m ${CERTBOT_MAIL} -d ${DOMAIN} 33 | ports: 34 | - '80:80/tcp' 35 | volumes: 36 | - ./certs:/etc/letsencrypt 37 | environment: 38 | - CERTBOT_MAIL=certbot@domain.tld 39 | - DOMAIN=satisfactory.domain.tld --------------------------------------------------------------------------------