View difference between Paste ID: vX4WCDvm and
SHOW: | | - or go back to the newest paste.
1-
1+
#! /bin/sh
2
### BEGIN INIT INFO
3
# Provides:          minecraft_server
4
# Required-Start:    $remote_fs $syslog
5
# Required-Stop:     $remote_fs $syslog
6
# Default-Start:     2 3 4 5
7
# Default-Stop:      0 1 6
8
# Short-Description: Minecraft Server control script.
9
# Description:       Minecraft Server control script.
10
#   Options
11
#     start <world>          - Start the Minecraft world server.  Starts all world servers by default.
12
#     stop <world>           - Stop the Minecraft world server.  Stops all world servers by default.
13
#     force-stop <world>     - Forcibly stop the Minecraft world server.  Forcibly stops all world servers by default.
14
#     restart <world>        - Restart the Minecraft world server.  Restarts all world servers by default.
15
#     force-restart <world>  - Forcibly restart the Minecraft world server.  Forcibly restarts all world servers by default.
16
#     status <world>         - Display the status of the Minecraft world server.  Displays the status of all world servers by default.
17
#     send <world> <command> - Send a command to a Minecraft world server.
18
#     backup <world>         - Backup the Minecraft world.  Backup all worlds by default.
19
#     c10t <world>           - Run the c10t mapping software on the Minecraft world.  Maps all worlds by default.
20
#     update <software>      - Update a software package.  Update the server software and all addons by default.
21
#                                Available software options
22
#                                  server - Minecraft server software.
23
#                                  c10t - Mapping software.
24
### END INIT INFO
25
26
27
USER_NAME=minecraft
28
29
LOCATION="/home/$USER_NAME"
30
31
32
## Minecraft software options.
33
34
# Software packages, used for updating.
35
PACKAGES="server c10t"
36
37
# Minecraft server.
38
SERVER_URL="http://www.minecraft.net/download/minecraft_server.jar"
39
SERVER_LOCATION="$LOCATION/minecraft_server"
40
SERVER_JAR="$SERVER_LOCATION/minecraft_server.jar"
41
SERVER_ARGS="nogui"
42
INITIAL_MEMORY="1024M"
43
MAXIMUM_MEMORY="1024M"
44
45
# c10t mapping software.
46
ARCH=$(uname -m)
47
C10T_URL="http://toolchain.eu/hudson/job/c10t%20HEAD%20Linux%20$ARCH/lastStableBuild/artifact/build/c10t-HEAD-linux-$ARCH.tar.gz"
48
C10T_LOCATION="$LOCATION/c10t"
49
C10T_BIN="$C10T_LOCATION/c10t"
50
51
# Location to place map images, and the URL displayed to users for map access.
52
MAPS_URL="http://minecraft.server.com/maps"
53
MAPS_LOCATION="$LOCATION/maps"
54
55
56
## World configuration.
57
58
WORLDS_LOCATION="$LOCATION/worlds"
59
60
# List of worlds and the ports they are running on.  This file will
61
# be generated if missing.
62
# Note: World name should not contain a space, at least for now.
63
# ie:
64
#   alpha	25565
65
#   beta	25566
66
WORLDS_CONF="$LOCATION/worlds.conf"
67
68
# Default world name and port if worlds.conf is missing.
69
DEFAULT_WORLD="world"
70
DEFAULT_PORT="25565"
71
72
73
## Backup configuration.
74
75
BACKUP_LOCATION="$LOCATION/backups"
76
77
# Length in days that backups survive.
78
BACKUP_FULL_DURATION=31
79
80
81
## Internal Methods.
82
83
getProcessIDs() {
84
	# Get the PIDs of the Screen and Java process for the world server.
85
	# ARGS: world
86
	local SCREEN_PID JAVA_PID
87
	SCREEN_PID=$(su -c "$SCREEN -ls" $USER_NAME | $PERL -ne 'if ($_ =~ /^\t(\d+)\.minecraft-'$1'/) { print $1; }')
88
	JAVA_PID=$(ps -a -u $USER_NAME -o pid,ppid,comm | $PERL -ne 'if ($_ =~ /^\s*(\d+)\s+'$SCREEN_PID'\s+java/) { print $1; }')
89
	echo "$SCREEN_PID $JAVA_PID"
90
}
91
92
serverRunning() {
93
	# Check to see if the world server is running.
94
	# ARGS: world
95
	local PIDS
96
	PIDS=$(getProcessIDs $1)
97
	# Try to determine if the world is running.
98
	if [ -n "$(echo $PIDS | cut -d ' ' -f1)" ] && [ -n "$(echo $PIDS | cut -d ' ' -f2)" ]; then
99
		echo 1
100
	else
101
		echo 0
102
	fi
103
}
104
105
sendCommand() {
106
	# Send a command to the world server.
107
	# ARGS: world command
108
	local COMMAND
109
	COMMAND=$(printf "$2\r")
110
	su -c "$SCREEN -S minecraft-$1 -p 0 -X stuff \"$COMMAND\"" $USER_NAME
111
	if [ ! $? = 0 ]; then
112
		printf "Error sending command to server.\n"
113
		exit 1
114
	fi
115
116
}
117
118
listContains() {
119
	# Check whether a list contains something that matches the pattern.
120
	# ARGS: pattern list
121
	local MATCH
122
	MATCH=$(echo \"$2\" | $PERL -ne 'if ($_ =~ /('$1')/) { print "$1"; }')
123
	if [ -n "$MATCH" ]; then
124
		echo 1
125
	else
126
		echo 0
127
	fi
128
}
129
130
start() {
131
	# Start the world server.
132
	# ARGS: world
133
	local SERVER_PORT
134
	# Make sure that the server directory exists.
135
	su -c "mkdir -p $WORLDS_LOCATION/$1" $USER_NAME
136
	cd $WORLDS_LOCATION/$1
137
	# Make sure that the server properties file exists.
138
	if [ ! -e server.properties ]; then
139
		SERVER_PORT=$(grep $1 $WORLDS_CONF | cut -f2)
140
		su -c "printf \"# Minecraft server properties\n\" > server.properties" $USER_NAME
141
		su -c "printf \"level-name=world\n\" >> server.properties" $USER_NAME
142
		su -c "printf \"server-port=$SERVER_PORT\n\" >> server.properties" $USER_NAME
143
	fi
144
	# Start the server.
145
	su -c "$SCREEN -dmS minecraft-$1 $JAVA -Xms$INITIAL_MEMORY -Xmx$MAXIMUM_MEMORY -jar $SERVER_JAR $SERVER_ARGS" $USER_NAME
146
	if [ ! $? = 0 ]; then
147
		printf "Error starting the server.\n"
148
		exit 1
149
	fi
150
}
151
152
forceStop() {
153
	# Forcibly stop the world server.
154
	# ARGS: world
155
	local PIDS
156
	PIDS=$(getProcessIDs $1)
157
	# Try to stop the server cleanly first.
158
	sendCommand $1 "stop"
159
	sleep 5
160
	# Kill the process ids of the world server.
161
	kill -9 $PIDS > /dev/null 2>&1
162
}
163
164
fullBackup() {
165
	# Backup the world server.
166
	# ARGS: world
167
	# Grab the date and time.
168
	local DATE_TIME
169
	DATE_TIME=$(date +%Y-%m-%d-%H:%M:%S)
170
	su -c "mkdir -p $BACKUP_LOCATION" $USER_NAME
171
	cd $WORLDS_LOCATION
172
	su -c "tar -czf $BACKUP_LOCATION/fullBackup-$1-$DATE_TIME.tar.gz $1" $USER_NAME
173
	# Cleanup old backups.
174
	su -c "find $BACKUP_LOCATION -name fullBackup-$1-* -type f -mtime +$BACKUP_FULL_DURATION -delete" $USER_NAME
175
}
176
177
update() {
178
	# Update software package.
179
	# ARGS: package
180
	case "$1" in
181
		server)
182
			su -c "mkdir -p $SERVER_LOCATION" $USER_NAME
183
			# Backup the old server jar.
184
			if [ -e $SERVER_JAR ]; then
185
				su -c "mv -f $SERVER_JAR $SERVER_JAR.old" $USER_NAME
186
			fi
187
			# Download the new server software.
188
			su -c "$WGET -qO $SERVER_JAR $SERVER_URL" $USER_NAME
189
			# Check for error and restore backup if found.
190
			if [ ! $? = 0 ]; then
191
				printf "Error updating server software.\n"
192
				if [ -e "$SERVER_JAR.old" ]; then
193
					su -c "mv -f $SERVER_JAR.old $SERVER_JAR" $USER_NAME
194
				fi
195
				exit 1
196
			fi
197
		;;
198
		c10t)
199
			su -c "mkdir -p $C10T_LOCATION" $USER_NAME
200
			# Download the new c10t mapping software.
201
			cd $C10T_LOCATION
202
			su -c "$WGET -qO c10t.tar.gz $C10T_URL" $USER_NAME
203
			if [ ! $? = 0 ]; then
204
				printf "Error updating c10t.\n"
205
				exit 1
206
			fi
207
			su -c "tar xzf c10t.tar.gz" $USER_NAME
208
			su -c "cp -R c10t-HEAD/* ." $USER_NAME
209
			su -c "rm -R c10t-HEAD" $USER_NAME
210
		;;
211
		*)
212
			printf "Unknown software package: $1\n"
213
			exit 1
214
		;;
215
	esac
216
}
217
218
c10t() {
219
	# Run c10t mapping software on the server.
220
	# ARGS: world
221
	su -c "mkdir -p $MAPS_LOCATION/$1" $USER_NAME
222
	# Make sure that the world files are actually there before mapping.
223
	if [ -e "$WORLDS_LOCATION/$1/server.properties" ]; then
224
		su -c "$C10T_BIN -s -w $WORLDS_LOCATION/$1/$1 -o $MAPS_LOCATION/$1/surface.png" $USER_NAME
225
		su -c "$C10T_BIN -s -c -w $WORLDS_LOCATION/$1/$1 -o $MAPS_LOCATION/$1/caves.png" $USER_NAME
226
		su -c "$C10T_BIN -s -c -H -w $WORLDS_LOCATION/$1/$1 -o $MAPS_LOCATION/$1/caves_heightmap.png" $USER_NAME
227
		su -c "$C10T_BIN -s -a -i 56 -H -w $WORLDS_LOCATION/$1/$1 -o $MAPS_LOCATION/$1/diamonds_heightmap.png" $USER_NAME
228
		su -c "$C10T_BIN -s -a -i 4 -H -w $WORLDS_LOCATION/$1/$1 -o $MAPS_LOCATION/$1/cobble_heightmap.png" $USER_NAME
229
		su -c "$C10T_BIN -s -q -w $WORLDS_LOCATION/$1/$1 -o $MAPS_LOCATION/$1/surface_oblique.png" $USER_NAME
230
		su -c "$C10T_BIN -s -q -c -w $WORLDS_LOCATION/$1/$1 -o $MAPS_LOCATION/$1/caves_oblique.png" $USER_NAME
231
	fi
232
}
233
234
## Begin.
235
236
# Make sure that Java, Perl, GNU Screen, and GNU Wget are installed.
237
JAVA=$(which java)
238
PERL=$(which perl)
239
SCREEN=$(which screen)
240
WGET=$(which wget)
241
if [ ! -e $JAVA ]; then
242
	printf "Java not found!\n"
243
	printf "Try installing this with:\n"
244
	printf "sudo apt-get install openjdk-6-jre\n"
245
	exit 1
246
fi
247
if [ ! -e $PERL ]; then
248
	printf "Perl not found!\n"
249
	printf "Try installing this with:\n"
250
	printf "sudo apt-get install perl\n"
251
	exit 1
252
fi
253
if [ ! -e $SCREEN ]; then
254
	printf "GNU Screen not found!\n"
255
	printf "Try installing this with:\n"
256
	printf "sudo apt-get install screen\n"
257
	exit 1
258
fi
259
if [ ! -e $WGET ]; then
260
	printf "GNU Wget not found!\n"
261
	printf "Try installing this with:\n"
262
	printf "sudo apt-get install wget\n"
263
	exit 1
264
fi
265
266
# Make sure that the minecraft user exists, print a sane error if not.
267
if [ ! -n "$(grep $USER_NAME /etc/passwd)" ]; then
268
	printf "This script requires that a user account named $USER_NAME exist on this system.\n"
269
	printf "Try adding this user:\n"
270
	printf "sudo adduser $USER_NAME\n"
271
	exit 1
272
fi
273
274
# From here on out the script needs to be run as root, print a sane error if running as a user.
275
WHOAMI=$(whoami)
276
if [ ! "$WHOAMI" = "root" ]; then
277
	printf "This script needs to be run as root, you seem to be $WHOAMI.\n"
278
	printf "Try running this with sudo:\n"
279
	printf "sudo $0 start\n"
280
	exit 1
281
fi
282
283
# Make sure that the worlds.conf file exists.
284
if [ ! -e $WORLDS_CONF ]; then
285
	su -c "printf \"$DEFAULT_WORLD\t$DEFAULT_PORT\n\" > $WORLDS_CONF" $USER_NAME
286
fi
287
288
# Grab the list of worlds.
289
WORLDS=$(cut -f1 $WORLDS_CONF)
290
291
# Respond to the command line arguments.
292
case "$1" in
293
	start)
294
		printf "Starting Minecraft Server:"
295
		# Make sure that the server software exists.
296
		if [ ! -e $SERVER_JAR ]; then
297
			printf "Server software not found, downloading it...\n"
298
			update "server"
299
		fi
300
		# Check for the optional world command line argument.
301
		if [ -n "$2"  ] && [ $(listContains $2 "$WORLDS") = 1 ]; then
302
			WORLDS="$2"
303
		elif [ -n "$2" ]; then
304
			printf "Minecraft world $2 not found!\n"
305
			printf "  Usage:  $0 $1 <world>\n"
306
			exit 1
307
		fi
308
		# Start each world requested, if not already running.
309
		for WORLD in $WORLDS; do
310
			if [ $(serverRunning $WORLD) = 0 ]; then
311
				printf " $WORLD"
312
				start $WORLD
313
			fi
314
		done
315
		printf "\n"
316
	;;
317
	stop|force-stop)
318
		printf "Stopping Minecraft Server:"
319
		# Check for the optional world command line argument.
320
		if [ -n "$2"  ] && [ $(listContains $2 "$WORLDS") = 1 ]; then
321
			WORLDS="$2"
322
		elif [ -n "$2" ]; then
323
			printf "Minecraft world $2 not found!\n"
324
			printf "  Usage:  $0 $1 <world>\n"
325
			exit 1
326
		fi
327
		# Stop each world requested, if running.
328
		for WORLD in $WORLDS; do
329
			# Try to stop the world cleanly.
330
			if [ $(serverRunning $WORLD) = 1 ]; then
331
				printf " $WORLD"
332
				sendCommand $WORLD "say The server is about to go down."
333
				sendCommand $WORLD "save-all"
334
				sendCommand $WORLD "save-off"
335
				sendCommand $WORLD "say The server is going down in 5 seconds..."
336
				sleep 5
337
				if [ "$1" = "force-stop" ]; then
338
					forceStop $WORLD
339
				else
340
					sendCommand $WORLD "stop"
341
				fi
342
				sleep 5
343
			fi
344
		done
345
		printf "\n"
346
	;;
347
	restart|reload|force-restart|force-reload)
348
		printf "Restarting Minecraft Server:"
349
		# Check for the optional world command line argument.
350
		if [ -n "$2"  ] && [ $(listContains $2 "$WORLDS") = 1 ]; then
351
			WORLDS="$2"
352
		elif [ -n "$2" ]; then
353
			printf "Minecraft world $2 not found!\n"
354
			printf "  Usage:  $0 $1 <world>\n"
355
			exit 1
356
		fi
357
		# Restart each world requested, start those not already running.
358
		for WORLD in $WORLDS; do
359
			printf " $WORLD"
360
			if [ $(serverRunning $WORLD) = 1 ]; then
361
				sendCommand $WORLD "say The server is about to restart."
362
				sendCommand $WORLD "save-all"
363
				sendCommand $WORLD "save-off"
364
				sendCommand $WORLD "say Restarting in 5 seconds..."
365
				sleep 5
366
				if [ "$(echo $1 | cut -d '-' -f1)" = "force" ]; then
367
					forceStop $WORLD
368
				else
369
					sendCommand $WORLD "stop"
370
				fi
371
				sleep 5
372
			fi;
373
			start $WORLD
374
		done
375
		printf "\n"
376
	;;
377
	status|show)
378
		printf "Minecraft Server Status:\n"
379
		# Check for the optional world command line argument.
380
		if [ -n "$2"  ] && [ $(listContains $2 "$WORLDS") = 1 ]; then
381
			WORLDS="$2"
382
		elif [ -n "$2" ]; then
383
			printf "Minecraft world $2 not found!\n"
384
			printf "  Usage:  $0 $1 <world>\n"
385
			exit 1
386
		fi
387
		# Show the status of each world requested.
388
		for WORLD in $WORLDS; do
389
			printf "  $WORLD: "
390
			if [ $(serverRunning $WORLD) = 1 ]; then
391
				printf "running.\n"
392
			else
393
				printf "not running.\n"
394
			fi
395
		done
396
	;;
397
	send)
398
		WORLD=$2
399
		if [ -n "$WORLD" ] && [ $(listContains $WORLD "$WORLDS") = 1 ]; then
400
			shift 2
401
			printf "Send command to world $WORLD: $*\n"
402
			sendCommand $WORLD "$*"
403
		else
404
			printf "Minecraft world $WORLD not found!\n"
405
			printf "  Usage:  $0 $1 <world> <command>\n"
406
			printf "     ie:  $0 $1 world say \"Hello World!\"\n"
407
			exit 1
408
		fi
409
	;;
410
	backup)
411
		printf "Backing up Minecraft Server:"
412
		# Check for the optional world command line argument.
413
		if [ -n "$2"  ] && [ $(listContains $2 "$WORLDS") = 1 ]; then
414
			WORLDS="$2"
415
		elif [ -n "$2" ]; then
416
			printf "Minecraft world $2 not found!\n"
417
			printf "  Usage:  $0 $1 <world>\n"
418
			exit 1
419
		fi
420
		# Backup each world requested.
421
		for WORLD in $WORLDS; do
422
			printf " $WORLD"
423
			if [ $(serverRunning $WORLD) = 1 ]; then
424
				sendCommand $WORLD "say Backing up the world."
425
				sendCommand $WORLD "save-all"
426
				sendCommand $WORLD "save-off"
427
				sleep 20
428
				fullBackup $WORLD
429
				sendCommand $WORLD "save-on"
430
				sendCommand $WORLD "say Backup complete."
431
			else
432
				fullBackup $WORLD
433
			fi
434
		done
435
		printf "\n"
436
	;;
437
	update)
438
		printf "Updating the Minecraft Server software...\n"
439
		# Check for the optional package command line argument.
440
		if [ -n "$2"  ] && [ $(listContains $2 "$PACKAGES") = 1 ]; then
441
			PACKAGES="$2"
442
		elif [ -n "$2" ]; then
443
			printf "Minecraft software package $2 not found!\n"
444
			printf "  Usage:  $0 $1 <software package>\n"
445
			exit 1
446
		fi
447
		if [ -n "$(echo $PACKAGES | grep server)" ]; then
448
			printf "Stopping Minecraft Server:"
449
			for WORLD in $WORLDS; do
450
				printf " $WORLD"
451
				if [ $(serverRunning $WORLD) = 1 ]; then
452
					sendCommand $WORLD "say The server software is being updated."
453
					sendCommand $WORLD "say Server restart is imminent."
454
					sendCommand $WORLD "save-all"
455
					sendCommand $WORLD "save-off"
456
					sleep 20
457
					fullBackup $WORLD
458
					sendCommand $WORLD "say Restarting in 5 seconds."
459
					sleep 5
460
					sendCommand $WORLD "stop"
461
				else
462
					fullBackup $WORLD
463
				fi
464
			done
465
			printf "\n"
466
		fi
467
		# Update each software package requested.
468
		printf "Updating software package:"
469
		for PACKAGE in $PACKAGES; do
470
			printf " $PACKAGE"
471
			update $PACKAGE
472
		done
473
		printf "\n"
474
		if [ -n "$(echo $PACKAGES | grep server)" ]; then
475
			printf "Starting Minecraft Server:"
476
			for WORLD in $WORLDS; do
477
				printf " $WORLD"
478
				start $WORLD
479
			done
480
			printf "\n"
481
		fi
482
	;;
483
	c10t|map)
484
		# Make sure that the c10t software exists.
485
		if [ ! -e $C10T_BIN ]; then
486
			printf "c10t software not found, downloading it...\n"
487
			update "c10t"
488
		fi
489
		# Check for the optional world command line argument.
490
		if [ -n "$2"  ] && [ $(listContains $2 "$WORLDS") = 1 ]; then
491
			WORLDS="$2"
492
		elif [ -n "$2" ]; then
493
			printf "Minecraft world $2 not found!\n"
494
			printf "  Usage:  $0 $1 <world>\n"
495
			exit 1
496
		fi
497
		# Run c10t on each world requested.
498
		printf "Running c10t mapping:"
499
		for WORLD in $WORLDS; do
500
			printf " $WORLD"
501
			if [ $(serverRunning $WORLD) = 1 ]; then
502
				sendCommand $WORLD "say The world is about to be mapped with c10t."
503
				sendCommand $WORLD "save-all"
504
				sendCommand $WORLD "save-off"
505
				sleep 20
506
				fullBackup $WORLD
507
				c10t $WORLD
508
				sendCommand $WORLD "save-on"
509
				sendCommand $WORLD "say Mapping is complete.  You can access the maps at:"
510
				sendCommand $WORLD "say $MAPS_URL/$WORLD"
511
			else
512
				fullBackup $WORLD
513
				c10t $WORLD
514
			fi
515
		done
516
		printf "\n"
517
	;;
518
	*)
519
		printf "Usage: $0 {start|stop|force-stop|restart|force-restart|status|send|backup|update|c10t} {Optional: world or software package}\n"
520
		exit 1
521
	;;
522
esac
523
exit 0