#!/bin/sh set -e EXEC_NAME="$(basename -- "$0")" USAGE="Usage: $EXEC_NAME [OPTION]... [NAME|JAR|DIR] [INPUT]..." VERSION="mca 1.0.0" OPTION="$(getopt -n "$EXEC_NAME" -s sh -o hvfdxrsc -l help -l version -- "$@")" eval set -- "$OPTION" SCREEN_OPTS= REATTACH_ONLY=0 SKIP_CONFIGURE=0 CONFIGURE_ONLY=0 while :; do case "$1" in -h|--help) echo "$USAGE" exit ;; -v|--version) echo "$VERSION" exit ;; -f) SCREEN_OPTS=Dm ;; -d) SCREEN_OPTS=d ;; -x) SCREEN_OPTS=x ;; -r) REATTACH_ONLY=1 ;; -s) SKIP_CONFIGURE=1 ;; -c) CONFIGURE_ONLY=1 ;; --) shift break esac shift done if [ "$SKIP_CONFIGURE" -eq 1 ] && [ "$CONFIGURE_ONLY" -eq 1 ]; then echo "$EXEC_NAME: can't skip configuration and only configure at \ the same time" >&2 exit 2 fi MCA_NAME=$1 if [ -z "$MCA_NAME" ]; then MCA_DIR="$PWD" MCA_NAME="$(basename -- "$MCA_DIR")" elif echo "$MCA_NAME" | grep -qE '^\.{,2}/' \ && MCA_NAME="$(readlink -f -- "$MCA_NAME")"; then if [ -d "$MCA_NAME" ]; then MCA_DIR="$MCA_NAME" MCA_NAME="$(basename -- "$MCA_DIR")" else MCA_DIR="$(dirname -- "$MCA_NAME")" MCA_JAR="$MCA_NAME" MCA_NAME="$(basename -- "$MCA_DIR")" fi else MCA_DIR="$HOME/mca/$MCA_NAME" fi if [ "$CONFIGURE_ONLY" -eq 0 ]; then if ! command -v screen > /dev/null; then echo "$EXEC_NAME: screen not found" >&2 exit 2 fi SCREEN_NAME="$(echo "$MCA_NAME" | tr / -)" if [ -n "$2" ]; then shift INPUT="$(echo "$*" | sed 's/"/\\\0/g')" if screen -S "$SCREEN_NAME" \ -X eval "stuff \"$INPUT\"\\015" > /dev/null; then exit 0 else echo "$EXEC_NAME: $SCREEN_NAME: no such screen found" >&2 exit 2 fi fi SCREEN_RC_FILE= for i in "$HOME/.config" /etc; do j="$i/mca/screenrc" if [ -r "$j" ]; then SCREEN_RC_FILE=$j break fi done if [ -z "$SCREEN_RC_FILE" ]; then echo "$EXEC_NAME: can't find screenrc" >&2 exit 2 fi if screen -c "$SCREEN_RC_FILE" "-$SCREEN_OPTS"rS \ "$SCREEN_NAME" > /dev/null 2>&1; then exit 0 elif screen -S "$SCREEN_NAME" -X select . > /dev/null 2>&1; then echo "$EXEC_NAME: this screen has already been attached to someone" >&2 exit 2 fi if [ "$REATTACH_ONLY" -eq 1 ]; then echo "$EXEC_NAME: $SCREEN_NAME: no such screen found" >&2 exit 2 fi if [ ! -d "$MCA_DIR" ]; then echo "$EXEC_NAME: $MCA_DIR: such directory not found" >&2 exit 2 fi fi cd "$MCA_DIR" MCA_RC_FILE="$MCA_DIR/.mcarc" if [ -e "$MCA_RC_FILE" ]; then # don't use `-p` in `read` for line breaks to avoid long lines. # shellcheck disable=SC2162 while IFS= read line; do # skip lines starting with '#' case "$line" in ''|\#*) continue ;; esac # to get `$1` and `$2` as key and value. # shellcheck disable=SC2086 set $line if echo "$1" | grep -i '[^a-zA-Z0-9_]'; then echo "$EXEC_NAME: $1: option key contains invalid characters" >&2 else KEY="MCA_$(echo "$1" | tr '[:lower:]' '[:upper:]')" if [ -n "$2" ]; then shift VALUE=$* fi export "$KEY"="$VALUE" fi done < "$MCA_RC_FILE" MCA_RC_FILE_READ=1 else MCA_RC_FILE_READ=0 fi if [ "$CONFIGURE_ONLY" -eq 0 ]; then if [ -z "$MCA_PLATFORM" ]; then if [ -z "$MCA_JAR" ]; then MCA_JAR="$(find "$MCA_DIR" -maxdepth 1 -type f -name \*.jar \ | head -1)" [ -z "$MCA_JAR" ] && { echo "$EXEC_NAME: no jar found in $MCA_DIR" >&2 exit 2 } fi if [ -e "$MCA_JAR" ]; then MCA_PLATFORM="$(basename -- "$MCA_JAR")" MCA_PLATFORM="$(echo "$MCA_PLATFORM" \ | tr '[:upper:]' '[:lower:]' | grep -oE '[a-z]+' | head -1)" else echo "$EXEC_NAME: $MCA_JAR: no such file" >&2 exit 2 fi else [ -z "$MCA_JAR" ] && MCA_JAR="$MCA_DIR/$MCA_PLATFORM.jar" if [ ! -e "$MCA_JAR" ]; then echo "$EXEC_NAME: $MCA_JAR: no such file" >&2 exit 2 fi fi [ -n "$MCA_JVM_FLAGS" ] && MCA_JVM_FLAGS="$MCA_JVM_FLAGS " MCA_JVM_FLAGS="$MCA_JVM_FLAGS-Dfile.encoding=UTF-8 \ -Djava.awt.headless=true" case "$MCA_PLATFORM" in bungee) ;; waterfall|velocity) [ "$MCA_PLATFORM" = "bungee" ] || MCA_JVM_FLAGS="$MCA_JVM_FLAGS \ -XX:+UseG1GC \ -XX:G1HeapRegionSize=4M \ -XX:+UnlockExperimentalVMOptions \ -XX:+ParallelRefProcEnabled \ -XX:+AlwaysPreTouch \ -DIReallyKnowWhatIAmDoingISwear=true" ;; minecraft|bukkit|craftbukkit|spigot|paper) MCA_JVM_FLAGS="$MCA_JVM_FLAGS -Dcom.mojang.eula.agree=true \ -DIReallyKnowWhatIAmDoingISwear=true" [ "$MCA_PLATFORM" = "paper" ] && MCA_JVM_FLAGS="$MCA_JVM_FLAGS \ -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" ;; custom) ;; *) echo "$EXEC_NAME: invalid platform: $MCA_PLATFORM. Valid: bungee, \ waterfall, velocity, minecraft, bukkit, craftbukkit, spigot, \ paper, custom" >&2 exit 2 ;; esac if [ -n "$MCA_OPT_JAVA_HOME" ] && [ -d "$MCA_OPT_JAVA_HOME/bin" ]; then PATH="$MCA_OPT_JAVA_HOME/bin:$PATH" fi if ! command -v java > /dev/null; then echo "$EXEC_NAME: unable to get Java version (is Java installed?)" >&2 exit 2 fi [ -n "$MCA_OPT_MAX_MEMORY" ] \ && MCA_JVM_FLAGS="-Xmx$MCA_OPT_MAX_MEMORY $MCA_JVM_FLAGS" [ -n "$MCA_OPT_MIN_MEMORY" ] \ && MCA_JVM_FLAGS="-Xms$MCA_OPT_MIN_MEMORY $MCA_JVM_FLAGS" SCREEN_CMD="$(which java) -server $MCA_JVM_FLAGS -jar $MCA_JAR" fi if [ "$SKIP_CONFIGURE" -eq 0 ] && [ "$MCA_RC_FILE_READ" -eq 1 ]; then ENV_LIST="$(env | grep ^MCA_ | cut -d= -f1 | sed 's/^/$/g')" FILE_LIST="$(find . -name '*.in' -type f)" [ -z "$FILE_LIST" ] || echo "$FILE_LIST" | while read -r line; do envsubst "$ENV_LIST" < "$line" > "${line%*.in}" done fi if [ "$CONFIGURE_ONLY" -eq 0 ]; then # papermc prints uncolored text if the COLORTERM variable is set to # `truecolor`. export COLORTERM= # we don't quote $SCREEN_CMD because screen treats arguments after the # second one as a command. # shellcheck disable=SC2086 exec screen -c "$SCREEN_RC_FILE" "-$SCREEN_OPTS"S "$SCREEN_NAME" \ -- $SCREEN_CMD fi