├── .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 | 
4 |
5 | 
6 | 
7 | 
8 | 
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 | [](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
--------------------------------------------------------------------------------