Skip to content

Commit

Permalink
Merged in steam (pull request SectorAlpha#7)
Browse files Browse the repository at this point in the history
Pushing steam changes into the main Branch
  • Loading branch information
staircase27 committed Dec 6, 2016
2 parents 54fde57 + f0ef4c2 commit 3a662ef
Show file tree
Hide file tree
Showing 18 changed files with 671 additions and 7 deletions.
22 changes: 22 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,33 @@ Dependencies
python3-crontab
sudo (for multi user/shared downloads support)

SteamCMD dependencies

lib32gcc1 or libstdc++ or libstdc++.i686

e.g ubuntu, ib32stdc++6

for Ubuntu/Debian, redhat/centos and redhat/centos 64 bit respectively (pick one).
see https://developer.valvesoftware.com/wiki/SteamCMD#Linux for more details.

Example of setting up of a Minecraft vanilla server

alphagsm mymcserver create minecraft setup
alphagsm mymcserver start

Example of setting up a CS:GO server

alphagsm mycsgo create csgo
alphagsm mycsgo setup
alphagsm mycsgo start

Example of updating the CS:GO server (A specific command for updating the server files). These commands are not specific to all game servers (e.g Minecraft).

alphagsm mycsgo update
alphagsm mycsgo update -v -r

Where the -v flag requests the validation of files, and -r will restart your server once the update has been done.

This will create a new minecraft server called mymcserver and set it up
asking you for which version and port to use and any other info it needs.
The second command will (assuming the setup succeded) start the server.
Expand Down
2 changes: 2 additions & 0 deletions alphagsm.conf-template
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
# target_path = /home/alphagsm_user/.alphagsm/downloader/downloads
## system - The package that downloadermodules are in
# downloaders_package = downloadermodules.
## steamcmd_path - The directory SteamCMD is installed into
# steamcmd_path = /home/alphagsm_user/Steam/

[downloader.pathgen]
## directories are generated in two levels. By default it's a parent directory with a short name with
Expand Down
4 changes: 2 additions & 2 deletions core/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ def runone(name,server,cmd,args):
program.PATH=os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__)))),"alphagsm")
try:
server.run_command(cmd,*args,**opts)
# developers, be sure to check if command_functions dictionary has been filled
# otherwise the error called here will make no sense
except ServerError as ex:
print("Error running Command",file=stderr)
printhandledex(ex)
Expand Down Expand Up @@ -309,5 +311,3 @@ def help(name,server,cmd=None,*,file=stderr):
help(name,server,file=file)
return
cmdparse.longhelp(cmd,server.get_command_description(cmd),server.get_command_args(cmd))


84 changes: 84 additions & 0 deletions downloadermodules/steamcmd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# WARNING, THIS MODULE IS NOT SUPPORTED FOR NOW
# USE AT YOUR OWN RISK
# Or message the developers if you would like to help get this working.
# There may be major bugs and errors and it isn't at all tested.
from utils.settings import settings
from . import url as urlextra
import downloader.downloader as downloader
import pwd
import os
import shutil



USER=settings.system.downloader.get('user') or pwd.getpwuid(os.getuid()).pw_name
# if a user has already installed steam to e.g ubuntu, steamcmd prefers to be installed in the same directory (or at least when steamcmd starts, it sends the error related things there as if it wants to be installed there.
STEAMCMD_DIR = settings.system.downloader.get('steamcmd_path') or "/home/" + USER + "/.local/share/Steam/" if os.path.isdir( "/home/" + USER + "/.local/share/Steam/") else "/home/" + USER + "/Steam/"
STEAMCMD_EXE = STEAMCMD_DIR + "steamcmd.sh"
STEAMCMD_URL = "https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz"

# check if steamcmd exists, if not download it and install it via wget https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz
# execute steamcmd/steamcmd.sh
# <user> = Anonymous by default
# ./steamcmd +login <user> +force_install_dir <download_location> +app_update <appid> +quit

def install_steamcmd():

# if steamcmd dir does not exist, download it
if not os.path.exists(steam_cmd_install_dir):
os.makedirs(STEAMCMD_DIR)

if not os.path.isfile(STEAMCMD_EXE):
# if steamcmd files do not exist, download it
urlextra.download(STEAMCMD_DIR,(STEAMCMD_IRL,"steamcmd_linux.tar.gz","tar.gz"))


def download(path,args):
""" downloads a game via steamcmd"""
Steam_AppID, version, steam_anonymous_login_possible = *args
version = int(version)
# check to see if steamcmd exists
install_steamcmd()
# run steamcmd
existing = downloader.getpaths("steamcmd", sort = "version", Steam_AppID = Steam_AppID, steam_anonymous_login_possible = steam_anonymous_login_possible)

if len(existing) > 0:
lmodule,largs,llocation,ldate,lactive = existing[0]
shutil.copytree(llocation,path)

if bool(steam_anonymous_login_possible):
print("Running SteamCMD")
proc_list = [STEAMCMD_EXE,"+login","anonymous","+force_install_dir",target_dir,"+app_update",str(Steam_AppID),"+quit"]
sp.call(proc_list)
else:
print("no support for normal SteamCMD logins yet.")

def getfilter(active=None,Steam_AppID=None,steam_anonymous_login_possible=None,sort=None):
filterfn=_true
sortfn=None
if active!=None:
active=bool(active)
if Steam_AppID!=None:
if steam_anonymous_login_possible!=None:
filterfn=lambda lmodule,largs,llocation,ldate,lactive: active == lactive and str(Steam_AppID) == largs[0] and str(steam_anonymous_login_possible) == largs[2]
else
filterfn=lambda lmodule,largs,llocation,ldate,lactive: active == lactive and str(Steam_AppID) == largs[0]
elif steam_anonymous_login_possible!=None:
filterfn=lambda lmodule,largs,llocation,ldate,lactive: active == lactive and str(steam_anonymous_login_possible) == largs[2]
else
filterfn=lambda lmodule,largs,llocation,ldate,lactive: active == lactive
elif Steam_AppID!=None:
if steam_anonymous_login_possible!=None:
filterfn=lambda lmodule,largs,llocation,ldate,lactive: str(Steam_AppID) == largs[0] and str(steam_anonymous_login_possible) == largs[2]
else
filterfn=lambda lmodule,largs,llocation,ldate,lactive: str(Steam_AppID) == largs[0]
else if steam_anonymous_login_possible!=None:
filterfn=lambda lmodule,largs,llocation,ldate,lactive: str(steam_anonymous_login_possible) == largs[2]
if sort == "version":
sortfn=lambda lmodule,largs,llocation,ldate,lactive: int(largs[1])
elif sort != None:
raise DownloaderError("Unknown sort key")
return filterfn,sortfn

def _true(*arg):
return True
4 changes: 2 additions & 2 deletions downloadermodules/url.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def download(path,args):
raise DownloaderError("Too many arguments")
elif len(args)==1:
decompress=args[0]
if decompress not in ["zip","tar","gz","tgz"]:
if decompress not in ["zip","tar","gz","tgz","tar.gz"]:
raise DownloaderError("Unknown decompression type")
if decompress in ["gz"]: # compression without filenames
targetname+="."+decompress
Expand All @@ -39,7 +39,7 @@ def download(path,args):
ret=sp.call(["unzip",targetname,"-d",path],stdout=sys.stderr)
if ret!=0:
raise DownloaderError("Error extracting download")
elif decompress == "tar":
elif decompress == "tar" or decompress == "tar.gz":
ret=sp.call(["tar","-xf",targetname,"-C",path],stdout=sys.stderr)
if ret!=0:
raise DownloaderError("Error extracting download")
Expand Down
195 changes: 195 additions & 0 deletions gamemodules/counterstrikeglobaloffensive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import os
import urllib.request
import json
import time
import datetime
import subprocess as sp
from server import ServerError
import re
import screen
import downloader
import utils.updatefs
from utils.cmdparse.cmdspec import CmdSpec,OptSpec,ArgSpec
from utils import backups
from utils import updatefs
import random

from utils.fileutils import make_empty_file

import utils.steamcmd as steamcmd

steam_app_id = 740
steam_anonymous_login_possible = True

commands=("update","restart")
command_args={"setup":CmdSpec(optionalarguments=(ArgSpec("PORT","The port for the server to listen on",int),ArgSpec("DIR","The Directory to install minecraft in",str),)),
"update":CmdSpec(options=(OptSpec("v",["validate"],"Validate the server files after updating",'validate',None,True), \
OptSpec("r",["restart"],"Restarts the server upon updating",'restart',None,True),)),
"restart":CmdSpec()}

# required still
command_descriptions={"update": "Updates the game server to the latest version.",
"restart": "Restarts the game server without killing the process."}


# required still
command_descriptions={}
command_functions={} # will have elements added as the functions are defined


_confpat=re.compile(r"\s*([^ \t\n\r\f\v#]\S*)\s* (?:\s*(\S+))?(\s*)\Z")
def updateconfig(filename,settings):
lines=[]
if os.path.isfile(filename):
settings=settings.copy()
with open(filename,"r") as f:
for l in f:
m=_confpat.match(l)
if m is not None and m.group(1) in settings:
lines.append(m.expand(r"\1 "+settings[m.group(1)]+r"\3"))
del settings[m.group(1)]
else:
lines.append(l)
for k,v in settings.items():
lines.append(k+" "+v+"\n")
print(lines)
with open(filename,"w") as f:
f.write("".join(lines))

def configure(server,ask,port=None,dir=None,*,exe_name="srcds_run"):
"""
This function creates the configuration details for the server
inputs:
server: the server object
ask: whether to request details (e.g port) from the user
dir: the server installation dir
*: All arguments after this are keyword only arguments
exe_name: the executable name of the server
"""


server.data["Steam_AppID"] = steam_app_id
server.data["Steam_anonymous_login_possible"] = steam_anonymous_login_possible

# set some defaults
server.data["mapgroup"] = "mg_active"
server.data["startmap"] = "de_dust2"
server.data["maxplayers"] = "16"
server.data["gametype"] = "0"
server.data["gamemode"] = "0"


# do we have backup data already? if not initialise the dictionary
if 'backup' not in server.data:
server.data['backup']={}
if 'profiles' not in server.data['backup']:
server.data['backup']['profiles']={}
# if no backup profile exists, create a basic one
if len(server.data['backup']['profiles'])==0:
# essentially, the world, server properties and the root level json files are the best ones to back up. This can be configured though in the backup setup
server.data['backup']['profiles']['default']={'targets':{}}
if 'schedule' not in server.data['backup']:
server.data['backup']['schedule']=[]
if len(server.data['backup']['schedule'])==0:
# if default does not exist, create it
profile='default'
if profile not in server.data['backup']['profiles']:
profile=next(iter(server.data['backup']['profiles']))
# set the default to never back up
server.data['backup']['schedule'].append((profile,0,'days'))

# assign the port to the server
if port is None and "port" in server.data:
port=server.data["port"]
if ask:
while True:
inp=input("Please specify the port to use for this server: "+("(current=" +str(port) + ") " if port is not None else "")).strip()
if port is not None and inp == "":
break
try:
port=int(inp)
except ValueError as v:
print(inp+" isn't a valid port number")
continue
break
if port is None :
raise ValueError("No Port")
server.data["port"]=port

# assign install dir for the server
if dir is None:
if "dir" in server.data and server.data["dir"] is not None:
dir=server.data["dir"]
else:
dir=os.path.expanduser(os.path.join("~",server.name))
if ask:
inp=input("Where would you like to install the tf2 server: ["+dir+"] ").strip()
if inp!="":
dir=inp
server.data["dir"]=os.path.join(dir, "") # guarentees the inclusion of trailing slashes.

# if exe_name is not asigned, use the function default one
if not "exe_name" in server.data:
server.data["exe_name"] = "srcds_run" # don't use srcds_linux, as srcds_run sorts out your environment for you
server.data.save()

return (),{}


def install(server):
doinstall(server)
#TODO: any config files that need creating or any commands that need running before the server can start for the first time

# create config file
server_cfg = server.data["dir"] + "csgo/cfg/" + "server.cfg"
cfg_exists = os.path.isfile(server_cfg)
if cfg_exists == False:
make_empty_file(server_cfg)

# technically this command is not needed, but leaving it commented as an example
# updateconfig(server_cfg,{"hostport":str(server.data["port"])})


def doinstall(server):
""" Do the installation of the latest version. Will be called by both the install function thats part of the setup command and by the auto updater """
if not os.path.isdir(server.data["dir"]):
os.makedirs(server.data["dir"])

steamcmd.download(server.data["dir"],server.data["Steam_AppID"],server.data["Steam_anonymous_login_possible"],validate=True)


def restart(server):
server.stop()
server.start()

def update(server,validate=False,restart=False):
try:
server.stop()
except:
print("Server has probably already stopped, updating")
steamcmd.download(server.data["dir"],steam_app_id,steam_anonymous_login_possible,validate=validate)
print("Server up to date")
if restart == True:
print("Starting the server up")
server.start()

def get_start_command(server):
# sample start command
# ./srcds_run -game csgo -console -usercon +game_type 0 +game_mode 0 +mapgroup mg_active +map de_dust2 -maxplayers 30
exe_name = server.data["exe_name"]
if not os.path.isfile(server.data["dir"] + exe_name):
ServerError("Executable file not found")

if exe_name[:2] != "./":
exe_name = "./" + exe_name
return [exe_name,"-game","csgo","-console","-usercon","+game_type",str(server.data["gametype"]),"+game_mode",str(server.data["gamemode"]),"-port",str(server.data["port"]),"+mapgroup",str(server.data["mapgroup"]),"+map",str(server.data["startmap"]),"-maxplayers",str(server.data["maxplayers"])],server.data["dir"]

def do_stop(server,j):
screen.send_to_server(server.name,"\nquit\n")

def status(server,verbose):
pass

# required, must be defined to allow functions listed below which are not in the defaults to be used
command_functions={"update":update,"restart":restart} # will have elements added as the functions are defined
1 change: 1 addition & 0 deletions gamemodules/csgo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALIAS_TARGET="counterstrikeglobaloffensive"
2 changes: 2 additions & 0 deletions gamemodules/factorio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#download directory https://www.factorio.com/download-headless
raise NotImplementedError("This game module has not been implemented yet")
Loading

0 comments on commit 3a662ef

Please sign in to comment.