Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Satisfactory Server AWS
Automated Satisfactory Dedicated Server management on AWS

## Extra Stuff
1. If the Satisfactory server doesn't work but the AWS EC2 instance is running, it probably means the donwload for the satisfactory files from the newly created S3 bucket failed. It can be done manually as well.
2. This process does **not** import blueprints. Satisfactoy Blueprints are saved separately from Save files in a neighbouring file.
1. Join the Satisfactory server as a player and create a test blueprint so it creates the directory needed
2. Find you blueprints locally in a file neighbouring your local Save files (look up directories on the Satisfactory wiki)
3. SCP to the appropriate file on the EC2 server. Make sure to restart the Satisfactory server

## Intro
FICSIT Incorporated has provided you with this tool (cost deducted from your existing balance) to assist you with Project Assembly. This tool can help you collaborate with friends on your factory projects.

Expand Down
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@
"cdk": "cdk"
},
"devDependencies": {
"@types/node": "10.17.27",
"aws-cdk": "2.3.0",
"ts-node": "^9.0.0",
"typescript": "~3.9.7",
"@aws-sdk/client-ec2": "3.45.0",
"esbuild": "0.14.10"
"@types/node": "^20.5.0",
"aws-cdk": "^2.154.0",
"ts-node": "^10.9.2",
"typescript": "^5.5.4",
"@aws-sdk/client-ec2": "^3.636.0",
"esbuild": "^0.23.1"
},
"dependencies": {
"aws-cdk-lib": "2.3.0",
"constructs": "^10.0.0",
"source-map-support": "^0.5.16"
"aws-cdk-lib": "^2.154.0",
"constructs": "^10.3.0",
"source-map-support": "^0.5.21"
}
}
12 changes: 9 additions & 3 deletions server-hosting/lambda/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EC2Client, StartInstancesCommand } from "@aws-sdk/client-ec2";
import { EC2Client, StartInstancesCommand, DescribeInstancesCommand } from "@aws-sdk/client-ec2";

const instanceId = process.env.INSTANCE_ID
const client = new EC2Client({ region: process.env.AWS_REGION });
Expand All @@ -9,12 +9,18 @@ exports.handler = async function (event: any) {
console.log("Attempting to start game server", instanceId);

return client.send(command)
.then((res) => {
.then(async (res) => {
console.log(JSON.stringify(res));

// Get public IP
const describeCommand = new DescribeInstancesCommand({ InstanceIds: [instanceId!] });
const describeResult = await client.send(describeCommand);
const publicIp = describeResult.Reservations?.[0]?.Instances?.[0]?.PublicIpAddress;

return {
statusCode: 200,
headers: { "Content-Type": "text/json" },
body: JSON.stringify({ message: "Started satisfactory server", response: JSON.stringify(res) })
body: JSON.stringify({ message: "Started satisfactory server", response: JSON.stringify(res), publicIp: publicIp })
}
})
.catch((err) => {
Expand Down
31 changes: 28 additions & 3 deletions server-hosting/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ USE_EXPERIMENTAL_BUILD=${2-false}


# install steamcmd: https://developer.valvesoftware.com/wiki/SteamCMD?__cf_chl_jschl_tk__=pmd_WNQPOiK18.h0rf16RCYrARI2s8_84hUMwT.7N1xHYcs-1635248050-0-gqNtZGzNAiWjcnBszQiR#Linux.2FmacOS)
echo "Installing SteamCMD..."
add-apt-repository multiverse
dpkg --add-architecture i386
apt update
Expand All @@ -18,7 +19,15 @@ echo steam steam/license note '' | sudo debconf-set-selections

apt install -y unzip lib32gcc1 steamcmd

if [ $? -ne 0 ]; then
echo "Error: Failed to install SteamCMD"
exit 1
fi

echo "SteamCMD installed successfully"

# install satisfactory: https://satisfactory.fandom.com/wiki/Dedicated_servers
echo "Installing Satisfactory..."
if [ $USE_EXPERIMENTAL_BUILD = "true" ]; then
STEAM_INSTALL_SCRIPT="/usr/games/steamcmd +login anonymous +app_update 1690800 -beta experimental validate +quit"
else
Expand All @@ -27,6 +36,13 @@ fi
# note, we are switching users because steam doesn't recommend running steamcmd as root
su - ubuntu -c "$STEAM_INSTALL_SCRIPT"

if [ $? -ne 0 ]; then
echo "Error: Failed to install Satisfactory"
exit 1
fi

echo "Satisfactory installed successfully"

# enable as server so it stays up and start: https://satisfactory.fandom.com/wiki/Dedicated_servers/Running_as_a_Service
cat << EOF > /etc/systemd/system/satisfactory.service
[Unit]
Expand All @@ -37,20 +53,29 @@ After=syslog.target network.target nss-lookup.target network-online.target
[Service]
Environment="LD_LIBRARY_PATH=./linux64"
ExecStartPre=$STEAM_INSTALL_SCRIPT
ExecStart=/home/ubuntu/.steam/steamapps/common/SatisfactoryDedicatedServer/FactoryServer.sh
ExecStart=/home/ubuntu/.steam/SteamApps/common/SatisfactoryDedicatedServer/FactoryServer.sh -ServerQueryPort=15777 -BeaconPort=15000 -Port=7777 -ReliablePort=8888 -log -unattended
User=ubuntu
Group=ubuntu
StandardOutput=journal
Restart=on-failure
KillSignal=SIGINT
WorkingDirectory=/home/ubuntu/.steam/steamapps/common/SatisfactoryDedicatedServer
WorkingDirectory=/home/ubuntu/.steam/SteamApps/common/SatisfactoryDedicatedServer

[Install]
WantedBy=multi-user.target
EOF
systemctl enable satisfactory
systemctl start satisfactory

# Check if service started successfully
if ! systemctl is-active --quiet satisfactory; then
echo "Error: Satisfactory service failed to start"
systemctl status satisfactory
exit 1
fi

echo "Satisfactory service started successfully"

# enable auto shutdown: https://github.com/feydan/satisfactory-tools/tree/main/shutdown
cat << 'EOF' > /home/ubuntu/auto-shutdown.sh
#!/bin/sh
Expand Down Expand Up @@ -104,4 +129,4 @@ systemctl enable auto-shutdown
systemctl start auto-shutdown

# automated backups to s3 every 5 minutes
su - ubuntu -c "crontab -l -e ubuntu | { cat; echo \"*/5 * * * * /usr/local/bin/aws s3 sync /home/ubuntu/.config/Epic/FactoryGame/Saved/SaveGames/server s3://$S3_SAVE_BUCKET\"; } | crontab -"
su - ubuntu -c "crontab -u ubuntu -l | { cat; echo \"*/5 * * * * /usr/local/bin/aws s3 sync /home/ubuntu/.config/Epic/FactoryGame/Saved/SaveGames/server s3://$S3_SAVE_BUCKET\"; } | crontab -"
29 changes: 21 additions & 8 deletions server-hosting/server-hosting-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,13 @@ export class ServerHostingStack extends Stack {
description: "Allow Satisfactory client to connect to server",
})

securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.udp(7777), "Game port")
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.udp(15000), "Beacon port")
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.udp(15777), "Query port")
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.udp(7777), "Game port UDP")
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.udp(15000), "Beacon port UDP")
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.udp(15777), "Query port UDP")
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(7777), "Game port TCP")
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(15000), "Beacon port TCP")
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(15777), "Query port TCP")
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(8888), "Ver 1.1 Game port TCP")

const server = new ec2.Instance(this, `${prefix}Server`, {
// 2 vCPU, 8 GB RAM should be enough for most factories
Expand All @@ -75,7 +79,7 @@ export class ServerHostingStack extends Stack {
blockDevices: [
{
deviceName: "/dev/sda1",
volume: ec2.BlockDeviceVolume.ebs(15),
volume: ec2.BlockDeviceVolume.ebs(30, {volumeType: ec2.EbsDeviceVolumeType.GP3}),
}
],
// server needs a public ip to allow connections
Expand Down Expand Up @@ -129,10 +133,10 @@ export class ServerHostingStack extends Stack {
bucket: startupScript.bucket,
bucketKey: startupScript.s3ObjectKey,
});
server.userData.addExecuteFileCommand({
filePath: localPath,
arguments: `${savesBucket.bucketName} ${Config.useExperimentalBuild}`
});
server.userData.addCommands(`chmod +x ${localPath}`)

// Execute with sudo and proper error handling
server.userData.addCommands(`${localPath} "${savesBucket.bucketName}" "${Config.useExperimentalBuild}" || echo "Install script failed, check logs"`)

//////////////////////////////
// Add api to start server
Expand All @@ -157,6 +161,15 @@ export class ServerHostingStack extends Stack {
]
}))

startServerLambda.addToRolePolicy(new iam.PolicyStatement({
actions: [
'ec2:DescribeInstances',
],
resources: [
'*',
]
}))

new apigw.LambdaRestApi(this, `${Config.prefix}StartServerApi`, {
handler: startServerLambda,
description: "Trigger lambda function to start server",
Expand Down