Skip to content

Instantly share code, notes, and snippets.

@maruohon
Created November 11, 2014 18:13
Show Gist options
  • Save maruohon/49ef39c3992316202553 to your computer and use it in GitHub Desktop.
Save maruohon/49ef39c3992316202553 to your computer and use it in GitHub Desktop.
A bash script to generate huge Minecraft worlds in several smaller parts
#!/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