Poor Start Script for Java 8 in FTB Infinity 1.4.0

  • The FTB Forum is now read-only, and is here as an archive. To participate in our community discussions, please join our Discord! https://ftb.team/discord

hungrig3rHug0

New Member
Jul 29, 2019
2
0
0
I had some Problems with the latest Version of FTB Infinty. Im a (modded) minecraft noob. But i have a lot of exprience with Servers.

So first the System is a Xeon with 4 Cores and 16GBs of RAM and 4TB of Space. I use Ubuntu 15.04 Beta as Host System. Set up qemu, Networking stuff ... bridges, vlans ... and the Minecraft Server System (Virtual) 4Cores (KVM enabled... Host CPU passed through) everything set up with vertio (really really fast). Benchmarked the CPU and the virtual System reaches 99.8% of the Performance of the native OS on the Host.

I use Java Server JRE 8u45. With the default Settings enabled the Server lags like crazy and java has some issues with the comandline arguments

Code:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=256m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release

I usually create a user for gameservers and set everything up in the home directory (java binaries, minecraft everything in the home no admin or root access required)

I've enabled the GarbageCollector Output after some time of playing the GC Runs about 2.2 seconds with the default settings. And the Server keeps telling "Can't keep up ... "

Why is the Server ZIP file shipped with that java-options?
Code:
java -server -Xms512m -Xmx2048M -XX:PermSize=256m -d64 -XX:+UseParNewGC -XX:+CMSIncrementalPacing -XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads=2 -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10 -jar FTBServer-1.7.10-1352.jar nogui


With changed Settings (using thedefault GarbageCollector) everything works fine.

Code:
java -server -Xms7G -Xmx7G -d64 -XX:+CMSIncrementalPacing -XX:+CMSClassUnloadingEnabled -XX:ParallelGCThreads=4 -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10 -XX:+PrintGCDetails -jar FTBServer-1.7.10-1352.jar nogui


Looks like Java Server JRE 8 uses PSYoungGen and runs a less frequent and faster then the NewParGC. I haven't tested the System for a long time but so far no problem.

Any suggestions ? To run the Server with Java 8? Pls change the Start Script for the noobs! Maybe some question answer stuff to configure the Paramerters for java (Memory ... CPU ... ). Maybe i write one and post it here.

happy mining, Hugo
 

hungrig3rHug0

New Member
Jul 29, 2019
2
0
0
OK i have started to develop a server setup and configuration script. Basically its the same as the FTB Launcher written in python (CLI only). Im not ready yet but it works so far on Ubuntu 15.04 with python3

It downloads the latest Java Server JRE, extracts it to ~/.jre/jdk1.X.0_XX and creates a symbolic link to ~/.jre/current-jre

Some basic configuration files are fetched from grml.org (for screen and zsh)

After that ~/.jre/current-jre/bin is added to the PATH-variable.

When the Basic Setup is down the Script acts as a FTB Launcher (for Server only).

The user selects the download mirror, the Modpack (only official FTB Mods supported now) and the version, the zip file for the server is downloaded and extracted. I try to implement as much as possible in pure python to avoid dependencies with external programs. (unzip for example)

Parsing the System specs and generate the optimized server start scripts isn't implemented yet. Another plan is to implement a Java update function, to keep the Server up to date.

Thats a first prototype. Is there anything like my script out there yet?





Code:
#!/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()


This script on github
https://gist.github.com/LordH3lmchen/9e2cf53114acd27abe03


Execution Example
Code:
minecraft@development-minecraft-server ~ % ./ftb_minecraft_server_launcher.py
Select the nearest server to download feed the beast modpack servers: )
0:      Maidenhead   -   england1.creeperrepo.net
1:      CurseCDN   -   ftb.cursecdn.com
2:      Nottingham   -   england2.creeperrepo.net
3:      Los Angeles   -   losangeles1.creeperrepo.net
4:      Auckland   -   auckland1.creeperrepo.net
5:      Grantham   -   england3.creeperrepo.net
6:      Atlanta-2   -   atlanta2.creeperrepo.net
7:      Atlanta   -   atlanta1.creeperrepo.net
8:      Denver   -   denver1.creeperrepo.net
Selection: 2
Select the Modpack
0:      FTB Infinity 1.7
1:      FTB Trident
2:      Direwolf20
3:      FTB Lite 3
4:      FTB Mage Quest
5:      FTB Resurrection
6:      Monster
7:      Direwolf20
8:      Horizons
9:      Tech World 2
10:     Magic World 2
11:     FTBLite2
12:     FTB Unleashed
13:     FTB Ultimate
14:     Unstable 1.7.x (Public Beta Test Pack)
15:     FTB Lite
16:     Direwolf20 1.5 v2
17:     Direwolf20 Pack
18:     MindCrack Pack
19:     YogCraft Modpack
20:     FTB Unhinged
21:     Magic World
22:     Tech World
23:     Pax South 2015 Map
24:     Pax Prime 2014 Map
25:     Pax East 2014 Map
26:     Pax Challenge Pack 2013
27:     Feed The Beast Retro SSP
28:     Feed The Beast Retro SMP
29:     Slow's Stream Pack
30:     Feed The Beast Beta Pack A
Selection: 0
Select the version of the mod:
0:      1.4.1
1:      1.4.0
2:      1.3.4
3:      1.3.3
4:      1.3.2
5:      1.3.1
6:      1.3.0
7:      1.2.1
8:      1.2.0
9:      1.1.0
10:     1.0.2
11:     1.0.1
12:     1.0.0
Selection: 0
/home/minecraft/FTBInfinityServer_1_4_1.zip already downloaded
Extracting "/home/minecraft/FTBInfinityServer_1_4_1.zip" to "/home/minecraft/FTBInfinityServer_1_4_1"
 

DZCreeper

New Member
Jul 29, 2019
1,469
0
1
Two minor nitpicks I have:

-d64 is only used to force usage of 64 bit JVM. Useless because nearly all FTB packs need 64 bit Java to have large enough heap space anyways and there is no 32 bit mode in 64 bit Java. For the same reason, -server is useless.

tmux is a better alternative to screen.

The following aren't complaints but merely personal observations.

Java 8 Update 45 from Oracle is the fastest version available.

The best garbage collector is the Garbage first collector aka G1.
Code:
–XX:+UseG1GC -XX:+UseStringDeduplicationJVM

FTB Infinity needs its own startup script, its more intensive than other modpacks. 2GB max heap space is not enough and neither is 2 GC threads.