Created
November 11, 2014 18:13
-
-
Save maruohon/49ef39c3992316202553 to your computer and use it in GitHub Desktop.
A bash script to generate huge Minecraft worlds in several smaller parts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Author: masa | |
# Date: 2014-11-11 | |
# | |
# A script for automatically creating (from a template), starting, stopping and running | |
# a command on multiple Minecraft server instances. | |
# The goal is to quickly generate a huge Minecraft world in a multiprocessor environment. | |
# The script is currently built around running 16 separate instances at once, | |
# and the world it will generate is 50k x 50k blocks (about, a little more since it is 100 x 100 region files). | |
# The script assumes the Admin Command Toolbox mod by ProfMobius has been installed (requires a Forge based server). | |
# | |
# To use this script, you want to configure the TEMPLATE_DIR to point to a server template, which the script will | |
# then duplicate to 16 different directories. If/when you want a consistent world (no biome borders/inconsistencies), | |
# then be sure to have a world/ dir with a level.dat for the seed in the template dir, OR have the seed set | |
# in the server.properties file. | |
# | |
# Also set the DST_DIR to point to the root directory under which the separate server instances will be created. | |
# The COMBINED_WORLD_DIR will have the final, combined set of region files after you run the move_regions command. | |
# WORLD_DIR is used when moving the regions files to the final directory, it should match the one set in | |
# server.properties, which is just 'world' by default. | |
# Be sure to have the server jar named as 'minecraft_server.jar' inside the template dir, or adjust the SERVICE variable. | |
# It must contain the name of the server jar, WITHOUT the '.jar' extension. | |
# | |
# Set the BASE_PORT to the port for the first instance. The script will automatically increase it by one | |
# for each server instance and write it to the instance's server.properties. | |
# Set the USERNAME to the username you want to run server processes as. | |
# | |
# The available commands are: | |
# create_all - Creates all the server instances by copying the template directory, and modifying the port in server.properties. | |
# start_all - Starts all the server instances in screen sessions named as 'mc_server_01' etc. | |
# stop_all - Stops all the running server instances. | |
# pregen_all - Runs the pregen command from Admin Command Toolbox mod by ProfMobius. Each server instance will generate a separate 12.5k x 12.5k region. | |
# move_regions - Moves the right region files from each server instance to the final location. You should only run this when the servers have been shut down. | |
# | |
# If you want to generate a different size world, or use a different number of processes, then a decent amount of | |
# changes are needed. The number of instances can be changed easily in the 'Process the commands' section on the bottom. | |
# You must however modify the pregen commands according to your desired world size, and also change the calculations | |
# in the mc_move_regions function accordingly. | |
# | |
# Some tips for those: | |
# The pregen command format is: /pregen <dimension> <startX> <endX> <startZ> <endZ> | |
# The coordinates for pregen are CHUNK coordinates. | |
# A chunk is 16x16 blocks. A region file is 512x512 blocks, which is 32x32 chunks. | |
# Do the math based on those ;) | |
TEMPLATE_DIR="/tmp/tmpl" | |
DST_DIR="/tmp/servers" | |
COMBINED_WORLD_DIR="/tmp/final_world" | |
WORLD_DIR="world" # The name of the world directory | |
SERVICE="minecraft_server" # Base name of the server jar (in templates, this would be minecraft_server.jar, in the instances it will become minecraft_server_12.jar etc.) | |
BASE_PORT="25565" | |
USERNAME="masa" | |
SCREEN_SESSION="mc_server" | |
INVOCATION="java -Xms2G -Xmx3G -XX:PermSize=256m -Xincgc -XX:+UseConcMarkSweepGC -jar" | |
OPTIONS="nogui" | |
export LANG=en_US.UTF-8 | |
export LC_ALL=en_US.UTF-8 | |
as_user() { | |
ME=`whoami` | |
if [ ${ME} == ${USERNAME} ] ; then | |
bash -c "${1}" | |
else | |
su - ${USERNAME} -c "${1}" | |
fi | |
} | |
mc_create_instances() { | |
PORT=${BASE_PORT} | |
LAST="${1}" | |
for i in `seq 1 ${LAST}`; | |
do | |
NUM=`printf %02d ${i}` | |
cp -auxi "${TEMPLATE_DIR}" "${DST_DIR}/server_${NUM}" | |
mv -i "${DST_DIR}/server_${NUM}/${SERVICE}.jar" "${DST_DIR}/server_${NUM}/${SERVICE}_${NUM}.jar" | |
sed -i "s/server-port=.*/server-port=${PORT}/; s/query.port=.*/query.port=${PORT}/" "${DST_DIR}/server_${NUM}/server.properties" | |
((PORT += 1)) | |
done | |
} | |
mc_start() { | |
L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` | |
L_PREFIX="${L_TIMESTAMP}" | |
NUM=`printf %02d ${i}` | |
LOCAL_SERVICE="${SERVICE}_${NUM}.jar" | |
LOCAL_INVOCATION="${INVOCATION} ${LOCAL_SERVICE} ${OPTIONS}" | |
INSTANCE_PATH="${DST_DIR}/server_${NUM}" | |
# Check that the server is not already running | |
if pgrep -u ${USERNAME} -f ${LOCAL_SERVICE} > /dev/null | |
then | |
echo "${L_PREFIX} [WARNING] mc_start(): ${LOCAL_SERVICE} is already running" | |
else | |
echo "${L_PREFIX} [INFO] mc_start(): Starting ${LOCAL_SERVICE}..." | |
# Start the server process | |
as_user "cd ${INSTANCE_PATH} && screen -dmS ${SCREEN_SESSION}_${NUM} ${LOCAL_INVOCATION}" | |
sleep 1 | |
# Verify that the server process was started successfully, by checking a few times in a delayed loop | |
COUNTER=0 | |
while [ $COUNTER -lt 30 ]; do | |
if pgrep -u ${USERNAME} -f ${LOCAL_SERVICE} > /dev/null | |
then | |
L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` | |
L_PREFIX="${L_TIMESTAMP}" | |
echo "${L_PREFIX} [INFO] mc_start(): ${LOCAL_SERVICE} is now running" | |
break | |
fi | |
sleep 1 | |
((COUNTER += 1)) | |
done | |
# If the loop counter hit the max value, the process was not started successfully | |
if [ ${COUNTER} -ge 30 ] | |
then | |
L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` | |
L_PREFIX="${L_TIMESTAMP}" | |
echo "${L_PREFIX} [ERROR] mc_start(): Could not start ${LOCAL_SERVICE}" | |
fi | |
fi | |
} | |
mc_stop() { | |
L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` | |
L_PREFIX="${L_TIMESTAMP}" | |
NUM=`printf %02d ${i}` | |
LOCAL_SERVICE="${SERVICE}_${NUM}.jar" | |
# Check that the process is running | |
if pgrep -u ${USERNAME} -f ${LOCAL_SERVICE} > /dev/null | |
then | |
echo "${L_PREFIX} [INFO] mc_stop(): Stopping ${LOCAL_SERVICE}" | |
as_user "screen -p 0 -S ${SCREEN_SESSION}_${NUM} -X eval 'stuff \"save-all\"\015'" | |
sleep 1 | |
as_user "screen -p 0 -S ${SCREEN_SESSION}_${NUM} -X eval 'stuff \"stop\"\015'" | |
sleep 1 | |
# Check if the process was successfully stopped | |
COUNTER=0 | |
while [ ${COUNTER} -lt 30 ]; do | |
if ! pgrep -u ${USERNAME} -f ${LOCAL_SERVICE} > /dev/null | |
then | |
L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` | |
L_PREFIX="${L_TIMESTAMP}" | |
echo "${L_PREFIX} [INFO] mc_stop(): ${LOCAL_SERVICE} has been stopped" | |
break | |
fi | |
sleep 1 | |
((COUNTER += 1)) | |
done | |
if [ ${COUNTER} -ge 30 ] | |
then | |
L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` | |
L_PREFIX="${L_TIMESTAMP}" | |
echo "${L_PREFIX} [ERROR] mc_stop(): ${LOCAL_SERVICE} could not be stopped" | |
fi | |
else | |
echo "${L_PREFIX} [WARNING] mc_stop(): ${LOCAL_SERVICE} was not running" | |
fi | |
} | |
mc_pregen() { | |
L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` | |
L_PREFIX="${L_TIMESTAMP}" | |
#NUM=`printf %02d ${i}` | |
NUM="${1}" | |
LOCAL_SERVICE="${SERVICE}_${NUM}.jar" | |
# Check that the process is running | |
if pgrep -u ${USERNAME} -f ${LOCAL_SERVICE} > /dev/null | |
then | |
echo "${L_PREFIX} [INFO] mc_pregen(): Starting pregen on ${LOCAL_SERVICE}" | |
as_user "screen -p 0 -S ${SCREEN_SESSION}_${NUM} -X eval 'stuff \"${2}\"\015'" | |
else | |
echo "${L_PREFIX} [WARNING] mc_pregen(): ${LOCAL_SERVICE} was not running" | |
fi | |
} | |
mc_move_regions() { | |
if [ ! -d "${COMBINED_WORLD_DIR}/region" ] | |
then | |
mkdir -p "${COMBINED_WORLD_DIR}/region" | |
fi | |
SERVER=1 | |
for i in `seq 0 3`; | |
do | |
((XS = ($i - 2) * 25)) | |
((XE = $XS + 24)) | |
for j in `seq 0 3`; | |
do | |
NUM=`printf %02d ${SERVER}` | |
cd "${DST_DIR}/server_${NUM}/${WORLD_DIR}/region/" | |
echo "server ${NUM}" | |
((ZS = ($j - 2) * 25)) | |
((ZE = $ZS + 24)) | |
for x in `seq $XS $XE`; | |
do | |
for z in `seq $ZS $ZE`; | |
do | |
#echo "mv -n r.${x}.${z}.mca ${COMBINED_WORLD_DIR}/region/" | |
mv -n "r.${x}.${z}.mca" "${COMBINED_WORLD_DIR}/region/" | |
done | |
done | |
((SERVER += 1)) | |
done | |
done | |
} | |
# Process the commands | |
case "${1}" in | |
create_all) | |
mc_create_instances 16 | |
;; | |
start_all) | |
for i in `seq 1 16`; | |
do | |
mc_start $i | |
done | |
;; | |
stop_all) | |
for i in `seq 1 16`; | |
do | |
mc_stop $i | |
done | |
;; | |
pregen_all) | |
mc_pregen "01" "pregen 0 -1600 -801 -1600 -801" | |
mc_pregen "02" "pregen 0 -1600 -801 -800 -1" | |
mc_pregen "03" "pregen 0 -1600 -801 0 799" | |
mc_pregen "04" "pregen 0 -1600 -801 800 1599" | |
mc_pregen "05" "pregen 0 -800 -1 -1600 -801" | |
mc_pregen "06" "pregen 0 -800 -1 -800 -1" | |
mc_pregen "07" "pregen 0 -800 -1 0 799" | |
mc_pregen "08" "pregen 0 -800 -1 800 1599" | |
mc_pregen "09" "pregen 0 0 799 -1600 -801" | |
mc_pregen "10" "pregen 0 0 799 -800 -1" | |
mc_pregen "11" "pregen 0 0 799 0 799" | |
mc_pregen "12" "pregen 0 0 799 800 1599" | |
mc_pregen "13" "pregen 0 800 1599 -1600 -801" | |
mc_pregen "14" "pregen 0 800 1599 -800 -1" | |
mc_pregen "15" "pregen 0 800 1599 0 799" | |
mc_pregen "16" "pregen 0 800 1599 800 1599" | |
;; | |
move_regions) | |
mc_move_regions | |
;; | |
*) | |
echo "Usage: /etc/init.d/minecraft {create_all|start_all|stop_all|pregen_all \"server command\"}" | |
exit 1 | |
;; | |
esac | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment