diff --git a/README.md b/README.md index 44b226e..58d006a 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/package.json b/package.json index 3e3a42b..c3e3e03 100644 --- a/package.json +++ b/package.json @@ -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" } } \ No newline at end of file diff --git a/server-hosting/lambda/index.ts b/server-hosting/lambda/index.ts index d6bdcea..dd96a1e 100644 --- a/server-hosting/lambda/index.ts +++ b/server-hosting/lambda/index.ts @@ -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 }); @@ -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) => { diff --git a/server-hosting/scripts/install.sh b/server-hosting/scripts/install.sh index 33b3718..fa21946 100644 --- a/server-hosting/scripts/install.sh +++ b/server-hosting/scripts/install.sh @@ -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 @@ -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 @@ -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] @@ -37,13 +53,13 @@ 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 @@ -51,6 +67,15 @@ 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 @@ -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 -" diff --git a/server-hosting/server-hosting-stack.ts b/server-hosting/server-hosting-stack.ts index d19355b..16e7a4b 100644 --- a/server-hosting/server-hosting-stack.ts +++ b/server-hosting/server-hosting-stack.ts @@ -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 @@ -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 @@ -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 @@ -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",