diff --git a/terrastick/Deployment/scripts/aws.sh b/terrastick/Deployment/scripts/aws.sh index 75816c36..90959f6f 100755 --- a/terrastick/Deployment/scripts/aws.sh +++ b/terrastick/Deployment/scripts/aws.sh @@ -1,5 +1,9 @@ #!/bin/bash +# Define the required variables +EXP_TIME=$(date +%Y-%m-%d-%H-%M-%S) +DIR_NAME="terraria-experiment-$EXP_TIME" + # load the config file . ./configs/aws-config.txt @@ -15,123 +19,261 @@ INSTANCE_TYPE=$INSTANCE_TYPE AMI_ID=$BASE_AMI_ID NUM_BOT_INSTANCES=$NUM_BOT_INSTANCES -# Check if the key pair exists and create it if not -KEY_PAIR_EXISTS=$(aws ec2 describe-key-pairs \ - --region "$AWS_REGION" \ - --filters "Name=key-name,Values=$KEY_PAIR_NAME" \ - --query 'KeyPairs[0].KeyName' \ - --output text) +# Function to create key pair +create_key_pair() { + local key_pair_name=$1 + local region=$2 -if [ "$KEY_PAIR_EXISTS" == "None" ]; then - echo "Creating a temp folder..." - mkdir -p temp - echo "Creating new key pair: $KEY_PAIR_NAME..." - aws ec2 create-key-pair \ - --region "$AWS_REGION" \ - --key-name $KEY_PAIR_NAME \ - --query 'KeyMaterial' \ - --output text > temp/$KEY_PAIR_NAME.pem - - chmod 400 temp/$KEY_PAIR_NAME.pem - ssh-keygen -y -f temp/$KEY_PAIR_NAME.pem > temp/$KEY_PAIR_NAME.pub - - # Set the correct permissions for the key pair - chmod 400 temp/$KEY_PAIR_NAME.pem -else - echo "Key pair $KEY_PAIR_NAME already exists." -fi - -# Check if the security group exists and create it if not -SECURITY_GROUP_ID=$(aws ec2 describe-security-groups \ - --region "$AWS_REGION" \ - --filters "Name=group-name,Values=$SECURITY_GROUP_NAME" \ - --query 'SecurityGroups[0].GroupId' \ - --output text) + local key_pair_exists=$(aws ec2 describe-key-pairs \ + --region "$region" \ + --filters "Name=key-name,Values=$key_pair_name" \ + --query 'KeyPairs[0].KeyName' \ + --output text) -if [ "$SECURITY_GROUP_ID" == "None" ]; then - echo "Creating new security group: $SECURITY_GROUP_NAME..." - SECURITY_GROUP_ID=$(aws ec2 create-security-group \ + if [ "$key_pair_exists" == "None" ]; then + echo "Creating a temp folder..." + mkdir -p temp + echo "Creating new key pair: $key_pair_name..." + aws ec2 create-key-pair \ + --region "$region" \ + --key-name $key_pair_name \ + --query 'KeyMaterial' \ + --output text > temp/$key_pair_name.pem + + chmod 400 temp/$key_pair_name.pem + ssh-keygen -y -f temp/$key_pair_name.pem > temp/$key_pair_name.pub + + # Set the correct permissions for the key pair + chmod 400 temp/$key_pair_name.pem + else + echo "Key pair $key_pair_name already exists." + fi +} + +cleanup_instances() { + instance_type=$1 + echo "Cleaning up ${instance_type} instances..." + instance_ids=$(aws ec2 describe-instances --filters "Name=tag:EXP_TIME,Values=$LAST_EXP_TIME" "Name=tag:TYPE,Values=$instance_type" --query "Reservations[].Instances[].InstanceId" --output text | tr '\t' ' ') + + if [ -n "$instance_ids" ]; then + aws ec2 terminate-instances --instance-ids $instance_ids || { echo "Failed to terminate $instance_type instances."; exit 1; } + echo "$instance_type instances cleaned up successfully." + else + echo "No $instance_type instances found to clean up." + fi +} + + +# Function to clean up previous experiment +cleanup_previous_experiment() { + if [ -f temp/last_exp_time.txt ]; then + . temp/last_exp_time.txt + + if [ -n "$LAST_EXP_TIME" ]; then + echo "Last experiment time: $LAST_EXP_TIME" + echo "The previous experiment will be cleaned up..." + + cleanup_instances "server" + cleanup_instances "bot" + cleanup_instances "prometheus" + + echo "Cleanup completed. LAST_EXP_TIME entry will be removed." + rm temp/last_exp_time.txt # Delete the last experiment time file after cleanup + else + echo "It appears to be the first time running the experiment as LAST_EXP_TIME is not set." + fi + else + echo "First time running the experiment. No cleanup needed." + fi +} + +# Function to ask the user if they want to start a new experiment +start_new_experiment() { + local exp_time=$1 + + while true; do + read -r -p "Do you want to start a new experiment? [y/N] " START_NEW_EXP + case $START_NEW_EXP in + [Yy]* ) + echo "Starting new experiment..." + echo "LAST_EXP_TIME=$exp_time" > temp/last_exp_time.txt + break;; + [Nn]* ) + echo "Not starting new experiment. Exiting script." + exit;; + * ) echo "Please answer yes or no.";; + esac + done +} + +# Function to manage the security group +manage_security_group() { + SECURITY_GROUP_ID=$(aws ec2 describe-security-groups \ --region "$AWS_REGION" \ - --group-name $SECURITY_GROUP_NAME \ - --description "Security group for Terraria server and bot" \ - --query 'GroupId' \ + --filters "Name=group-name,Values=$SECURITY_GROUP_NAME" \ + --query 'SecurityGroups[0].GroupId' \ --output text) - # getting the default vpc cidr block - DEFAULT_VPC_CIDR=$(aws ec2 describe-vpcs \ - --filters Name=isDefault,Values=true \ - --query 'Vpcs[0].CidrBlock' \ + if [ "$SECURITY_GROUP_ID" == "None" ]; then + echo "Creating new security group: $SECURITY_GROUP_NAME..." + SECURITY_GROUP_ID=$(aws ec2 create-security-group \ + --region "$AWS_REGION" \ + --group-name $SECURITY_GROUP_NAME \ + --description "Security group for Terraria server and bot" \ + --query 'GroupId' \ + --output text) + + # getting the default vpc cidr block + DEFAULT_VPC_CIDR=$(aws ec2 describe-vpcs \ + --filters Name=isDefault,Values=true \ + --query 'Vpcs[0].CidrBlock' \ + --region "$AWS_REGION" \ + --output text) + + # Add rules to the security group to allow necessary traffic + aws ec2 authorize-security-group-ingress \ + --region "$AWS_REGION" \ + --group-id "$SECURITY_GROUP_ID" \ + --protocol tcp \ + --port 7777 \ + --cidr "$DEFAULT_VPC_CIDR" + + aws ec2 authorize-security-group-ingress \ + --region "$AWS_REGION" \ + --group-id "$SECURITY_GROUP_ID" \ + --protocol tcp \ + --port 9256 \ + --cidr "$DEFAULT_VPC_CIDR" + + aws ec2 authorize-security-group-ingress \ + --region "$AWS_REGION" \ + --group-id "$SECURITY_GROUP_ID" \ + --protocol tcp \ + --port 9090 \ + --cidr "$DEFAULT_VPC_CIDR" + else + echo "Security group $SECURITY_GROUP_NAME already exists." + fi +} + +# Function to create an instance +create_instance() { + instance_name=$1 + instance_type=$2 + instance_id=$(aws ec2 run-instances \ --region "$AWS_REGION" \ + --image-id "$AMI_ID" \ + --count 1 \ + --instance-type "$INSTANCE_TYPE" \ + --key-name $KEY_PAIR_NAME \ + --security-group-ids "$SECURITY_GROUP_ID" \ + --query 'Instances[0].InstanceId' \ --output text) - - # Add rules to the security group to allow necessary traffic - aws ec2 authorize-security-group-ingress \ + tag_instance "$instance_id" "$instance_name" "$instance_type" + echo $instance_id +} + +# Function to tag an instance +tag_instance() { + instance_id=$1 + instance_name=$2 + instance_type=$3 + aws ec2 create-tags \ --region "$AWS_REGION" \ - --group-id "$SECURITY_GROUP_ID" \ - --protocol tcp \ - --port 22 \ - --cidr "0.0.0.0/0" # Adjust the CIDR block to restrict access as needed + --resources "$instance_id" \ + --tags Key=Name,Value="$instance_name" Key=EXP_TIME,Value="$EXP_TIME" Key=TYPE,Value="$instance_type" +} + +# Function to create bot instances +create_bot_instances() { + declare -a bot_instance_ids + for ((i=1; i<=$NUM_BOT_INSTANCES; i++)) + do + bot_instance_id=$(create_instance "bot$i" "bot") + bot_instance_ids+=($bot_instance_id) + done + echo "${bot_instance_ids[@]}" +} + +# Function to get an instance state +get_instance_state() { + instance_id=$1 + instance_name=$2 + echo "$instance_name state:" + aws ec2 describe-instances --instance-ids "$instance_id" --query 'Reservations[].Instances[].State.Name' +} + +# Function to wait for instances to be running +wait_for_instances() { + instance_ids=("$@") + for instance_id in "${instance_ids[@]}" + do + aws ec2 wait instance-running --instance-ids "$instance_id" + done +} - aws ec2 authorize-security-group-ingress \ +# Function for AWS Availability +get_aws_availability_zone() { + instance_id=$1 + echo $(aws ec2 describe-instances \ --region "$AWS_REGION" \ - --group-id "$SECURITY_GROUP_ID" \ - --protocol tcp \ - --port 7777 \ - --cidr "$DEFAULT_VPC_CIDR" -else - echo "Security group $SECURITY_GROUP_NAME already exists." -fi - -# Create the Terraria server instance -echo "Creating Terraria server instance..." -TERRARIA_SERVER_INSTANCE_ID=$(aws ec2 run-instances \ - --region "$AWS_REGION" \ - --image-id "$AMI_ID" \ - --count 1 \ - --instance-type "$INSTANCE_TYPE" \ - --key-name $KEY_PAIR_NAME \ - --security-group-ids "$SECURITY_GROUP_ID" \ - --query 'Instances[0].InstanceId' \ - --output text) - -echo "Terraria server instance created with ID: $TERRARIA_SERVER_INSTANCE_ID" - -# Create bot instances -declare -a BOT_INSTANCE_IDS -for ((i=1; i<=$NUM_BOT_INSTANCES; i++)) -do - BOT_INSTANCE_ID=$(aws ec2 run-instances \ - --region "$AWS_REGION" \ - --image-id "$AMI_ID" \ - --count 1 \ - --instance-type "$INSTANCE_TYPE" \ - --key-name $KEY_PAIR_NAME \ - --security-group-ids "$SECURITY_GROUP_ID" \ - --query 'Instances[0].InstanceId' \ - --output text) - BOT_INSTANCE_IDS+=($BOT_INSTANCE_ID) -done - -echo "Terraria bot instances created with IDs: ${BOT_INSTANCE_IDS[@]}" + --instance-ids "$instance_id" \ + --query 'Reservations[0].Instances[0].Placement.AvailabilityZone' \ + --output text) +} -# Print the instance states -echo "Server instance state:" -aws ec2 describe-instances --instance-ids "$TERRARIA_SERVER_INSTANCE_ID" --query 'Reservations[].Instances[].State.Name' -for BOT_INSTANCE_ID in "${BOT_INSTANCE_IDS[@]}" -do - echo "Bot instance $BOT_INSTANCE_ID state:" - aws ec2 describe-instances --instance-ids "$BOT_INSTANCE_ID" --query 'Reservations[].Instances[].State.Name' -done +# for setting SSH Public Key +set_ssh_public_key() { + instance_id=$1 + availability_zone=$2 + aws ec2-instance-connect send-ssh-public-key \ + --region "$AWS_REGION" \ + --instance-id "$instance_id" \ + --availability-zone "$availability_zone" \ + --instance-os-user ubuntu \ + --ssh-public-key file://temp/$KEY_PAIR_NAME.pub +} -# Wait for instances to be running -aws ec2 wait instance-running --instance-ids "$TERRARIA_SERVER_INSTANCE_ID" -for BOT_INSTANCE_ID in "${BOT_INSTANCE_IDS[@]}" -do - aws ec2 wait instance-running --instance-ids "$BOT_INSTANCE_ID" -done +# for getting public DNS +get_public_dns() { + instance_id=$1 + echo $(aws ec2 describe-instances \ + --region "$AWS_REGION" \ + --instance-ids "$instance_id" \ + --query 'Reservations[0].Instances[0].PublicDnsName' \ + --output text) +} + +# for SSH availability check +check_ssh_availability() { + public_dns=$1 + until ssh -o StrictHostKeyChecking=no -o CheckHostIP=no -o ConnectTimeout=5 -i temp/$KEY_PAIR_NAME.pem ubuntu@"$public_dns" true; do + echo "Waiting for SSH to be available..." + sleep 5 + done +} + +# for running remote commands +run_remote_commands() { + public_dns=$1 + commands=$2 + ssh -i temp/$KEY_PAIR_NAME.pem ubuntu@"$public_dns" -o StrictHostKeyChecking=no -t "$commands" +} + +# for bot instance setup +setup_bot_instance() { + bot_instance_id=$1 + aws_availability_zone_bot=$(get_aws_availability_zone $bot_instance_id) + set_ssh_public_key $bot_instance_id $aws_availability_zone_bot + bot_public_dns=$(get_public_dns $bot_instance_id) + check_ssh_availability $bot_public_dns + run_remote_commands $bot_public_dns "$remote_install_commands_bot" + BOT_PUBLIC_DNS_ADDRESSES+=("$bot_public_dns") +} remote_install_commands_server=$(cat <> ~/.bashrc echo "export PATH=\$PATH:\$HOME/.dotnet:\$HOME/.dotnet/tools" >> ~/.bashrc source ~/.bashrc + dir_name="$DIR_NAME" + mkdir -p "\$dir_name" + cd "\$dir_name" + mkdir -p server bot + cd server + curl -sL https://github.com/Pryaxis/TShock/releases/download/v5.1.3/TShock-5.1.3-for-Terraria-1.4.4.9-linux-x64-Release.zip -o TShock-5.1.3-for-Terraria-1.4.4.9-linux-x64-Release.zip + unzip TShock-5.1.3-for-Terraria-1.4.4.9-linux-x64-Release.zip + tar -xvf TShock-Beta-linux-x64-Release.tar + rm TShock-Beta-linux-x64-Release.tar TShock-5.1.3-for-Terraria-1.4.4.9-linux-x64-Release.zip + curl -sL https://github.com/ncabatoff/process-exporter/releases/download/v0.7.10/process-exporter-0.7.10.linux-amd64.tar.gz -o process-exporter.gz + tar -xvf process-exporter.gz && rm process-exporter.gz + cd ServerPlugins + curl -sL https://github.com/atlarge-research/yardstick/releases/download/$TERRASTICK_VERSION/server-side-packet-monitor.zip -o server-side-packet-monitor.zip + unzip -n server-side-packet-monitor.zip && rm server-side-packet-monitor.zip + cd ../../bot + curl -sL https://github.com/atlarge-research/yardstick/archive/refs/tags/$TERRASTICK_VERSION.zip -o terrastick.zip + unzip terrastick.zip && rm terrastick.zip + mv yardstick-$TERRASTICK_VERSION/terrastick/Deployment/worlds ../server/ + mv yardstick-$TERRASTICK_VERSION/terrastick/Deployment/metrics-configs/server-process-exporter.yaml ../server/process-exporter-0.7.10.linux-amd64/ +CMD +) + +# start the server instance +remote_start_commands_server=$(cat <> ~/.bashrc + echo "Added the variable \$variable_name to the bash RC file." + else + # Variable is already set + echo "The variable \$variable_name is already set in the bash RC file." + fi + done cd ~ wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh chmod +x dotnet-install.sh ./dotnet-install.sh --version latest - echo "export DOTNET_ROOT=\$HOME/.dotnet" >> ~/.bashrc - echo "export PATH=\$PATH:\$HOME/.dotnet:\$HOME/.dotnet/tools" >> ~/.bashrc source ~/.bashrc + dir_name="$DIR_NAME" + mkdir -p "\$dir_name" + cd "\$dir_name" + mkdir -p bot + cd bot + curl -sL https://github.com/atlarge-research/yardstick/archive/refs/tags/$TERRASTICK_VERSION.zip -o terrastick.zip + unzip terrastick.zip && rm terrastick.zip + cd yardstick-$TERRASTICK_VERSION/terrastick/PlayerEmulations/TrClientTest && dotnet build -r linux-x64 -c Release --no-self-contained || echo "Build failed" CMD ) -AWS_AVAILABILITY_ZONE_SERVER=$(aws ec2 describe-instances \ - --region "$AWS_REGION" \ - --instance-ids "$TERRARIA_SERVER_INSTANCE_ID" \ - --query 'Reservations[0].Instances[0].Placement.AvailabilityZone' \ - --output text) - -# Install dotnet on the server and bot instances -aws ec2-instance-connect send-ssh-public-key \ - --region "$AWS_REGION" \ - --instance-id "$TERRARIA_SERVER_INSTANCE_ID" \ - --availability-zone "$AWS_AVAILABILITY_ZONE_SERVER" \ - --instance-os-user ubuntu \ - --ssh-public-key file://temp/$KEY_PAIR_NAME.pub - -TERRARIA_SERVER_PUBLIC_DNS=$(aws ec2 describe-instances \ - --region "$AWS_REGION" \ - --instance-ids "$TERRARIA_SERVER_INSTANCE_ID" \ - --query 'Reservations[0].Instances[0].PublicDnsName' \ - --output text) - -until ssh -o StrictHostKeyChecking=no -o CheckHostIP=no -o ConnectTimeout=5 -i temp/$KEY_PAIR_NAME.pem ubuntu@"$TERRARIA_SERVER_PUBLIC_DNS" true; do - echo "Waiting for SSH to be available..." - sleep 5 -done +# start the bot instance -# Now you can run your remote commands: -ssh -i temp/$KEY_PAIR_NAME.pem ubuntu@"$TERRARIA_SERVER_PUBLIC_DNS" -o StrictHostKeyChecking=no -t "$remote_install_commands_server" +remote_start_commands_bot=$(cat <> ~/.bashrc + echo "export PATH=\$PATH:\$HOME/.dotnet:\$HOME/.dotnet/tools" >> ~/.bashrc + echo "export TERRASTICK_IP=$TERRARIA_SERVER_PRIVATE_IP" >> ~/.bashrc + source ~/.bashrc + dir_name="$DIR_NAME" + mkdir -p "\$dir_name" + cd "\$dir_name" + mkdir -p prometheus bot + cd prometheus + curl -sL https://github.com/prometheus/prometheus/releases/download/v2.37.8/prometheus-2.37.8.linux-amd64.tar.gz -o prometheus.gz + tar -xvf prometheus.gz && rm prometheus.gz + cd ../bot + curl -sL https://github.com/atlarge-research/yardstick/archive/refs/tags/$TERRASTICK_VERSION.zip -o terrastick.zip + unzip terrastick.zip && rm terrastick.zip + mv yardstick-$TERRASTICK_VERSION/terrastick/Deployment/metrics-configs/prometheus-terrastick.yml ../prometheus/prometheus-2.37.8.linux-amd64/ +CMD +) +remote_start_commands_prometheus=$(cat <= 0.5) ? xPos + deltaX : xPos - deltaX; + await this.WalkPlayer(xPos, yPos, newXPos, yPos); + this.ChatText("Teleported to " + newXPos + " " + yPos); + if (secs-- == 0) + { + this.ChatText("WORKLOAD COMPLETE"); + break; + } + } + timer.Dispose(); + } + + public bool connected = false; public void GameLoop(string host, int port, string password) @@ -310,6 +346,32 @@ public void TeleportPlayer(int x, int y) // Home Position Y Single Home Position for Potion of Return, only sent if UsedPotionofReturn flag is true }); + this.SpawnX = x; + this.SpawnY = y; + } + + public async Task WalkPlayer(int x1,int y1,int x2,int y2) + { + var lerp = new Vector2 { X = x1, Y = y1 }; + var lerp2 = new Vector2 { X = x2, Y = y2 }; + var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(1000)); + while (await timer.WaitForNextTickAsync()) + { + lerp = Vector2.Lerp(lerp, lerp2, 0.5f); + Send(new UpdatePlayer + { + PlayerSlot = this.PlayerSlot, + Bit1 = 0, + Bit2 = 0, + Bit3 = 0, + Bit4 = 0, + SelectedItem = 0, + Position = new Vector2 { X = lerp.X, Y = lerp.Y }, + Velocity = new Vector2 { X = lerp2.X - lerp.X, Y = lerp2.Y - lerp.Y } + }); + this.SpawnX = (int)lerp.X; + this.SpawnY = (int)lerp.Y; + } } private void GameLoopInternal(string password) { @@ -317,12 +379,6 @@ private void GameLoopInternal(string password) Console.WriteLine("Sending Client Hello..."); Hello(CurRelease); - /*TcpClient verify = new TcpClient(); - byte[] raw = Encoding.ASCII.GetBytes("-1551487326"); - verify.Connect(new IPEndPoint(endPoint.Address, 7980)); - verify.GetStream().Write(raw, 0, raw.Length); - verify.Close();*/ - On(_ => Send(new SendPassword { Password = password })); //On(_=> ) connected = true; diff --git a/terrastick/PlayerEmulations/TrClientTest/Program.cs b/terrastick/PlayerEmulations/TrClientTest/Program.cs index 43ea1f64..00513828 100644 --- a/terrastick/PlayerEmulations/TrClientTest/Program.cs +++ b/terrastick/PlayerEmulations/TrClientTest/Program.cs @@ -20,7 +20,8 @@ static void Main(string[] args) { string ip = "127.0.0.1"; string workload = "TEL"; - string name = "BOT"; + // name is botname and last 5 characters of hostname + string name = "BOT_" + Environment.MachineName.Substring(Environment.MachineName.Length - 5); string logpath = "terrastick_bot_logs"; if (Environment.GetEnvironmentVariable("TERRASTICK_IP") != null) diff --git a/terrastick/PlayerEmulations/TrProtocol/Models/Vector2.cs b/terrastick/PlayerEmulations/TrProtocol/Models/Vector2.cs index 80692b30..ec67582d 100644 --- a/terrastick/PlayerEmulations/TrProtocol/Models/Vector2.cs +++ b/terrastick/PlayerEmulations/TrProtocol/Models/Vector2.cs @@ -18,4 +18,11 @@ public override string ToString() { return $"[{X}, {Y}]"; } + public static Vector2 Lerp(Vector2 origin,Vector2 dest, float amount) + { + var X = origin.X + (dest.X - origin.X) * amount; + var Y = origin.Y + (dest.Y - origin.Y) * amount; + return new Vector2(X, Y); + + } }