Skip to content

Instantly share code, notes, and snippets.

@LordH3lmchen
Last active August 29, 2015 14:19
Show Gist options
  • Save LordH3lmchen/9e2cf53114acd27abe03 to your computer and use it in GitHub Desktop.
Save LordH3lmchen/9e2cf53114acd27abe03 to your computer and use it in GitHub Desktop.

Revisions

  1. LordH3lmchen revised this gist Apr 26, 2015. 1 changed file with 128 additions and 20 deletions.
    148 changes: 128 additions & 20 deletions ftb_minecraft_server_launcher.py
    Original file line number Diff line number Diff line change
    @@ -17,8 +17,6 @@
    * zsh (one of the best Unix shells out there)
    * screen (to run the Minecraft console in background)
    * wget (a commandline tool to download file via http)
    * vim (a text-editor)
    It will show the command to install the missing programs and exits, if something is needed.
    @@ -29,7 +27,6 @@
    http://git.grml.org/f/grml-etc-core/etc/zsh/zshrc --> ~/.zshrc
    http://git.grml.org/f/grml-etc-core/etc/skel/.zshrc --> ~/.zshrc.local
    creates a ~/.jre directory for java installations
    downloads the latest java to ~/.jre
    @@ -47,13 +44,16 @@
    Server Setup
    ------------
    mport
    #### Step 1. Repository Server Listen holen
    #### Step 1. Fetches the repository server lists
    http://www.creeperrepo.net/edges.json
    http://ftb.cursecdn.com/edges.json
    #### Modpacks.xml von einem der Repo Server laden
    modpacks.xml isnt the latest version on all mirrors
    third party modpacks
    thirdparty.xml
    ...../FTB2/static/modpacks.xml
    @@ -78,16 +78,19 @@
    import json
    import xml.etree.ElementTree
    import zipfile
    import shutil
    import stat

    """
    Exception wird geworfen wenn eine ungütlige destination angegeben wird.
    """
    class InvalidDestinationException(Exception):
    def __init__(self, destination):
    self.destination = destination
    class FtbServerLauncherException(Exception): #TODO implement basic errorhandling with that exception
    def __init__(self, message):
    self.message = message




    @@ -124,6 +127,43 @@ def regex_websearch(url, pattern):




    def get_java_download_url(java_version=8, packag='server-jre', extension='tar.gz', architecture='linux-x64'):
    valid_packages = ['jre', 'server-jre', 'jdk']
    if package not in valid_packages:
    print('Invalid Java package selection, valid packages are:')
    for valid_package in valid_packages:
    print('\t' + valid_package)
    return None

    url = "http://www.oracle.com"

    url_1 = url + "/technetwork/java/javase/downloads/index.html"
    pattern_1 = '\/technetwork\/java/\javase\/downloads\/'+ package + str(java_version) + '-downloads-.+?\.html'
    match = regex_websearch(url_1, pattern_1)
    if match == None:
    print('Unable to download Java from ' + url_1)
    print('Website is down or script is outdated')
    return None

    url_2 = url + match.group(0)
    pattern_2 = "http\:\/\/download.oracle\.com\/otn-pub\/java\/jdk\/[7-9]u[0-9]+?-.+?\/" + package + "-[7-9]u[0-9]+?-" + architecture + "." + extension
    match = regex_websearch(url_2, pattern_2)
    if match == None:
    print('Selected architecture.extension \"' + architecture + '.' + extension + '\" is not available')
    print('Visit \"' + url_2 + '\" to see available architectures and extensions')
    return None


    req = urllib.request.Request(match.group(0))
    req.add_header('Cookie', 'oraclelicense=accept-securebackup-cookie')
    destination = dlfile(req, destination)


    return destination



    """
    this function downloads the lates Java
    @@ -134,17 +174,12 @@ def regex_websearch(url, pattern):
    """
    def download_latest_java(destination, java_version=8, package='server-jre', extension='tar.gz', architecture='linux-x64'):



    valid_packages = ['jre', 'server-jre', 'jdk']
    if package not in valid_packages:
    print('Invalid Java package selection, valid packages are:')
    for valid_package in valid_packages:
    print('\t' + valid_package)
    return None



    url = "http://www.oracle.com"

    @@ -190,8 +225,8 @@ def install_latest_java(destination, java_version=8, package='server-jre', exten
    def userhome_setup():
    missing_programs = []
    home_dir=os.environ['HOME']
    for program in ["zsh", "screen", "wget", "vim" ]:
    if(os.system("which " + program)!=0):
    for program in ["zsh", "screen", "vim" ]:
    if shutil.which(program) == None:
    missing_programs.append(program)
    if(len(missing_programs)!=0):
    install_command="apt-get install "
    @@ -239,26 +274,80 @@ def interactive_selection(message, data):
    for key in data.keys():
    print(str(i) + ': \t' + key + ' - ' + data.get(key)) #TODO replace with printf (for better look and feel
    i+=1
    selection = list(data.keys())[int(input("Selection: "))]
    selection = list(data.keys())[int(input("Selection: "))] # TODO check user input
    return (selection, data.get(selection))
    if isinstance(data, list):
    for item in data:
    print(str(i) + ': \t' + item)
    i+=1
    index = int(input("Selection: "))
    index = int(input("Selection: ")) #TODO check user input
    return (index, data[index])


    def ask_user(message, default_value, input_check_regex):
    input_str = ''
    while re.search(input_check_regex, input_str) == None:
    input_str = input(message + ' [' + default_value + ']: ')
    if input_str == '':
    input_str=default_value
    break
    return input_str

    """
    returns /cat/proc/meminfo as dictionary with a ( amount, unittype ) tuple as data. If unittype is a empty string
    the amount is a count
    """
    def meminfo():
    meminfo_d = {}

    if(os.path.exists('/proc/meminfo')):
    with open('/proc/meminfo', 'r') as proc_meminfo:
    meminfo_content = proc_meminfo.readlines()
    for line in meminfo_content:
    match = re.search('(^[a-zA-Z0-9_\(\)]+): +(\d+) {0,1}(\w*)', line)
    meminfo_d.update({match.group(1): (int(match.group(2)), match.group(3))})
    return meminfo_d
    else:
    return None


    def num_of_processing_units():
    return int(subprocess.check_output('nproc').decode('UTF-8'))

    def update_server_start_sh(server_start_sh_path, xmx_value=2048, xms_value=512, java_version=8, thread_count=1):
    server_start_sh_content = open(server_start_sh_path, 'r').read()
    # shutil.move(destination_folder + '/ServerStart.sh', destination_folder + '/ServerStart.sh.bak')
    shutil.move(server_start_sh_path, server_start_sh_path + '.bak')
    match = re.search('java .+-jar ([a-zA-Z0-9_\-\.]+) (.*)$', server_start_sh_content, re.MULTILINE)
    server_start_sh_startline = match.group(0)
    server_jar=match.group(1)
    server_jar_cmd_arguments = match.group(2)
    foldername = os.path.basename(os.path.dirname(server_start_sh_path))

    if java_version==8 :
    #remove depracted options
    server_start_sh_startline = 'screen -dmS ' + foldername + ' java -Xms'+str(xms_value)+'m -Xmx'+str(xmx_value)+ \
    'm -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+CMSIncrementalPacing -XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads='\
    +str(thread_count)+' -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10 -jar '+ server_jar + ' ' + server_jar_cmd_arguments + '\n'

    server_start_sh_content = re.sub('java .+-jar ([a-zA-Z0-9_\-\.]+) (.*)$', server_start_sh_startline , server_start_sh_content, flags=re.MULTILINE)
    with open(server_start_sh_path, 'w') as f:
    f.write(server_start_sh_content)
    f.close()
    os.chmod(server_start_sh_path, stat.S_IXUSR | stat.S_IWUSR | stat.S_IRUSR) # chmod 700


    def ftb_server_install():

    #1 Fetching Serverlists
    # 1 Download and extract server files
    repo_servers = json.loads(urllib.request.urlopen('http://www.creeperrepo.net/edges.json').read().decode('UTF-8'))
    repo_servers.update(json.loads(urllib.request.urlopen('http://ftb.cursecdn.com/edges.json').read().decode('UTF-8')))
    selected_repo_mirror = interactive_selection("Select the nearest server to download feed the beast modpack servers: )", repo_servers)
    # req = urllib.request.urlopen('http://' + selected_repo_mirror[1] + '/FTB2/static/modpacks.xml') # modpacks.xml isn't tha latest on all mirrors!!!!!
    req = urllib.request.urlopen('http://ftb.cursecdn.com/FTB2/static/modpacks.xml') # hard coded mirror
    # req = urllib.request.urlopen('http://' + selected_repo_mirror[1] + '/FTB2/static/modpacks.xml') # modpacks.xml isn't the latest on all mirrors!!!!!A
    modpack_xml_files = { 'FTB Modpacks': 'http://ftb.cursecdn.com/FTB2/static/modpacks.xml', '3rd Party Modpacks':'http://ftb.cursecdn.com/FTB2/static/thirdparty.xml' } # Hardcoded mirror for xml-Files
    xml_url = interactive_selection('Select Modpack Type:', modpack_xml_files)[1]
    req = urllib.request.urlopen(xml_url)
    content = req.read()
    modpacks = xml.etree.ElementTree.fromstring(content.decode('UTF-8'))
    modpack_list = []
    @@ -275,6 +364,25 @@ def ftb_server_install():
    with zipfile.ZipFile(destination_zip_file, 'r') as zipf:
    zipf.extractall(destination_folder)

    # 2 Configure minecraft server startscript
    print('configure ServerStart.sh - \n\tThe default values are calcualted based on your system specification')
    avail_mem = meminfo().get('MemAvailable')[0]//1024
    total_mem = meminfo().get('MemTotal')[0]//1024
    xmx_value = avail_mem-(total_mem//10) # keep 10% of total mem free
    xmx_value = int(ask_user('Java Xmx Size (Maximal Heap Space) in MiB', str(xmx_value), '\d+'))
    xms_value = xmx_value//2
    xms_value = int(ask_user('Java Xms Size (Initial Heap Space) in MiB', str(xms_value), '\d+'))
    thread_count = num_of_processing_units()
    thread_count = ask_user('Number of parallel Garbage Collector threads', str(thread_count), '[1-3]{0,1}[0-9]') # more then 30 CPUs are rare in MC Servers maybe wrong?
    print('update_server_start_sh(' + destination_folder + '/ServerStart.sh, xmx_value=' + str(xmx_value) + ', xms_value=' + str(xms_value) +', thread_count=' + str(thread_count) + ')' )

    update_server_start_sh(destination_folder + '/ServerStart.sh', xmx_value=xmx_value, xms_value=xms_value, thread_count=thread_count)









  2. LordH3lmchen created this gist Apr 25, 2015.
    292 changes: 292 additions & 0 deletions ftb_minecraft_server_launcher.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,292 @@
    #!/usr/bin/env python3
    # encoding: utf-8

    """
    ftb-minecraft-server-launcher
    is a small script that automates the installation of a minecraft server. It's build for Linux noobs to setup a Minecraft Server from the Feed the Beast Launcher.
    What it does
    ============
    Environment Setup
    -----------------
    It checks if the following programs are available:
    * zsh (one of the best Unix shells out there)
    * screen (to run the Minecraft console in background)
    * wget (a commandline tool to download file via http)
    * vim (a text-editor)
    It will show the command to install the missing programs and exits, if something is needed.
    (Optional) Downloads user configurations for zsh, screen and vim (is recommended for noobs)
    http://git.grml.org/f/grml-etc-core/etc/grml/screenrc_generic --> ~/.screenrc
    http://git.grml.org/f/grml-etc-core/etc/vim/vimrc --> ~/.vimrc
    http://git.grml.org/f/grml-etc-core/etc/zsh/zshrc --> ~/.zshrc
    http://git.grml.org/f/grml-etc-core/etc/skel/.zshrc --> ~/.zshrc.local
    creates a ~/.jre directory for java installations
    downloads the latest java to ~/.jre
    extracts java
    creates a symbolic link
    ln -s ~/.jre/jre_XXXX... ~/.jre/current-jre
    adds java to the PATH variable
    Server Setup
    ------------
    mport
    #### Step 1. Repository Server Listen holen
    http://www.creeperrepo.net/edges.json
    http://ftb.cursecdn.com/edges.json
    #### Modpacks.xml von einem der Repo Server laden
    ...../FTB2/static/modpacks.xml
    #### User sucht Mod aus
    #### User sucht Version aus
    #### ServerZip laden.
    """
    import os
    import urllib
    import urllib.request
    import re
    import subprocess
    import json
    import xml.etree.ElementTree
    import zipfile

    """
    Exception wird geworfen wenn eine ungütlige destination angegeben wird.
    """
    class InvalidDestinationException(Exception):
    def __init__(self, destination):
    self.destination = destination



    def create_dir_if_not_exists(path):
    if((os.path.exists(path) and os.path.isdir(path))==false):
    os.mkdir(path)


    def dlfile(req, destination):
    # create a Request-object if given argument is a string(url)
    if isinstance(req, urllib.request.Request) == False:
    req = urllib.request.Request(req)

    if(os.path.exists(destination) and os.path.isdir(destination)):
    destination = destination + '/' + os.path.basename(req.full_url)
    if(os.path.isfile(destination)):
    print(destination + ' already downloaded')
    return destination
    print('Downloading ' + destination)
    with urllib.request.urlopen(req) as f:
    open(destination, "wb").write(f.read())
    return destination



    def regex_websearch(url, pattern):
    resp = urllib.request.urlopen(url)
    content = resp.read().decode('UTF-8')
    resp.close()
    match = re.search(pattern, content)
    return match





    """
    this function downloads the lates Java
    thx to n0ts for the idea
    https://gist.github.com/n0ts/40dd9bd45578556f93e7
    """
    def download_latest_java(destination, java_version=8, package='server-jre', extension='tar.gz', architecture='linux-x64'):



    valid_packages = ['jre', 'server-jre', 'jdk']
    if package not in valid_packages:
    print('Invalid Java package selection, valid packages are:')
    for valid_package in valid_packages:
    print('\t' + valid_package)
    return None



    url = "http://www.oracle.com"

    url_1 = url + "/technetwork/java/javase/downloads/index.html"
    pattern_1 = '\/technetwork\/java/\javase\/downloads\/'+ package + str(java_version) + '-downloads-.+?\.html'
    match = regex_websearch(url_1, pattern_1)
    if match == None:
    print('Unable to download Java from ' + url_1)
    print('Website is down or script is outdated')
    return None

    url_2 = url + match.group(0)
    pattern_2 = "http\:\/\/download.oracle\.com\/otn-pub\/java\/jdk\/[7-9]u[0-9]+?-.+?\/" + package + "-[7-9]u[0-9]+?-" + architecture + "." + extension
    match = regex_websearch(url_2, pattern_2)
    if match == None:
    print('Selected architecture.extension \"' + architecture + '.' + extension + '\" is not available')
    print('Visit \"' + url_2 + '\" to see available architectures and extensions')
    return None


    req = urllib.request.Request(match.group(0))
    req.add_header('Cookie', 'oraclelicense=accept-securebackup-cookie')
    destination = dlfile(req, destination)


    return destination

    def install_latest_java(destination, java_version=8, package='server-jre', extension='tar.gz', architecture='linux-x64'):
    latest_java_tar_gz = download_latest_java(destination, java_version=java_version, package=package, extension=extension, architecture=architecture)
    print('Extracting ' + latest_java_tar_gz)
    tar_output = subprocess.check_output(['tar', 'xzvf', latest_java_tar_gz, '--directory', destination])
    new_java_dir = destination+'/'+tar_output.decode('UTF-8').split('\n')[0]
    print('Creating symlink for latest java')
    current_jre_link = destination + '/current-jre'
    if os.path.exists(current_jre_link):
    os.remove(current_jre_link)
    os.symlink(new_java_dir, current_jre_link)





    def userhome_setup():
    missing_programs = []
    home_dir=os.environ['HOME']
    for program in ["zsh", "screen", "wget", "vim" ]:
    if(os.system("which " + program)!=0):
    missing_programs.append(program)
    if(len(missing_programs)!=0):
    install_command="apt-get install "
    for program in missing_programs:
    install_command = install_command + program + ' '
    print('to install missing programs use \"'+ install_command + '\" and run this script again')
    exit(0)

    print('dowloading configurations for current user')
    #TODO remove xxxTESTxxx in release versio
    dlfile('http://git.grml.org/f/grml-etc-core/etc/grml/screenrc_generic', home_dir + '/.screenrc')
    dlfile('http://git.grml.org/f/grml-etc-core/etc/vim/vimrc', home_dir + '/.vimrcxxxTESTxxx')
    dlfile('http://git.grml.org/f/grml-etc-core/etc/zsh/zshrc', home_dir + '/.zshrc')
    dlfile('http://git.grml.org/f/grml-etc-core/etc/skel/.zshrc', home_dir + '/.zshrc.local')

    if( (os.path.exists(home_dir+"/.jre") and os.path.isdir(home_dir+"/.jre")) == False):
    print("creating .jre directory")
    os.mkdir(home_dir + "/.jre")

    latest_java_tar_gz = install_latest_java(home_dir + '/.jre', java_version=8)

    path_update_shell_script = '\n\nif [ -d "$HOME/.jre/current-jre/bin" ] ; then\n PATH="$HOME/.jre/current-jre/bin:$PATH"\nfi\n'
    profile_file = home_dir+'/.profile'
    if os.path.isfile(profile_file):
    print('Updating ~/.profile to include java in PATH-variable')
    f = open(profile_file, 'a')
    f.write(path_update_shell_script)
    f.close()
    zshrc_local_file = home_dir+'/.zshrc.local'
    if os.path.isfile(zshrc_local_file):
    print('Updating ~/.zshrc.local to include java in PATH-variable')
    f = open(zshrc_local_file, 'a')
    f.write(path_update_shell_script)
    f.close()
    open(home_dir+'/.ftb_minecraft_server_home_setup_finished', 'a').close()




    def interactive_selection(message, data):
    print(message)

    i = 0
    if isinstance(data, dict):
    for key in data.keys():
    print(str(i) + ': \t' + key + ' - ' + data.get(key)) #TODO replace with printf (for better look and feel
    i+=1
    selection = list(data.keys())[int(input("Selection: "))]
    return (selection, data.get(selection))
    if isinstance(data, list):
    for item in data:
    print(str(i) + ': \t' + item)
    i+=1
    index = int(input("Selection: "))
    return (index, data[index])




    def ftb_server_install():

    #1 Fetching Serverlists
    repo_servers = json.loads(urllib.request.urlopen('http://www.creeperrepo.net/edges.json').read().decode('UTF-8'))
    repo_servers.update(json.loads(urllib.request.urlopen('http://ftb.cursecdn.com/edges.json').read().decode('UTF-8')))
    selected_repo_mirror = interactive_selection("Select the nearest server to download feed the beast modpack servers: )", repo_servers)
    # req = urllib.request.urlopen('http://' + selected_repo_mirror[1] + '/FTB2/static/modpacks.xml') # modpacks.xml isn't tha latest on all mirrors!!!!!
    req = urllib.request.urlopen('http://ftb.cursecdn.com/FTB2/static/modpacks.xml') # hard coded mirror
    content = req.read()
    modpacks = xml.etree.ElementTree.fromstring(content.decode('UTF-8'))
    modpack_list = []
    for modpack in modpacks:
    modpack_list.append(modpack.attrib.get('name'))
    selected_mod = interactive_selection("Select the Modpack", modpack_list)[0]
    modpack_versions = modpacks[selected_mod].get('oldVersions').split(';')
    selected_version = interactive_selection("Select the version of the mod:", modpack_versions)[1]
    download_url = 'http://' + selected_repo_mirror[1] + '/FTB2/modpacks/' + modpacks[selected_mod].get('dir') + '/' + selected_version.replace('.','_') + '/' + modpacks[selected_mod].get('serverPack')
    destination_zip_file = os.environ['HOME'] + '/' + modpacks[selected_mod].get('serverPack').replace('.zip', '_' + selected_version.replace('.','_') + '.zip')
    destination_folder = destination_zip_file.replace('.zip','')
    dlfile(download_url, destination_zip_file)
    print('Extracting "' + destination_zip_file + '" to "' + destination_folder + '"')
    with zipfile.ZipFile(destination_zip_file, 'r') as zipf:
    zipf.extractall(destination_folder)





    if __name__ == "__main__":
    if os.name != 'posix' :
    print(os.name + 'is not supported')
    exit(255)

    # Step 1 - setup home directory for minecraft installations.a
    if(os.path.isfile(os.environ['HOME']+'/.ftb_minecraft_server_home_setup_finished')==False):
    userhome_setup()
    ftb_server_install()