Skip to content

Instantly share code, notes, and snippets.

@dries007
Last active September 17, 2015 19:04
Show Gist options
  • Save dries007/47c87ee7ad332398e017 to your computer and use it in GitHub Desktop.
Save dries007/47c87ee7ad332398e017 to your computer and use it in GitHub Desktop.
ARK Survival Evolved Dedigated server run script
#!/bin/bash
#
# ARK SURVIVAL EVOLVED Dedigated server run script
#
# Assumed to be installed:
# - tmux,
# - steamcmd (in users home),
# - rcon (in user home, see second file on gist)
#
# MAKE SURE YOU ALWAYS RUN THIS AS THE SAME (NOT ROOT) USER!
# USE crontab -u <usename> -e TO MAKE SURE YOUR CRONJOB IS EXECUTED AS THAT USER TOO!
#
# Supposed to be run as a cronjob at regular (I use 5 min) intervals.
# Uses Tmux to assure you can take over control of the server at any point.
# To do so, use 'tmux attach -t Ark' be default (from the user running the cronjob!)
#
# Written by Dries007 (sept 2015), find source & updates at https://gist.github.com/dries007
# Under MIT License (https://opensource.org/licenses/MIT)
#
TMUX_SESSION_NAME=Ark
STEAM_CMD_DIR=~
SERVERDIR=~/ark
APPCACHE_DIR=$STEAM_CMD_DIR/Steam/appcache
IN_PROGRESS_MARKER=$SERVERDIR/updateinprogress
FILE_INSTALLED_VERSION=$SERVERDIR/installedversion.txt
FILE_VERSION_LOG=$SERVERDIR/versions.log
APP_ID="376030"
RCON_PASSWORD=PleaseReplaceWithRealPassword
RCON_IP=127.0.0.1
RCON_PORT=32330
RCON_FILE=~/rcon
RCON_CMD=$RCON_FILE' -P'$RCON_PASSWORD' -a'$RCON_IP' -p'$RCON_PORT
STEAM_CMD=$STEAM_CMD_DIR'/steamcmd.sh +login anonymous'
CMD_INFO=$STEAM_CMD' +app_info_update 1 +app_info_print '$APP_ID' +quit'
CMD_INSTALL=$STEAM_CMD' +force_install_dir '$SERVERDIR' +app_update '$APP_ID' +quit'
CMD_START_SERVER=$SERVERDIR"/ShooterGame/Binaries/Linux/ShooterGameServer \"TheIsland?RCONEnabled=True?RCONPort="$RCON_PORT"?listen\" -server -log"
function isOutOfDate()
{
rm -fr $APPCACHE_DIR
INSTALLED=`cat $FILE_INSTALLED_VERSION`
LATEST=`$CMD_INFO | grep -EA 1000 '"branches"' | tr -s '[:blank:]\n' ' ' | sed -En 's/^ "branches" \{ "public" \{ "buildid" "([0-9]+)".*/\1/p'`
return $(( $LATEST > $INSTALLED ))
}
if [ -f $IN_PROGRESS_MARKER ] ; then
echo 'Already updating.'
exit 0
fi
touch $IN_PROGRESS_MARKER
if [ ! -f $FILE_INSTALLED_VERSION ] ; then # No installed version file, assume worst case senario, set to 0
echo '0' > $FILE_INSTALLED_VERSION
fi
# Check to see if the server is running
tmux has-session -t $TMUX_SESSION_NAME 2>/dev/null
if (( $? == 0 )) ; then # Server is running
echo 'Server is running'
if isOutOfDate; then
echo 'Server is out of date.'
$RCON_CMD' broadcast New update available, server is restarting in 5 minutes!'
sleep 4m
$RCON_CMD' broadcast New update available, server is restarting!'
$RCON_CMD' saveworld'
$RCON_CMD' broadcast Any changes beond this point MAY BE LOST.'
sleep 1m
$RCON_CMD' doexit'
if (( $? != 0 )) ; then
echo 'Using ^C!'
tmux send-key -t $TMUX_SESSION_NAME C-c
fi
sleep 25
echo `date` ' ' $INSTALLED ' -> ' $LATEST >> $FILE_VERSION_LOG
$CMD_INSTALL
$CMD_INFO | grep -EA 1000 '"branches"' | tr -s '[:blank:]\n' ' ' | sed -En 's/^ "branches" \{ "public" \{ "buildid" "([0-9]+)".*/\1/p' > $FILE_INSTALLED_VERSION
tmux new-session -d -s Ark $CMD_START_SERVER
else
echo 'Server is running & up to date.'
fi
else # Server is not running
echo 'Server is NOT running'
if isOutOfDate; then
echo 'Server is out of date.'
echo `date` ' ' $INSTALLED ' -> ' $LATEST >> $FILE_VERSION_LOG
$CMD_INSTALL
$CMD_INFO | grep -EA 1000 '"branches"' | tr -s '[:blank:]\n' ' ' | sed -En 's/^ "branches" \{ "public" \{ "buildid" "([0-9]+)".*/\1/p' > $FILE_INSTALLED_VERSION
fi
tmux new-session -d -s Ark $CMD_START_SERVER
fi
rm $IN_PROGRESS_MARKER
/*
# This is a simple linux command line utility to execute rcon commands
# once downloaded on your linux system, compile it with:
#
# gcc -o rcon rcon.c
#
# written by [ASY]Zyrain
#
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#define DEBUG 0
#define SERVERDATA_EXECCOMMAND 2
#define SERVERDATA_AUTH 3
#define SERVERDATA_RESPONSE_VALUE 0
#define SERVERDATA_AUTH_RESPONSE 2
int send_rcon(int sock, int id, int command, char *string1, char *string2) {
int size, ret;
size = 10+strlen(string1)+strlen(string2);
ret = send(sock,&size,sizeof(int),0);
if(ret == -1) {
perror("send() failed:");
return -1;
}
ret = send(sock,&id,sizeof(int),0);
if(ret == -1) {
perror("send() failed:");
return -1;
}
ret = send(sock,&command,sizeof(int),0);
if(ret == -1) {
perror("send() failed:");
return -1;
}
ret = send(sock,string1,strlen(string1)+1,0);
if(ret == -1) {
perror("send() failed:");
return -1;
}
ret = send(sock,string2,strlen(string2)+1,0);
if(ret == -1) {
perror("send() failed:");
return -1;
}
if(DEBUG) printf("Sent %d bytes\n",size+4);
return 0;
}
int recv_rcon(int sock, int timeout, int *id, int *command, char *string1,
char *string2) {
struct timeval tv;
fd_set readfds;
int size;
char *ptr;
int ret;
char buf[8192];
size=0xDEADBEEF;
*id=0xDEADBEEF;
*command=0xDEADBEEF;
string1[0]=0;
string2[0]=0;
tv.tv_sec = timeout;
tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
/* don't care about writefds and exceptfds: */
select(sock+1, &readfds, NULL, NULL, &tv);
if (!FD_ISSET(sock, &readfds)) {
if(DEBUG) {
printf("recv timeout\n");
}
return -1; // timeout
}
if(DEBUG) printf("Got a response\n");
ret = recv(sock, &size, sizeof(int), 0);
if(ret == -1) {
perror("recv() failed:");
return -1;
}
if((size<10) || (size>8192)) {
printf("Illegal size %d\n",size);
exit(-1);
}
ret = recv(sock, id, sizeof(int),0);
if(ret == -1) {
perror("recv() failed:");
return -1;
}
size-=ret;
ret = recv(sock, command, sizeof(int),0);
if(ret == -1) {
perror("recv() failed:");
return -1;
}
size-=ret;
ptr = buf;
while(size) {
ret = recv(sock, ptr, size, 0);
if(ret == -1) {
perror("recv() failed:");
return -1;
}
size -= ret;
ptr += ret;
}
buf[8190] = 0;
buf[8191] = 0;
strncpy(string1, buf, 4095);
string1[4095] = 0;
strncpy(string2, buf+strlen(string1)+1, 4095);
return 0;
}
/* This is set to 1 when we've been authorized */
int auth = 0;
char string1[4096];
char string2[4096];
int process_response(int sock) {
int ret;
int id;
int command;
ret=recv_rcon(sock, 1, &id, &command, string1, string2);
if(DEBUG) printf("Received = %d : id=%d, command=%d, s1=%s, s2=%s\n",
ret, id, command, string1, string2);
if(ret==-1) {
return -1;
}
switch(command) {
case SERVERDATA_AUTH_RESPONSE:
switch(id) {
case 20:
auth = 1;
break;
case -1:
printf("Password Refused\n");
return -1;
default:
printf("Bad Auth Response ID = %d\n",id);
exit(-1);
};
break;
case SERVERDATA_RESPONSE_VALUE:
printf("%s",string1);
break;
default:
printf("Unexpected command: %d",command);
break;
};
}
int main(int argc, char **argv)
{
struct sockaddr_in a;
int sock;
int ret, i;
char password[512]="";
short port = 27015;
char address[512] = "127.0.0.1";
int arg;
auth = 0;
if(argc<2)
{
printf("Syntax: rcon [-P\"rcon_password\"] [-a127.0.0.1] [-p27015] command\n");
return 0;
}
for(arg = 1;arg<argc;arg++) {
if(argv[arg][0] != '-')
break; /* done with args */
switch(argv[arg][1]) {
case 'a':
strncpy(address, argv[arg]+2, 512);
break;
case 'p':
port = atoi(argv[arg]+2);
break;
case 'P':
strncpy(password, argv[arg]+2, 512);
break;
default:
fprintf(stderr, "Unknown option -%c\n",argv[arg][1]);
return 0;
}
}
a.sin_family = AF_INET;
a.sin_addr.s_addr = inet_addr(address);
a.sin_port = htons(port);
sock = socket(AF_INET, SOCK_STREAM,0); // TCP socket
ret = 0;
ret = connect(sock,(struct sockaddr *)&a,sizeof(a));
if(ret == -1) {
perror("connect() failed.");
return -1;
} else {
if(DEBUG) printf("Connected to Server\n");
}
if(DEBUG) printf("Sending RCON Password\n");
ret=send_rcon(sock, 20, SERVERDATA_AUTH, password, "");
if(ret == -1) {
perror("Sending password");
return -1;
};
while(auth==0) {
if(process_response(sock)==-1) {
printf("Couldn't Authenticate\n");
exit(-1);
}
}
if(DEBUG) printf("Password Accepted\n");
/* Now we're authorized, send command */
/* built command */
ret = 0;
while(arg < argc) {
if(strlen(argv[arg]) + ret < 4096) {
strcpy(string1+ret, argv[arg]);
ret += strlen(argv[arg]);
string1[ret] = ' ';
ret++;
arg++;
} else {
fprintf(stderr, "cmd too long to send\n");
return -1;
}
}
// string1[ret] = '\n';
//ret++;
ret--;
string1[ret]=0;
if(DEBUG) printf("Sending Command: \"%s\"\n", string1);
ret=send_rcon(sock, 20, SERVERDATA_EXECCOMMAND, string1, "");
if(ret == -1) {
perror("cmd send");
return -1;
}
// process responses until a timeout
while(process_response(sock) != -1);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment