Compare commits
5
Commits
1fcf05cc27
...
ac3febf109
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac3febf109 | ||
|
|
1902c62366 | ||
|
|
a918704ec6 | ||
|
|
5c192127f4 | ||
|
|
b9d445c57d |
No files matched your search
@@ -39,10 +39,26 @@ Know this before writing anything:
|
||||
- No real credentials, tokens or IDs anywhere in this repo. A value a server can't start without (a
|
||||
player ID, a license token) goes in `test.yaml` under `secrets`, mapped to a CI secret name.
|
||||
|
||||
### The image: SteamCMD first
|
||||
- If the game's dedicated server is on Steam and ships a Linux build, the template is
|
||||
`defaultDockerImage: cm2network/steamcmd` with `installMethod: STEAMCMD` and the **server's** app id
|
||||
(not the game's). Never a community image of that server (`wolveix/...`, `lloesche/...` and the like):
|
||||
they keep game files and saves in their own folders (`/config`, `/home/<user>`), outside `/server`, so
|
||||
everything is lost on the next start, and no setting can move them.
|
||||
- Community images are only for servers that aren't on Steam for Linux: Windows-only servers that need
|
||||
Proton or Wine, and games not on Steam.
|
||||
- Many Linux servers save under `$HOME` (Unreal Engine games: `~/.config/Epic/<Game>/Saved`). In
|
||||
`cm2network/steamcmd`, `$HOME` is `/home/steam`, outside the volume. Start the command with
|
||||
`export HOME=/server;` so those saves land in the volume, and list them in `backupPaths` relative to
|
||||
`/server` (e.g. `.config/Epic/<Game>/Saved/SaveGames`).
|
||||
|
||||
### Ports
|
||||
- `defaultPorts` lists **only what players connect to** (game and query ports). RCON, REST and admin
|
||||
ports are **never** published.
|
||||
- Use `"7777/udp"` or `"8211:8211/udp"`. Every port needs its protocol.
|
||||
- A port the game uses over both protocols is listed twice: `"7777/udp"` and `"7777/tcp"`.
|
||||
- A game that tells players a second port (a reliable-messaging or query port it announces) needs it as
|
||||
`{PUBLIC_PORT_<port>}` passed to the game's "external port" option, since nodes may publish it elsewhere.
|
||||
- Nodes always publish these container ports (only the host side moves when servers share a node).
|
||||
So the game's own port is **not a setting**: write it into the startup command or config file. A
|
||||
game that must announce the port players use gets `{PUBLIC_PORT_<port>}`.
|
||||
@@ -77,6 +93,20 @@ Know this before writing anything:
|
||||
- Take keys and default values from what the server really writes: run
|
||||
`gbrgames first-boot -game <id>` and read the settings in its report.
|
||||
- Every key in `defaultConfig` has a `configSchema` property, and the other way round.
|
||||
- **Every setting must reach the server.** Settings only arrive as environment variables (and in
|
||||
`configFilePath`, if set). A server that doesn't read environment variables (most official servers,
|
||||
every Unreal Engine server) ignores a setting unless the template delivers it: in `configFilePath`, as a
|
||||
launch option the startup command builds from the variable, or written into the server's own config
|
||||
file by the startup command before it starts. A setting that nothing reads looks editable in the panel
|
||||
and does nothing: leave it out instead.
|
||||
- `configFilePath` writes **every** setting into **one** file. When a server reads its settings from
|
||||
several files (Unreal: `Game.ini`, `Engine.ini`, `GameUserSettings.ini` under
|
||||
`<Game>/Saved/Config/LinuxServer/`), leave `configFilePath` out and write each file from the startup
|
||||
command before the server starts:
|
||||
`printf '[/Script/Engine.GameSession]\nMaxPlayers=%s\n' "${MAX_PLAYERS:-4}" > "$cfg/Game.ini";`.
|
||||
The server may rewrite these files when it stops; writing them on every start keeps the panel in charge.
|
||||
- Never carry over a community image's variable names (`MAXPLAYERS`, `AUTOSAVENUM`, `PUID`, `SKIPUPDATE`...)
|
||||
into a SteamCMD template: only that image's own scripts read them. Use the game's real setting names.
|
||||
- Every property has a `title`, a short `description` for a player, a `type`, a `default` equal to the
|
||||
`defaultConfig` value, and a `category`: one of `Security & Access`, `Gameplay & Rules`,
|
||||
`World & Environment`, `System & General`. Mark the few that matter when deploying with
|
||||
|
||||
@@ -174,9 +174,10 @@ const researchPrompt = "You research how to host a game's dedicated server for G
|
||||
"the ways this game's dedicated server can be run (variants): separate editions of the game with their own server, " +
|
||||
"crossplay builds, or notably different server images. For each variant give:\n" +
|
||||
"- images: how to run this server, best first. When the game has a dedicated server on Steam that runs on Linux, " +
|
||||
"cm2network/steamcmd (the plain SteamCMD route, installing the server into /server) always comes first and that " +
|
||||
"variant is the recommended one. Then Docker Hub images that appear in the pages and run as a non-root user " +
|
||||
"(needed for servers that only exist for Windows).\n" +
|
||||
"cm2network/steamcmd (the plain SteamCMD route, installing the server into /server) is its only image, and " +
|
||||
"there is no separate variant for a community image of the same server: those keep their saves outside /server. " +
|
||||
"Docker Hub images that appear in the pages and run as a non-root user are only for servers that aren't on Steam " +
|
||||
"for Linux (Windows-only servers that need Proton or Wine, games not on Steam).\n" +
|
||||
"- steamAppId: the Steam app id of the DEDICATED SERVER (not the game itself), if installed via SteamCMD; else \"\".\n" +
|
||||
"- startupHint: the command that starts the server from its install folder /server, with the launch options " +
|
||||
"for name, port, world and save location exactly as the pages document them; \"\" if unknown.\n" +
|
||||
@@ -311,7 +312,7 @@ func verifyVariants(res *researchResult) {
|
||||
}
|
||||
}
|
||||
apps := steamApps(appIDs)
|
||||
anyRunnable := false
|
||||
anyRunnable, linuxSteam := false, false
|
||||
for i := range res.Variants {
|
||||
v := &res.Variants[i]
|
||||
var images []string
|
||||
@@ -333,7 +334,8 @@ func verifyVariants(res *researchResult) {
|
||||
v.Verified = append(v.Verified, fmt.Sprintf("Steam app %s is %q", v.SteamAppID, app.name))
|
||||
v.Images = withSteamCMD(v.Images, app.linux)
|
||||
if app.linux {
|
||||
v.Verified = append(v.Verified, "it ships a Linux server, so the SteamCMD option (installing it into /server) is recommended")
|
||||
linuxSteam = true
|
||||
v.Verified = append(v.Verified, "it ships a Linux server, so it is installed with SteamCMD into /server")
|
||||
}
|
||||
} else {
|
||||
v.Problems = append(v.Problems, "Steam app "+v.SteamAppID+" not found for anonymous SteamCMD")
|
||||
@@ -352,10 +354,26 @@ func verifyVariants(res *researchResult) {
|
||||
res.Reason = "unknown"
|
||||
res.Message = "No variant has a Docker image or Steam app that could be confirmed. " + res.Message
|
||||
}
|
||||
if linuxSteam {
|
||||
res.Variants = steamCMDOnly(res.Variants)
|
||||
}
|
||||
res.Variants = oneOptionPerImage(res.Variants)
|
||||
recommend(res.Variants)
|
||||
}
|
||||
|
||||
// steamCMDOnly keeps only the SteamCMD options once the game has a Linux server on Steam. Ready-made images
|
||||
// of such servers keep their files and saves in their own folders (/config, /home/...), outside /server,
|
||||
// and read settings from their own variables, so they can't be made or repaired into a working template.
|
||||
func steamCMDOnly(variants []variant) []variant {
|
||||
var out []variant
|
||||
for _, v := range variants {
|
||||
if len(v.Images) > 0 && isSteamCMDImage(v.Images[0]) && v.SteamAppID != "" {
|
||||
out = append(out, v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// recommend marks exactly one variant recommended, among the runnable ones. A variant that installs a Linux
|
||||
// server with SteamCMD comes before the model's choice: it keeps everything under /server, as nodes need.
|
||||
func recommend(variants []variant) {
|
||||
@@ -422,7 +440,7 @@ func steamApps(apps []string) map[string]steamApp {
|
||||
}
|
||||
|
||||
// withSteamCMD adds the plain SteamCMD route to a variant's images. When the Steam app ships a Linux server
|
||||
// it goes first: it installs into /server, which nodes keep, while ready-made images often keep their
|
||||
// it is the only route: it installs into /server, which nodes keep, while ready-made images keep their
|
||||
// files elsewhere. For Windows-only servers it stays last, behind images that bring Proton or Wine.
|
||||
func withSteamCMD(images []string, linux bool) []string {
|
||||
var others []string
|
||||
@@ -432,7 +450,7 @@ func withSteamCMD(images []string, linux bool) []string {
|
||||
}
|
||||
}
|
||||
if linux {
|
||||
return append([]string{"cm2network/steamcmd"}, others...)
|
||||
return []string{"cm2network/steamcmd"}
|
||||
}
|
||||
return append(others, "cm2network/steamcmd")
|
||||
}
|
||||
|
||||
@@ -59,10 +59,10 @@ func TestParseAppInfoFindsLinuxServers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithSteamCMDGoesFirstOnlyForLinuxServers(t *testing.T) {
|
||||
func TestWithSteamCMDIsTheOnlyRouteForLinuxServers(t *testing.T) {
|
||||
images := []string{"someone/example-server", "cm2network/steamcmd", "other/example"}
|
||||
got := withSteamCMD(images, true)
|
||||
if len(got) != 3 || got[0] != "cm2network/steamcmd" || got[1] != "someone/example-server" {
|
||||
if len(got) != 1 || got[0] != "cm2network/steamcmd" {
|
||||
t.Errorf("linux: %v", got)
|
||||
}
|
||||
got = withSteamCMD([]string{"someone/proton-example"}, false)
|
||||
@@ -89,10 +89,22 @@ func TestRecommendPrefersTheSteamCMDVariant(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSteamCMDOnlyDropsImageVariantsOfLinuxSteamGames(t *testing.T) {
|
||||
// A Linux Steam server researched as two variants: SteamCMD, and a community image under its own title
|
||||
got := steamCMDOnly([]variant{
|
||||
{ID: "dedicated", SteamAppID: "1000000", Images: []string{"cm2network/steamcmd"}},
|
||||
{ID: "vanilla-stable", Images: []string{"someone/example-server"}},
|
||||
})
|
||||
if len(got) != 1 || got[0].ID != "dedicated" {
|
||||
t.Errorf("got %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOneOptionPerImageThenSteamCMDRecommended(t *testing.T) {
|
||||
// A variant runnable two ways becomes one option per way, SteamCMD recommended
|
||||
v := oneOptionPerImage([]variant{{
|
||||
ID: "dedicated", Title: "Example Dedicated Server", Recommended: true, SteamAppID: "1000000",
|
||||
Images: withSteamCMD([]string{"someone/example-server"}, true),
|
||||
Images: []string{"cm2network/steamcmd", "someone/example-server"},
|
||||
Verified: []string{"image someone/example-server exists", "Steam app 1000000 is \"Example Dedicated Server\""},
|
||||
}})
|
||||
recommend(v)
|
||||
|
||||
@@ -1,165 +1,114 @@
|
||||
# yaml-language-server: $schema=../../schema/game.schema.json
|
||||
# The official dedicated server (Steam app 1690800), installed by the node with anonymous SteamCMD.
|
||||
# Satisfactory keeps its saves under $HOME/.config/Epic/FactoryGame, so HOME is pointed at the server
|
||||
# volume; otherwise they would land in /home/steam and be lost on the next start.
|
||||
# The server reads no environment variables, so the startup command writes the settings into its
|
||||
# Game.ini and Engine.ini before every start, and passes the rest as launch options.
|
||||
id: satisfactory-vanilla-stable
|
||||
name: Satisfactory (Vanilla Stable)
|
||||
defaultDockerImage: wolveix/satisfactory-server:latest
|
||||
name: Satisfactory
|
||||
defaultDockerImage: cm2network/steamcmd
|
||||
defaultStartupCommand: >-
|
||||
export HOME=/server;
|
||||
cfg=FactoryGame/Saved/Config/LinuxServer;
|
||||
mkdir -p "$HOME/.steam/sdk64" "$cfg";
|
||||
cp -f /home/steam/steamcmd/linux64/steamclient.so "$HOME/.steam/sdk64/" 2>/dev/null;
|
||||
case "${SERVER_STREAMING:-true}" in true|True|1) ss=1;; *) ss=0;; esac;
|
||||
printf '[/Script/Engine.GameSession]\nMaxPlayers=%s\n' "${MAX_PLAYERS:-4}" > "$cfg/Game.ini";
|
||||
printf '[/Script/FactoryGame.FGSaveSession]\nmNumRotatingAutosaves=%s\n\n[/Script/OnlineSubsystemUtils.IpNetDriver]\nNetServerMaxTickRate=%s\nConnectionTimeout=%s\nInitialConnectTimeout=%s\n\n[/Script/Engine.GarbageCollectionSettings]\ngc.MaxObjectsInEditor=%s\ngc.MaxObjectsInGame=%s\n\n[ConsoleVariables]\nwp.Runtime.EnableServerStreaming=%s\n'
|
||||
"${AUTOSAVE_NUM:-5}" "${MAX_TICK_RATE:-30}" "${CONNECTION_TIMEOUT:-30}" "${CONNECTION_TIMEOUT:-30}"
|
||||
"${MAX_OBJECTS:-2162688}" "${MAX_OBJECTS:-2162688}" "$ss" > "$cfg/Engine.ini";
|
||||
args=(-Port=7777 -ReliablePort=8888 -ExternalReliablePort="${EXTERNAL_RELIABLE_PORT:-8888}" -log -unattended);
|
||||
case "${DISABLE_SEASONAL_EVENTS:-false}" in true|True|1) args+=(-DisableSeasonalEvents);; esac;
|
||||
[ -n "${MULTIHOME:-}" ] && args+=(-multihome="$MULTIHOME");
|
||||
exec ./FactoryServer.sh "${args[@]}"
|
||||
requiredMemoryMb: 8192
|
||||
requiredDiskMb: 16384
|
||||
requiredDiskMb: 20480
|
||||
defaultPorts:
|
||||
- 7777/tcp
|
||||
- 7777/udp
|
||||
- 7777/tcp
|
||||
- 8888/tcp
|
||||
installMethod: NONE
|
||||
installMethod: STEAMCMD
|
||||
installConfig:
|
||||
appId: "1690800"
|
||||
defaultConfig:
|
||||
AUTOSAVENUM: 5
|
||||
DEBUG: false
|
||||
DISABLESEASONALEVENTS: false
|
||||
LOG: false
|
||||
MAXOBJECTS: 2162688
|
||||
MAXPLAYERS: 4
|
||||
MAXTICKRATE: 30
|
||||
MULTIHOME: '::'
|
||||
PGID: 1000
|
||||
PUID: 1000
|
||||
SERVERGAMEPORT: 7777
|
||||
SERVERMESSAGINGPORT: 8888
|
||||
SERVERSTREAMING: true
|
||||
SKIPUPDATE: false
|
||||
STEAMBETA: false
|
||||
STEAMBETAID: ""
|
||||
STEAMBETAKEY: ""
|
||||
TIMEOUT: 30
|
||||
VMOVERRIDE: false
|
||||
MaxPlayers: 4
|
||||
AutosaveNum: 5
|
||||
MaxTickRate: 30
|
||||
ConnectionTimeout: 30
|
||||
MaxObjects: 2162688
|
||||
ServerStreaming: true
|
||||
DisableSeasonalEvents: false
|
||||
Multihome: ""
|
||||
ExternalReliablePort: '{PUBLIC_PORT_8888}'
|
||||
configSchema:
|
||||
$schema: http://json-schema.org/draft-07/schema#
|
||||
type: object
|
||||
properties:
|
||||
AUTOSAVENUM:
|
||||
MaxPlayers:
|
||||
title: Max Players
|
||||
description: How many players can be on the server at once. Set by your plan.
|
||||
type: integer
|
||||
category: Gameplay & Rules
|
||||
default: 4
|
||||
AutosaveNum:
|
||||
title: Autosave Rotation Count
|
||||
description: How many rotating autosave files to keep in the save folder.
|
||||
type: integer
|
||||
minimum: 1
|
||||
category: World & Environment
|
||||
default: 5
|
||||
description: The number of rotating autosave files to keep in the save folder.
|
||||
title: Autosave Rotation Count
|
||||
MaxTickRate:
|
||||
title: Max Tick Rate
|
||||
description: The highest simulation tick rate the server runs at, in Hz.
|
||||
type: integer
|
||||
DEBUG:
|
||||
minimum: 10
|
||||
maximum: 120
|
||||
category: System & General
|
||||
default: false
|
||||
description: Prints diagnostic environment and system details on startup, then stops.
|
||||
title: Debug Mode
|
||||
type: boolean
|
||||
DISABLESEASONALEVENTS:
|
||||
category: Gameplay & Rules
|
||||
default: false
|
||||
description: Disables seasonal in-game events such as FICSMAS holiday events.
|
||||
title: Disable Seasonal Events
|
||||
type: boolean
|
||||
LOG:
|
||||
default: 30
|
||||
ConnectionTimeout:
|
||||
title: Connection Timeout
|
||||
description: Seconds before a player who stops responding is disconnected.
|
||||
type: integer
|
||||
minimum: 5
|
||||
category: System & General
|
||||
default: false
|
||||
description: Disables automatic pruning of previous Satisfactory server log files on container startup.
|
||||
title: Retain Old Logs
|
||||
type: boolean
|
||||
MAXOBJECTS:
|
||||
default: 30
|
||||
MaxObjects:
|
||||
title: Max Objects Limit
|
||||
description: Unreal Engine object limit. Raise it for very large late-game factories.
|
||||
type: integer
|
||||
category: World & Environment
|
||||
default: 2162688
|
||||
description: Unreal Engine garbage collector UObject limit to support large late-game factories.
|
||||
title: Max Objects Limit
|
||||
type: integer
|
||||
MAXPLAYERS:
|
||||
category: System & General
|
||||
default: 4
|
||||
description: Sets the maximum number of concurrent players allowed on the server.
|
||||
title: Max Players
|
||||
type: integer
|
||||
MAXTICKRATE:
|
||||
category: System & General
|
||||
default: 30
|
||||
description: Sets the maximum simulation tick rate for the server in Hz.
|
||||
title: Max Tick Rate
|
||||
type: integer
|
||||
MULTIHOME:
|
||||
category: System & General
|
||||
default: '::'
|
||||
description: 'Network interface binding IP address (defaults to :: for dual-stack IPv4/IPv6).'
|
||||
title: Multihome Address
|
||||
type: string
|
||||
PGID:
|
||||
category: System & General
|
||||
default: 1000
|
||||
description: Sets the Linux group ID under which the dedicated server process runs.
|
||||
title: Process Group ID
|
||||
type: integer
|
||||
PUID:
|
||||
category: System & General
|
||||
default: 1000
|
||||
description: Sets the Linux user ID under which the dedicated server process runs.
|
||||
title: Process User ID
|
||||
type: integer
|
||||
SERVERGAMEPORT:
|
||||
category: System & General
|
||||
default: 7777
|
||||
description: Primary game port for UDP and TCP network communication.
|
||||
title: Server Game Port
|
||||
type: integer
|
||||
SERVERMESSAGINGPORT:
|
||||
category: System & General
|
||||
default: 8888
|
||||
description: Port used for reliable server messaging and beacon network traffic.
|
||||
title: Server Messaging Port
|
||||
type: integer
|
||||
SERVERSTREAMING:
|
||||
ServerStreaming:
|
||||
title: Server Streaming
|
||||
description: Lets the server stream world data in and out instead of keeping it all loaded.
|
||||
type: boolean
|
||||
category: System & General
|
||||
default: true
|
||||
description: Toggles whether the game server utilizes Unreal Engine World Partition asset streaming.
|
||||
title: Server Streaming
|
||||
DisableSeasonalEvents:
|
||||
title: Disable Seasonal Events
|
||||
description: Turns off seasonal events such as FICSMAS.
|
||||
type: boolean
|
||||
SKIPUPDATE:
|
||||
category: System & General
|
||||
category: Gameplay & Rules
|
||||
default: false
|
||||
description: Skips automatic SteamCMD game file update checks on container startup.
|
||||
title: Skip Update
|
||||
type: boolean
|
||||
STEAMBETA:
|
||||
category: System & General
|
||||
default: false
|
||||
description: When enabled, updates and runs the Experimental branch instead of the stable release.
|
||||
title: Steam Beta Branch
|
||||
type: boolean
|
||||
STEAMBETAID:
|
||||
Multihome:
|
||||
title: Multihome Address
|
||||
description: Bind the server to one IP address instead of all of them. Leave empty unless you know you need it.
|
||||
type: string
|
||||
category: System & General
|
||||
default: ""
|
||||
description: Specific beta branch identifier to download via SteamCMD.
|
||||
title: Steam Beta Branch Name
|
||||
ExternalReliablePort:
|
||||
title: Reliable Messaging Port (public)
|
||||
description: The port players' games use for reliable messaging. Filled in for you; only change it if you know you need to.
|
||||
type: string
|
||||
STEAMBETAKEY:
|
||||
category: Security & Access
|
||||
default: ""
|
||||
description: Password required to access a private Steam beta branch.
|
||||
title: Steam Beta Password
|
||||
type: string
|
||||
TIMEOUT:
|
||||
category: System & General
|
||||
default: 30
|
||||
description: Client network connection timeout threshold in seconds.
|
||||
title: Connection Timeout
|
||||
type: integer
|
||||
VMOVERRIDE:
|
||||
category: System & General
|
||||
default: false
|
||||
description: Skips CPU model checking on hypervisors to prevent crash warnings on KVM/QEMU hosts.
|
||||
title: VM CPU Check Override
|
||||
type: boolean
|
||||
default: '{PUBLIC_PORT_8888}'
|
||||
required: []
|
||||
type: object
|
||||
configFileFormat: INI
|
||||
configFormatOptions:
|
||||
assignment: =
|
||||
falseValue: "false"
|
||||
footer: ""
|
||||
header: ""
|
||||
quoteStrings: false
|
||||
separator: "\n"
|
||||
trueValue: "true"
|
||||
unquotedKeys: []
|
||||
uppercaseKeys: false
|
||||
maxPlayersConfigKey: MaxPlayers
|
||||
logFilePath: FactoryGame/Saved/Logs/FactoryGame.log
|
||||
agnosticCommandConfig:
|
||||
queryType: LOG_ONLY
|
||||
playerJoinRegex: 'LogNet: Join succeeded: ([^\r\n]+)'
|
||||
playerLeaveRegex: 'UNetConnection::Close:.*?\[PlayerName:\s*([^\]]+)\]'
|
||||
actions:
|
||||
Ban:
|
||||
type: UNSUPPORTED
|
||||
@@ -169,22 +118,7 @@ agnosticCommandConfig:
|
||||
type: UNSUPPORTED
|
||||
Save:
|
||||
type: UNSUPPORTED
|
||||
pingPort: null
|
||||
pingPortKey: null
|
||||
playerJoinRegex: '.*Join succeeded: (\S+).*'
|
||||
playerLeaveRegex: .*UNetConnection::Close:.*?\[PlayerName:\s*([^\]]+)\].*
|
||||
playersCommand: null
|
||||
playersFormat: null
|
||||
playersRegex: null
|
||||
queryPortKey: null
|
||||
queryType: LOG_ONLY
|
||||
rconPasswordKey: null
|
||||
rconPortKey: null
|
||||
restEndpoint: null
|
||||
restPasswordKey: null
|
||||
restPortKey: null
|
||||
restUser: null
|
||||
backupPaths:
|
||||
- saved
|
||||
maxBackupSizeMb: 1024
|
||||
maxPlayersConfigKey: MAXPLAYERS
|
||||
- .config/Epic/FactoryGame/Saved/SaveGames
|
||||
- FactoryGame/Saved/Config
|
||||
maxBackupSizeMb: 2048
|
||||
@@ -1,3 +1,4 @@
|
||||
# yaml-language-server: $schema=../../schema/test.schema.json
|
||||
# Values CI uses to boot-test this game. Never put real credentials here: map them under 'secrets'.
|
||||
timeoutSec: 900
|
||||
# SteamCMD install of ~8 GB plus first start
|
||||
timeoutSec: 2400
|
||||
Reference in new issue
Block a user