Skip to content

Instantly share code, notes, and snippets.

@jogerj
Last active November 7, 2021 17:19
Show Gist options
  • Save jogerj/2a1d949787f276438299704d70ced17c to your computer and use it in GitHub Desktop.
Save jogerj/2a1d949787f276438299704d70ced17c to your computer and use it in GitHub Desktop.
Minecraft start.sh script with screen + updater script
#!/bin/sh
MC="paper.jar"
MC_UPDATE="paper_update.jar"
# Replace if update exists
if test -f "$MC_UPDATE"; then
echo "Updating $MC..."
mv $MC $MC.old
mv $MC_UPDATE $MC
else
echo "No JAR updates found."
fi
# SAFE MODE
# screen -dmS minecraft java -Xms2G -Xmx2G -jar $MC --nogui
# Default optimized
# screen -dmS minecraft java -Xms10G -Xmx10G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true -jar $MC --nogui
# Optimized (personalized)
screen -dmS minecraft java -Xms16G -Xmx16G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=40 -XX:G1MaxNewSizePercent=50 -XX:G1HeapRegionSize=16M -XX:G1ReservePercent=15 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=20 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true -jar $MC --nogui
#!/usr/bin/env python3
### VARIABLES
# uncomment/comment which one you use
PROJECT = "paper"
# PROJECT = "waterfall"
VERSION = "1.17.1"
FILENAME = "paper_update.jar" # DO NOT OVERWRITE 'paper.jar' DIRECTLY
# FILENAME = "waterfall_update.jar" # DO NOT OVERWRITE 'waterfall.jar' DIRECTLY
### DO NOT CHANGE BELOW ###
from urllib import request
from urllib.error import HTTPError, URLError, ContentTooShortError
import json
import logging
from socket import timeout
import shutil
from hashlib import sha256
def json_to_dict(response):
return json.loads(response.read().decode('utf-8'))
def get_versions(project):
url = f"https://papermc.io/api/v2/projects/{project}"
try:
logging.info("Fetching versions list...")
with request.urlopen(url, timeout=30) as response:
data = json_to_dict(response)
versions = data['versions']
return versions
except (HTTPError, URLError) as e:
logging.exception(e)
logging.error("Could not fetch versions list! Check your connection!")
return None
def get_latest_build(project, version):
url = f"https://papermc.io/api/v2/projects/{project}/versions/{version}"
logging.info(f"Selected version: {project.capitalize()} {version}")
build = None
try:
logging.info("Fetching latest build number...")
with request.urlopen(url, timeout=10) as response:
data = json_to_dict(response)
build = data['builds'][-1]
except timeout:
logging.error(f"Connection timed out! Check your internet connection and URL! ({url})")
except HTTPError as e:
logging.exception(e)
if (e.code == 500):
logging.error("Connection timed out! Update server might be down?")
logging.error(f"URL: {url}")
if (e.code == 404):
logging.error("Invalid version number!")
versions = get_versions(project)
if versions is not None:
versions.reverse()
logging.info(f"Available versions: {versions}")
logging.info("Please change the selected version in this file's VERSION variable and run the script again!")
finally:
return build
def get_jar(project, version, build, filename=FILENAME):
logging.info(f"Latest build: {project}-{version}-{build}")
url = f"https://papermc.io/api/v2/projects/{project}/versions/{version}/builds/{build}/downloads/{project}-{version}-{build}.jar"
try:
with open(filename, 'wb') as out_file:
logging.info(f"Fetching latest {project}.jar...")
with request.urlopen(url, timeout=10) as response:
shutil.copyfileobj(response, out_file)
logging.info(f"SUCCESS! Latest build saved as {filename}")
return True
except PermissionError as e:
logging.exception(e)
logging.error("Insufficient write permissions! Could not write file to system!")
return False
except timeout:
logging.error(f"Connection timed out! Check your internet connection and URL! ({url})")
return False
except ContentTooShortError as e:
logging.exception(e)
logging.error("Download was interrupted!")
return False
except (HTTPError, URLError) as e:
logging.exception(e)
logging.error("Could not download latest build! Try downloading from the following URL manually:")
logging.error(f"URL: {url}")
return False
def calculate_sha256(filename):
with open(filename, "rb") as f:
sha256_hash = sha256()
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
def get_sha256(project, version, build):
logging.info(f"Fetching hash for {project}-{version}-{build}.jar")
url = f"https://papermc.io/api/v2/projects/{project}/versions/{version}/builds/{build}"
verify_hash = None
try:
with request.urlopen(url, timeout=10) as response:
data = json_to_dict(response)
verify_hash = data['downloads']['application']['sha256']
except timeout:
logging.warning(f"Connection timed out! Could not get SHA256 hash from server!")
except (HTTPError, URLError, ContentTooShortError) as e:
logging.exception(e)
logging.warning("Could not get SHA256 hash from server!")
finally:
return verify_hash
def verify_sha256(project, version, build, filename=FILENAME):
logging.info("Calculating hash...")
check_hash = calculate_sha256(filename)
logging.info(f"SHA256: {check_hash}")
verify_hash = get_sha256(project, version, build)
logging.debug(f"Verification hash: {verify_hash}")
if verify_hash is None:
logging.warning("SHA256 verification failed!")
return False
elif check_hash == verify_hash:
logging.info("SHA256 verification success!")
return True
else:
logging.warning(f"SHA256 verification failed! Expected {verify_hash}")
return False
def main():
logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s")
build = get_latest_build(PROJECT, VERSION)
if build is not None:
if get_jar(PROJECT, VERSION, build):
verify_sha256(PROJECT, VERSION, build)
if __name__ == "__main__":
main()

Setup

Make sure screen is installed (e.g. on Ubuntu sudo apt-get install screen, see guide for others)

  1. screen -S "minecraft"
  2. screen -ls to list all screens
  3. Change MC and MC_UPDATE variables when appropriate (paper.jar, server.jar, spigot.jar, etc.)
  4. (Optional) if you want to update your server client, just save as filename determined by MC_UPDATE variable and call restart in console; or stop the server and then run ./start.sh

Ctrl+A then D to detach from screen

screen -x or screen -r <screen_id> to bring back console

Tuning java args (Advanced)

Consult guide. Example above is my setup for a 4 vCPU 24GB RAM VPS

Updater

This python script automatically fetches the latest build from papermc.io.

Put in same directory as minecraft and run python3 updater.py. Use in conjunction with start.sh. DO NOT AUTOMATE THE UPDATER. Always call updater manually in case update failed.

@jogerj
Copy link
Author

jogerj commented Nov 6, 2021

in case you get timeout errors often, change timeout=10 to a greater value like timeout=30

@jogerj
Copy link
Author

jogerj commented Nov 7, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment