#!/bin/bash

#  __  __  _____  _____ _______       _____ _______ 
# |  \/  |/ ____|/ ____|__   __|/\   |  __ \__   __|
# | \  / | |    | (___    | |  /  \  | |__) | | |   
# | |\/| | |     \___ \   | | / /\ \ |  _  /  | |   
# | |  | | |____ ____) |  | |/ ____ \| | \ \  | |   
# |_|  |_|\_____|_____/   |_/_/    \_\_|  \_\ |_|   
# 
# author: Eikooc Sun
# version: 0.1.1
# online: https://mcstart.cookiepower.tech

# Global Infomation
MCSTART_VERSION="0.1.1"
MCSTART_LATEST_UPDATE="2026-7-13"
MCSTART_USER_AGENT="User-Agent: mcstart/${MCSTART_VERSION} (cookie@cookiepower.tech)"
# Output Format
GREEN="\033[36m"
RED="\033[31m"
BLUE="\033[34m"
YELLOW="\033[33m"
RESET="\033[0m"
# Environment
request_method=''
java_version=''
# Argument
interactive='false'
target_type=''
target_version=''
skip_java='false'
force_java='false'
java_type='jdk'
java_provider='adoption'
xms='1G'
xmx='4G'
debug='false'

log(){
	printf "${GREEN}[LOG]${RESET} $@\n"
}

err(){
	printf "${RED}[ERR]${RESET} $@\n"
}

debug(){
	if [ "$debug" = true ]; then
		printf "${BLUE}[DEBUG]${RESET} $@\n"
	fi
}

input(){
	local tip="$1"
	local default="$2"
	local result=''
	printf "${YELLOW}[INPUT]${RESET} " >&2
	read -p "$tip" result < /dev/tty
	echo "${result:-$default}"
}

search_environment_info(){
	if command -v curl >/dev/null 2>&1; then
		request_method="curl"
	elif command -v wget >/dev/null 2>&1; then
		request_method="wget"
	else
		err "curl or wget not found"
		exit
	fi
	debug "Select Web Request Method: ${request_method}"

	if command -v java >/dev/null 2>&1; then
		java_version=$(
			java -XshowSettings:properties --version 2>&1 \
			| grep -E '^[[:space:]]*java\.specification\.version[[:space:]]*=' \
			| sed -E 's/.*=[[:space:]]*//'
		)
	fi
	if [[ $java_version == 1.* ]]; then
		java_version=${java_version#1.}
	fi
	debug "Java version: ${java_version:-Not Found}"
}

request(){
	local url="$1"

	if [ "${request_method}" = "curl" ]; then
		command curl \
                	-A "${MCSTART_USER_AGENT}" \
                        -L \
                        --connect-timeout 10 \
			$([ "$debug" = "false" ] && echo -s) \
                        "${url}"
	elif [ "${request_method}" = "wget" ]; then
		command wget \
                	-U "${MCSTART_USER_AGENT}" \
                        -T 10 \
                        $([ "$debug" = "false" ] && echo -q) \
                        -O - \
                        "${url}"
	else
		err "Invalid status: Unrecognized status '${request_method}'"
	fi
}

download(){
	local url="$1"
        local filename="$2"

        if [ "${request_method}" = "curl" ]; then
        	command curl \
                	-A "${MCSTART_USER_AGENT}" \
                        -L \
                        --connect-timeout 10 \
			$([ "$debug" = "false" ] && echo -s) \
                        -o "${filename}"\
                        "${url}"
        elif [ "${request_method}" = "wget" ]; then
        	command wget \
                	-U "${MCSTART_USER_AGENT}" \
                        -T 10 \
                        $([ "$debug" = "false" ] && echo -q) \
                        -O "${filename}" \
                        "${url}"
        else
                err "Invalid status: Unrecognized status '${request_method}'"
        fi
}

parse_args(){
	while [ $# -gt 0 ]; do
		case "$1" in
			-Xms)
				xms=$2
				shift 2
				;;
			-Xmx)
				xmx=$2
				shift 2
				;;
			-java-type)
				java_type=$2
				shift 2
				;;
			--force-java)
				force_java="true"
				shift
				;;
			--skip-java)
				skip_java="true"
				shift
				;;
			--debug)
				debug="true"
				shift
				;;
	 		--version)
				log "MCStart v${MCSTART_VERSION} ${MCSTART_LATEST_UPDATE}";
				exit
				;;
			*)
				if [ -z "${target_type}"]; then
					target_type=$1
					shift
				elif [ -z "${target_version}"]; then
					target_version=$1
					shift
				else
					err "Unknow Argument: $1"
					exit
				fi
				;;
		esac
	done

	if [[ -z "$target_type" || -z "$target_version" ]]; then
                interactive="true"
                debug "No required parameters found; interactive scripting enabled"
                return 0
        fi
}

install_server_vanilla(){
	local version="$1"

	log "Preparing to download"
	local version_json_url=$(
		request https://piston-meta.mojang.com/mc/game/version_manifest_v2.json \
		| sed -En 's/.*"id": "'"$version"'", "type": "[a-z]+", "url": "([^"]+)".*/\1/p'
		)
	local download_url=$(
		request $version_json_url \
		| grep -o 'https\?://[^"]*server\.jar'
		)
	
	local filename="server-$version.jar"
	log "Start download $filename"
	download $download_url $filename
	
	log "Configure the startup script"
	cat > run.sh <<-EOF
	#!/bin/bash

	java -Xms$xms -Xmx$xmx -XX:+UseCompactObjectHeaders -XX:+AlwaysPreTouch -XX:+UseStringDeduplication -jar $filename nogui

	EOF
	log "Complete"
}

install_server_paper(){
	local version="$1"

	log "Preparing to download"
	local download_url=$(
		request https://fill.papermc.io/v3/projects/paper/versions/$version/builds \
		| grep -o 'https\?://[^"]*\.jar' \
		| head -n1
		)
	local filename="${download_url##*/}"
	log "Start download $filename"
	download $download_url $filename
	
	log "Configure the startup script"
	cat > run.sh <<-EOF
	#!/bin/bash

	java -Xms$xms -Xmx$xmx -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+ParallelRefProcEnabled -XX:+PerfDisableSharedMem -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1HeapRegionSize=8M -XX:G1HeapWastePercent=5 -XX:G1MaxNewSizePercent=40 -XX:G1MixedGCCountTarget=4 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1NewSizePercent=30 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:G1ReservePercent=20 -XX:InitiatingHeapOccupancyPercent=15 -XX:MaxGCPauseMillis=200 -XX:MaxTenuringThreshold=1 -XX:SurvivorRatio=32 -jar $filename nogui

	EOF

	log "Complete"
}

install_server_folia(){
        local version="$1"
        
	log "Preparing to download"
	local download_url=$(
                request https://fill.papermc.io/v3/projects/folia/versions/$version/builds \
                | grep -o 'https\?://[^"]*\.jar' \
                | head -n1
		)
        local filename="${download_url##*/}"
        log "Start download $filename"
	download $download_url $filename

	log "Configure the startup script"
        cat > run.sh <<-EOF
        #!/bin/bash

        java -Xms$xms -Xmx$xmx -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+ParallelRefProcEnabled -XX:+PerfDisableSharedMem -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1HeapRegionSize=8M -XX:G1HeapWastePercent=5 -XX:G1MaxNewSizePercent=40 -XX:G1MixedGCCountTarget=4 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1NewSizePercent=30 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:G1ReservePercent=20 -XX:InitiatingHeapOccupancyPercent=15 -XX:MaxGCPauseMillis=200 -XX:MaxTenuringThreshold=1 -XX:SurvivorRatio=32 -jar $filename nogui

	EOF

	log "Complete"
}

install_server_forge(){
	local version="$1"

	log "Preparing to download"
	local version_build=$(
		request https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json \
		| grep -o '"'"$version"'-latest": "[^"]*"' \
		| cut -d'"' -f4
		)
	local download_url="https://maven.minecraftforge.net/net/minecraftforge/forge/$version-$version_build/forge-$version-$version_build-installer.jar"
	local filename=${download_url##*/}
	log "Start download $filename"
	download $download_url $filename
	
	log "Start install server"
	java -jar $filename --installServer

	log "Complete"
}

install_server_fabric(){
	local version="$1"

	log "Preparing to download"
	local loader_version=$(
		request https://meta.fabricmc.net/v2/versions/loader/$version \
		| grep -o '"version": "[^"]*"' \
		| head -n1 \
		| cut -d'"' -f4
	)
	local installer_version=$(
		request https://meta.fabricmc.net/v2/versions/installer \
		| grep -o '"version": "[^"]*"' \
		| head -n1 \
		| cut -d'"' -f4
	)
	local filename="fabric-$version-$loader_version-$installer_version-launcher.jar"
	log "Start download $filename"
	download "https://meta.fabricmc.net/v2/versions/loader/$version/$loader_version/$installer_version/server/jar" $filename

	log "Configure the startup script"
	cat > run.sh <<-EOF
	#!/bin/bash

	java -Xms$xms -Xmx$xmx -jar $filename nogui

	EOF

	log "Complete"
}

install_server_neoforge(){
	local version="$1"
	
	log "Preparing to download"
	if [[ "$version" == “1.20.1” ]];then
		local loader_tag="forge"
		local loader_version=$(
			request https://maven.neoforged.net/releases/net/neoforged/forge/maven-metadata.xml \
			| grep -oP "<version>\K${version//./\\.}[^<]+" \
			| tail -n1
		)
	else
		local loader_tag="neoforge"
		local version_tail="${version#*.}"
		local loader_version=$(
			request https://maven.neoforged.net/releases/net/neoforged/neoforge/maven-metadata.xml \
			| grep -oP "<version>\K${version//./\\.}[^<]+" \
			| tail -n1
		)
	fi

	local filename="neoforge-$version-$loader_version-install.jar"
	log "Start download $filename"
	download "https://maven.neoforged.net/releases/net/neoforged/$loader_tag/$loader_version/$loader_tag-$loader_version-installer.jar" $filename
	
	log "Start install server"
	java -jar $filename --installServer

	log "Complete"
}

install_java_zulu(){
	local version="$1"
	local type="$2"
	
	log "Preparing to download Java"
	local download_url=$(
		request "https://api.azul.com/metadata/v1/zulu/packages/?java_version=$version&os=linux&arch=x64&archive_type=tar.gz&java_package_type=$type&latest=true&certifications=tck&certifications=aqavit&page_size=1" \
		| grep -oP '"download_url"\s*:\s*"\Khttps?://[^"]+'
	)
	local filename="$type-$version-zulu.tar.gz"
	log "Download Java: $filename"
	download $download_url $filename
	
	log "Install Java"
	mkdir -p $type
	tar -xzf $filename -C $type --strip-components=1
	rm $filename
	log "Java installation complete"
}

install_java_adoptium(){
	local version="$1"
	local type="$2"
	
	log "Preparing to download Java"
	local download_url="https://api.adoptium.net/v3/binary/latest/$version/ga/linux/x64/$type/hotspot/normal/eclipse"
	local filename="$type-$version-adoptium.tar.gz"
	log "Download Java: $filename"
	download $download_url $filename
	
	log "Install Java"
	mkdir -p $type
	tar -xzf $filename -C $type --strip-components=1
	rm $filename
	log "Java installation complete"
}

read_args(){
	target_type=$(input "Please enter the target server type (default: vanilla): " "vanilla")
	target_version=$(input "Please enter the target server version (default: 26.2): " "26.2")
	skip_java=$(input "Skip the standalone Java installation, even if the Java version doesn't match or isn't installed (default: false): " "false")
	if [ "$skip_java" != "true" ]; then 
		force_java=$(input "Force installation of a standalone Java, even if the Java version matches (default: false): " "false")
		java_type=$(input "Selecting a Java Runtime Type (default: jdk, optional: jre): " "jdk")
		java_provider=$(input "Select a Java Distributor (default:adoptium, optional: zulu): " "adoptium")
	fi
}

install_server(){
	log "Start install $target_type $target_version"
	case "$target_type" in
        	vanilla)
                	install_server_vanilla $target_version
                	;;
       		paper)
                	install_server_paper $target_version
                	;;
        	folia)
                	install_server_folia $target_version
                	;;
        	forge)
                	install_server_forge $target_version
                	;;
        	fabric)
                	install_server_fabric $target_version
                	;;
        	neoforge)
                	install_server_neoforge $target_version
                	;;
        	*)
                	err "Unknow Type $1"
			exit 1
			;;
	esac
}

install_java(){
	local supported_java_version=''
	case "$target_version" in
		1.[0-9]|1.1[0-6].*) 
			supported_java_version=8
			;;
		1.17.*|1.18.*|1.19.*|1.20.[0-4]*)
			supported_java_version=17
			;;
		1.20.[5-9]*|1.21.*)
			supported_java_version=21
			;;
		26.*)
			supported_java_version=25
			;;
		*)
			log "Unrecorded version $target_version; treated as a higher version"
			;;
	esac
	
	log "Recommended Java version: $supported_java_version"
	log "Installed Java versions: ${java_version:-Not Found}"
	if [ -n "$java_version" ]; then
		if [ $java_version -gt $supported_java_version ]; then
			log "The current Java version is too high."
		elif [ $java_version -eq $supported_java_version ]; then
			log "The current Java version is appropriate."
		else
			log "The current Java version is too low."
		fi
	fi
	
	if [ "$java_version" == "$supported_java_version" ]; then
		log "Do nothing"
	elif [ "$skip_java" == "true" ]; then
		log "Skip Java installation"
		return 0
	else	
		case "$java_provider" in
			adoptium)
				install_java_adoptium $supported_java_version $java_type
				;;
			zulu)
				install_java_zulu $supported_java_version $java_type
				;;
			*)
				err "Unknow Java provider: $java_provider"
				exit 1
				;;
		esac
		log "Modify the Java path in run.sh"
		sed -i 's/\bjava\b/\.\/'"$java_type"'\/bin\/java/g' run.sh
	fi
}

main(){
	parse_args $@
	search_environment_info
	if [ $interactive = "true" ]; then
		read_args
	fi
	install_server
	install_java
}

main "$@"

