I've been working on a solution, and here is what I have so far:
Ok, here is a working* version of the script to interface with the query server. It's not quite ready for inclusion in the main script, but as soon as I work out some bugs and get some test data from other people I would like to add its functionality.
* I still seem to be having some encode/decode issues, comments in the script (marked XXX) allude to the problems.
#!/bin/sh
# Initialize a connection with the Minecraft query server.
#
# @param 1 world
queryInitialize() {
local NAME PORT
# The name of the Minecraft query server.
NAME="minecraft-query-$1"
# XXX this should be known from $1
PORT=25565
# Start a tail process to watch for changes to the .query file to pipe to
# the Minecraft query server via netcat. The response from the query
# server is piped into the .response file.
rm -f $NAME.query
touch $NAME.query
# XXX the PID below is the script's, we will want to use the server's PID instead.
tail -f $NAME.query --pid=$$ | nc -q 1 -u 127.0.0.1 $PORT > $NAME.response &
}
# Pack a hex string into a buffer file that is piped to the Minecraft query
# server.
#
# @param 1 The world server of interest.
# @param 2 The hex string packet to send.
# @param 3 The response format.
# @return The response.
querySendPacket() {
local NAME PACKET RESPONSE
# The name of the Minecraft query server.
NAME="minecraft-query-$1"
# Add the magic byte and CR+LF to the incoming packet.
PACKET=$(printf "FEFD%s0D0A" $2)
# Pack the hex string packet and write it to the .query buffer file.
perl -e '
print map { pack("C", hex($_)) } ("'$PACKET'" =~ /(..)/g);
' >> $NAME.query
# Give the query server a second to respond.
sleep 1
# Unpack the token from the response buffer file.
# XXX The swap below removes the variable amount of null bytes at the start
# of the response string that shouldn't be there as far as I can tell.
# This variable amount of null bytes is replaced with 4 null bytes: one
# for the type (information request), and three for the first bytes of
# the number 1 (0001). This only seems to happen with the information
# request packet, but doesn't seem to affect the challenge packet.
RESPONSE=$(perl -ne '
$_ =~ s/^\0+/\0\0\0\0/; print join "\t", unpack ("'$3'", $_);
' $NAME.response)
# Remove the response from the .response buffer file.
printf "" > $NAME.response
# Return the response.
printf "$RESPONSE\n"
}
# Send a challenge packet to the Minecraft query server.
#
# @param 1 The world server of interest.
# @return <type>\t<id>\t<token>
querySendChallengePacket() {
local ID PACKET TOKEN
# XXX there is a translation issue somewhere in the pipeline,
# use ID of 1 until a better fix is found.
# # Use the current time as the packet ID.
# ID=$(date +%s)
ID=1
# Create the challenge packet.
PACKET=$(printf "09%.8x" $ID)
# Send the challenge packet to the Minecraft query server.
TOKEN=$(querySendPacket $1 $PACKET "Cl>Z*")
# Return the challenge token.
printf "$TOKEN\n"
}
# Send an information request packet to the Minecraft query server.
#
# @param 1 The world server of interest.
# @param 2 The challenge token.
# @return <type>\t<id>\t<MOTD>\t<gametype>\t<map>\t<numplayers>\t<maxplayers>\t<hostport>\t<hostip>
querySendInformationPacket() {
local ID PACKET RESPONSE
# XXX there is a translation issue somewhere in the pipeline,
# use ID of 1 until a better fix is found.
# # Use the current time as the packet ID.
# ID=$(date +%s)
ID=1
# Create the information request packet.
PACKET=$(printf "00%.8x%.8x" $ID $2)
# Send the information request packet to the Minecraft query server.
RESPONSE=$(querySendPacket $1 $PACKET "Cl>Z*Z*Z*Z*Z*s<Z*")
# Return the response.
printf "$RESPONSE\n"
}
# Send a status query to the Minecraft query server.
#
# @param 1 The world server of interest.
queryStatus() {
local TOKEN RESPONSE
# Send a challenge packet to the Minecraft query server.
TOKEN=$(querySendChallengePacket $1 | cut -f 3)
# Send an information request packet to the Minecraft query server.
RESPONSE=$(querySendInformationPacket $1 $TOKEN)
# Return the response.
printf "$RESPONSE\n"
}
# Initialize the connection with the Minecraft query server.
queryInitialize "world"
# Send a status query to the Minecraft query server.
PAYLOAD=$(queryStatus "world")
printf "payload: '$PAYLOAD'\n"
Sorry for seemingly vanishing - I get to have two wisdom teeth extracted and they're interfering with my ability to see straight, let alone get anything accomplished. (I'm typing this with a blob of Ambesol stuffed into one of them.) I'll probably be out of pocket for another couple weeks but will be following along where possible.
Sorry for seemingly vanishing - I get to have two wisdom teeth extracted and they're interfering with my ability to see straight, let alone get anything accomplished. (I'm typing this with a blob of Ambesol stuffed into one of them.) I'll probably be out of pocket for another couple weeks but will be following along where possible.
Oh dang, I hope you get feeling better.
I got lucky with wisdom teeth. I only had one, and it never developed into a full tooth. Didn't even know I had it until I had an xray of my head.
I've been looking at your changes to the RAM disk stuff in your version of the script, and feel like I'm coming around to doing things your way rather than how I had them in the script. I liked how I did things because it was simple, but I don't currently see any way to get around the symlink method if we don't want the configuration and log files for the world to be copied over (your method is actually very similar to one used in an old version of this script). I'm all ears if anyone sees a simpler way of accomplishing this, but I'm tempted to go ahead and make this change.
One question though. What is the point of changing the usage of rsync to cp? Won't they perform the same operation with rsync doing less IO overall?
I've been looking at your changes to the RAM disk stuff in your version of the script, and feel like I'm coming around to doing things your way rather than how I had them in the script. I liked how I did things because it was simple, but I don't currently see any way to get around the symlink method if we don't want the configuration and log files for the world to be copied over (your method is actually very similar to one used in an old version of this script). I'm all ears if anyone sees a simpler way of accomplishing this, but I'm tempted to go ahead and make this change.
One question though. What is the point of changing the usage of rsync to cp? Won't they perform the same operation with rsync doing less IO overall?
Rsync will only copy over missing and updated files, CP will copy everything.
During a mirror sync, rsync would be faster. During a server startup, cp would be faster if the mirror directory was cleared on server stop (I don't recall if it is).
Rsync will only copy over missing and updated files, CP will copy everything.
During a mirror sync, rsync would be faster. During a server startup, cp would be faster if the mirror directory was cleared on server stop (I don't recall if it is).
Basically, this. On Debian and Ubuntu and the derivatives thereof, the ramdisk on /dev/shm is empty on startup by default. Since there's nothing to compare against, rsync is slightly slower than cp due to the extra file check it performs, while cp can just straight-up blast bits into place.
We could probably even use this to notify when there is an update or even perform auto-updates.
Indeed - the new launcher has to have a fixed reference location for version lookups (especially since it now supports having multiple jar versions in one client install), so nothing says we can't sniff that file for news on version changes.
I can't help but wonder what the point will be where we exceed what we can realistically accomplish with shell scripts and have to move to an executable, or perhaps a combination of executable and scripts. Maybe even a jar or something that can also capture console output from running servers. An intermediary executable of some sort with a well-documented API would be the best thing since sliced bread for administration...
Zanix, thanks for the version info sleuthing. I posted a couple patches to handle that new information.
Webmaka, I finally got around to adding in your mirror-image changes. I've tested it, and it seems to work. It is not as simple as the old code, so here's hoping there are no corner cases that we missed.
Has anyone gotten around to testing my query server script? Here is the latest version as a patch to the script:
From 666d1c362bbac6058f6f83c8cc9e4cfc53b3a7ad Mon Sep 17 00:00:00 2001
From: "Jason M. Wood" <[email protected]>
Date: Sun, 4 Aug 2013 23:14:15 -0600
Subject: [PATCH] Use the Minecraft Query Server rather than parsing the log
file.
---
minecraft_server | 199 +++++++++++++++++++++++++++----------------------------
1 file changed, 97 insertions(+), 102 deletions(-)
diff --git a/minecraft_server b/minecraft_server
index 1414d84..4609fe4 100755
--- a/minecraft_server
+++ b/minecraft_server
@@ -583,95 +583,6 @@ checkOptionalArgument() {
}
# ---------------------------------------------------------------------------
-# Check for users logging into a world. If a user logs in, perform
-# login functions.
-#
-# @param 1 The world server of interest.
-# @param 2 The message to check for users logging in.
-# ---------------------------------------------------------------------------
-checkForLogin() {
- local LOGIN PLAYER_NAME
- LOGIN=$(echo "$2" | $PERL -ne 'if ($_ =~ /(\w+)\s*\[\/([0-9\.]+)\:(\d+)\] logged in with entity id (\d+)/) { print "$1\t$2\t$3\t$4"; }')
- if [ -n "$LOGIN" ]; then
- PLAYER_NAME=$(printf "$LOGIN" | cut -f1)
- # Add the user to the world.users file.
- execute "printf \"$LOGIN\n\" >> \"$WORLDS_LOCATION/$1.users\"" $USER_NAME
- # Announce the user logging in via lib-notify.
- if [ $USE_LIBNOTIFY ]; then
- libNotify "Minecraft - $1" "$PLAYER_NAME has logged into world."
- fi
- # Whisper the MOTD to the user logging in.
- tellMOTD $1 $PLAYER_NAME
- fi
-}
-
-# ---------------------------------------------------------------------------
-# Check for users logging out of a world. If a user logs out, perform the
-# logout functions.
-#
-# @param 1 The world server of interest.
-# @param 2 The message to check for users logging out.
-# ---------------------------------------------------------------------------
-checkForLogout() {
- local LOGOUT BAN PLAYER_NAME
- LOGOUT=$(echo "$2" | $PERL -ne 'if ($_ =~ /(\w+) lost connection\: (.+)/) { print "$1\t$2"; }')
- BAN=$(echo "$2" | $PERL -ne 'if ($_ =~ /Disconnecting (\w+)\s*\[\/([0-9\.\:]+)\]\: You are banned/) { print "$1\t$2"; }')
- if [ -n "$LOGOUT" ]; then
- PLAYER_NAME=$(printf "$LOGOUT" | cut -f1)
- # Remove the user from the world.users file.
- execute "$PERL -i -ne 'print unless /^$PLAYER_NAME\t[0-9\.]+\t\d+\d+/;' $WORLDS_LOCATION/$1.users" $USER_NAME
- # Announce the user logging out via lib-notify.
- if [ $USE_LIBNOTIFY ]; then
- libNotify "Minecraft - $1" "$PLAYER_NAME has logged out of world."
- fi
- elif [ -n "$BAN" ]; then
- PLAYER_NAME=$(printf "$BAN" | cut -f1)
- # Remove the user from the world.users file.
- execute "$PERL -i -ne 'print unless /^$PLAYER_NAME\t[0-9\.]+\t\d+\d+/;' $WORLDS_LOCATION/$1.users" $USER_NAME
- # Announce the user ban via lib-notify.
- if [ $USE_LIBNOTIFY ]; then
- libNotify "Minecraft - $1" "$PLAYER_NAME has been banned from the world."
- fi
- fi
-}
-
-# ---------------------------------------------------------------------------
-# Parse through the log file for the given world. Uses checkFor methods to
-# find events such as users logging in or out.
-#
-# @param 1 The world server generating the log to parse.
-# ---------------------------------------------------------------------------
-parseLog() {
- local LINE DATE TIME TYPE MESSAGE
- while read LINE; do
- LINE=$(echo "$LINE" | $PERL -ne 'if ($_ =~ /(.+) (.+) \[(\w+)\] (.+)/) { print "$1\t$2\t$3\t$4"; }')
- DATE=$(echo "$LINE" | cut -f1)
- TIME=$(echo "$LINE" | cut -f2)
- TYPE=$(echo "$LINE" | cut -f3)
- MESSAGE=$(echo "$LINE" | cut -f4)
- case "$TYPE" in
- INFO)
- checkForLogin $1 "$MESSAGE"
- checkForLogout $1 "$MESSAGE"
- ;;
- SEVERE)
- if [ $AUTO_RESTART_ON_ERROR -eq 1 ]; then
- sendCommand $1 "say The server is experiencing issues, restarting in 5 seconds..."
- sleep 5
- stop $1
- sleep 5
- start $1
- fi
- ;;
- WARNING)
- ;;
- *)
- ;;
- esac
- done
-}
-
-# ---------------------------------------------------------------------------
# Rotates the world server log file.
#
# @param 1 The world server generating the log to rotate.
@@ -743,8 +654,8 @@ syncMirrorImage() {
}
# ---------------------------------------------------------------------------
-# Start the world server and the log processor. Generate the appropriate
-# environment for the server if it doesn't already exist.
+# Start the world server. Generate the appropriate environment for the
+# server if it doesn't already exist.
#
# @param 1 The world server to start.
# ---------------------------------------------------------------------------
@@ -782,11 +693,6 @@ start() {
fi
# Change to the world's directory.
cd $WORLD_DIR
- # Make sure that the server.log file exists.
- execute "touch server.log" $USER_NAME
- # Erase the world's users file before starting up the world, in
- # case it is not already empty for some reason.
- execute "printf \"\" > \"$WORLDS_LOCATION/$1.users\"" $USER_NAME
# Start the server.
execute "$SCREEN -dmS minecraft-$1 $SERVER_COMMAND" $USER_NAME
if [ $? -ne 0 ]; then
@@ -805,8 +711,12 @@ start() {
printf "Error starting the server: couldn't retrieve the server's process ID.\n"
exit 1
fi
- # Start the log processor.
- tail -n0 -f --pid=$PID $WORLD_DIR/server.log | parseLog $1 &
+ # Start a tail process to watch for changes to the query.dat file to pipe to
+ # the Minecraft query server via netcat. The response from the query
+ # server is piped into the response.dat file.
+ execute "printf '' > $WORLD_DIR/query.dat" $USER_NAME
+ execute "printf '' > $WORLD_DIR/response.dat" $USER_NAME
+ execute "tail -f --pid=$PID $WORLD_DIR/query.dat | nc -q 1 -u 127.0.0.1 $(getPort $1) > $WORLD_DIR/response.dat &" $USER_NAME
# Create a lock file on RedHat and derivatives.
if [ -d "/var/lock/subsys" ]; then
touch /var/lock/subsys/minecraft_server
@@ -821,9 +731,6 @@ start() {
stop() {
local WORLD NUM
sendCommand $1 "stop"
- # Erase the world's users file since we won't be able to catch
- # anyone logging off.
- execute "printf \"\" > \"$WORLDS_LOCATION/$1.users\"" $USER_NAME
# Synchronize the mirror image of the world prior to closing, if
# required.
if [ $ENABLE_MIRROR -eq 1 ] && [ -d $MIRROR_PATH ]; then
@@ -951,6 +858,94 @@ overviewer() {
fi
}
+# ---------------------------------------------------------------------------
+# Pack a hex string into a buffer file that is piped to the Minecraft query
+# server.
+#
+# @param 1 The world server of interest.
+# @param 2 The packet type.
+# @param 3 The packet ID.
+# @param 4 The hex string packet to send.
+# @param 5 The response format.
+# @return The response.
+# ---------------------------------------------------------------------------
+querySendPacket() {
+ local PACKET RESPONSE WORLD_DIR
+ # The world's directory.
+ WORLD_DIR="$WORLDS_LOCATION/$1"
+ # Add the magic bytes and CR+LF to the incoming packet.
+ PACKET=$(printf "FEFD%s%s%s0D0A" "$2" "$3" "$4")
+ # Pack the hex string packet and write it to the query.dat buffer file.
+ execute "$PERL -e '
+ print map { pack(\"C\", hex($_)) } (\"'$PACKET'\" =~ /(..)/g);
+ ' >> $WORLD_DIR/query.dat" $USER_NAME
+ # Give the query server a second to respond.
+ sleep 1
+ # Unpack the token from the response.dat buffer file. There are a
+ # variable amount of null bytes at the start of the response string, so
+ # build a pattern string to search for the actual start.
+ PATTERN=$($PERL -e 'foreach ("'$2$3'" =~ /(..)/g) { print "\\x" . $_; }')
+ RESPONSE=$($PERL -ne '
+ $_ =~ s/^\x00*'$PATTERN'/'$PATTERN'/; print join "\t", unpack ("'$5'", $_);
+ ' $WORLD_DIR/response.dat)
+ # Remove the response from the response.dat buffer file.
+ execute "printf '' > $WORLD_DIR/response.dat" $USER_NAME
+ # Return the response.
+ printf "$RESPONSE\n"
+}
+
+# ---------------------------------------------------------------------------
+# Send a challenge packet to the Minecraft query server.
+#
+# @param 1 The world server of interest.
+# @return <type>\t<id>\t<token>
+# ---------------------------------------------------------------------------
+querySendChallengePacket() {
+ local ID PACKET TOKEN
+ # The packet identifier.
+ ID="00000001"
+ # Use an empty packet.
+ PACKET="00000000"
+ # Send the challenge packet to the Minecraft query server.
+ TOKEN=$(querySendPacket "$1" "09" "$ID" "$PACKET" "Cl>Z*")
+ # Return the challenge token.
+ printf "$TOKEN\n"
+}
+
+# ---------------------------------------------------------------------------
+# Send an information request packet to the Minecraft query server.
+#
+# @param 1 The world server of interest.
+# @param 2 The challenge token.
+# @return <type>\t<id>\t<MOTD>\t<gametype>\t<map>\t<numplayers>\t<maxplayers>\t<hostport>\t<hostip>
+# ---------------------------------------------------------------------------
+querySendInformationPacket() {
+ local ID PACKET RESPONSE
+ # The packet identifier.
+ ID="00000001"
+ # Use the challenge token for the packet.
+ PACKET=$(printf "%.8x" $2)
+ # Send the information request packet to the Minecraft query server.
+ RESPONSE=$(querySendPacket "$1" "00" "$ID" "$PACKET" "Cl>Z*Z*Z*Z*Z*s<Z*")
+ # Return the response.
+ printf "$RESPONSE\n"
+}
+
+# ---------------------------------------------------------------------------
+# Send a status query to the Minecraft query server.
+#
+# @param 1 The world server of interest.
+# ---------------------------------------------------------------------------
+queryStatus() {
+ local TOKEN RESPONSE
+ # Send a challenge packet to the Minecraft query server.
+ TOKEN=$(querySendChallengePacket $1 | cut -f 3)
+ # Send an information request packet to the Minecraft query server.
+ RESPONSE=$(querySendInformationPacket $1 $TOKEN)
+ # Return the response.
+ printf "$RESPONSE\n"
+}
+
# ---------------------------------------------------------------------------
# Begin.
@@ -1110,7 +1105,7 @@ case "$1" in
for WORLD in $WORLDS; do
printf " $WORLD: "
if [ $(serverRunning $WORLD) -eq 1 ]; then
- printf "running (%d users online). Screen PID: %d. Java PID: %d.\n" $(cat $WORLDS_LOCATION/$WORLD.users | wc -l) $(getProcessIDs $WORLD)
+ printf "running (%d users online). Process IDs: Screen %d, Java %d.\n" "$(queryStatus $WORLD | cut -f6)" $(getProcessIDs $WORLD)
else
printf "not running.\n"
fi
--
1.8.1.2
We don't really need the information stored in the worlds.conf file. We can find the world names by listing the directories in the /home/minecraft/worlds directory. These world names can be verified by looking for a server.properties file that we could also use to parse out the port and other needed information.
My one question is: If we store our own values in the server.properties file, do they persist through the normal operation of a server? If not, I propose we at least use the same key=value format in a file called something like server.conf to make it simple.
We don't really need the information stored in the worlds.conf file. We can find the world names by listing the directories in the /home/minecraft/worlds directory. These world names can be verified by looking for a server.properties file that we could also use to parse out the port and other needed information.
My one question is: If we store our own values in the server.properties file, do they persist through the normal operation of a server? If not, I propose we at least use the same key=value format in a file called something like server.conf to make it simple.
I just got back from Defcon and I plan on testing testing if additional values persist in a server.properties file and the MC query changes as well soon. But now, sleep...
I can't help but wonder what the point will be where we exceed what we can realistically accomplish with shell scripts and have to move to an executable, or perhaps a combination of executable and scripts. Maybe even a jar or something that can also capture console output from running servers. An intermediary executable of some sort with a well-documented API would be the best thing since sliced bread for administration...
That happened a long time ago when the first Perl one-liner went in the script. Since a good chunk of the functionality in the script is now hidden away in these one-liners, I wouldn't be that opposed to switching over to Perl entirely. However, the current Shell/Perl combo is working and I don't see any reason to suffer a rewrite unless everyone is on board.
That happened a long time ago when the first Perl one-liner went in the script. Since a good chunk of the functionality in the script is now hidden away in these one-liners, I wouldn't be that opposed to switching over to Perl entirely. However, the current Shell/Perl combo is working and I don't see any reason to suffer a rewrite unless everyone is on board.
Most of the perl one-liners seem to be just regex pattern matching, since bash doesn't have regex.
I would rather not go full executable since that means users would not be able to tweak and adjust the script to their needs. That said, if this were to move into "real" code, I would not be against it, I would rather keep it open like perl or python and not bundle it as a binary executable.
Hi,
i have a small issue. When i change in wold.conf to a new wold name, a new world is been created. But if delete this old world an add it later again (after server restart) it won´t generate a new world but reports
su minecraft /etc/init.d/minecraft_server start test
Starting Minecraft Server:.
su minecraft /etc/init.d/minecraft_server update
Stopping Minecraft Server: testNo screen session found.
Error sending command to servertest.
In this case i generate a test world. than change to test2 and delete test. After i change back to test it won´t generate the world for test. this occure for all worlds exist before.
Don´t know if this is a script issue, Bukkit or MC
System:
Debian
Multi World starting Bukkit
Thanks for Help in advanced
When you deleted "test" from worlds.conf, did you delete the world (test) folder in /home/minecraft/worlds/ as well?
If you want to remove a world, you need to stop the world first before removing the folder.
/etc/init.d/minecraft_server stop world
The only place world names are stored is in worlds.conf
If you remove a world from worlds.conf, that world's folder is not removed and the files will remain.
All worlds.conf does is tell the script what worlds exist, and it will create the world folder only if the world folder and a server.properties file does not already exist.
I have no idea why the world gets added back to worlds.conf, I didn't think the script could do that.
Yes the hole folder with all contend, as i thought it would be generated new in case a start a world with this name again.
But there seems to be an additional place where all world names are hold.
If i only remove it from the world.conf the files are kept and are used again if the world name is added to the world conf again.
The only place world info is stored is in the /home/minecraft/worlds.conf file and in the /home/minecraft/worlds/<world name>/ directory. If you stop (or maybe better force-stop) the world server, remove the world name from the worlds.conf file, and delete the world directory in /home/minecraft/worlds/ the world will cease to exist.
If you are sure that you have done all three (a server reboot might be in order too), and you are still getting those errors then there might be a problem with the script. Are you running the latest version?
Note: We are talking about dropping the worlds.conf file in a future version of the script, but that hasn't happened yet. I just want you to know that this will get simpler once someone has the time to write the code.
Thanks, the issue happens only on stop, it won´t happen if i use force-stop.
So it now works for me with the force-stop.
A Tip for the Main Post/Guide, you could change this part for actual Java:
apt-get install openjdk-7-jre
And thanks for this great script.
I'm glad that force-stop worked for you. Although it does leave me curious as to how the server got to the state of needing force-stop in the first place (when stop should be enough).
I'll update the text to read 'apt-get default-jre' so that it will work with whatever version Ubuntu/Debian is currently serving.
I'm glad that force-stop worked for you. Although it does leave me curious as to how the server got to the state of needing force-stop in the first place (when stop should be enough).
I'll update the text to read 'apt-get default-jre' so that it will work with whatever version Ubuntu/Debian is currently serving.
Ok, here is a working* version of the script to interface with the query server. It's not quite ready for inclusion in the main script, but as soon as I work out some bugs and get some test data from other people I would like to add its functionality.
* I still seem to be having some encode/decode issues, comments in the script (marked XXX) allude to the problems.
Oh dang, I hope you get feeling better.
I got lucky with wisdom teeth. I only had one, and it never developed into a full tooth. Didn't even know I had it until I had an xray of my head.
I've been looking at your changes to the RAM disk stuff in your version of the script, and feel like I'm coming around to doing things your way rather than how I had them in the script. I liked how I did things because it was simple, but I don't currently see any way to get around the symlink method if we don't want the configuration and log files for the world to be copied over (your method is actually very similar to one used in an old version of this script). I'm all ears if anyone sees a simpler way of accomplishing this, but I'm tempted to go ahead and make this change.
One question though. What is the point of changing the usage of rsync to cp? Won't they perform the same operation with rsync doing less IO overall?
Rsync will only copy over missing and updated files, CP will copy everything.
During a mirror sync, rsync would be faster. During a server startup, cp would be faster if the mirror directory was cleared on server stop (I don't recall if it is).
http://docs.overview...ng-the-textures
It's expecting the jar to be in the subfolder "versions" now.
I'll update the git repository soon
This also make me wonder if there is a text file somewhere that is the current version #, this would solve the server jar download problem.
We could probably even use this to notify when there is an update or even perform auto-updates.
Basically, this. On Debian and Ubuntu and the derivatives thereof, the ramdisk on /dev/shm is empty on startup by default. Since there's nothing to compare against, rsync is slightly slower than cp due to the extra file check it performs, while cp can just straight-up blast bits into place.
Indeed - the new launcher has to have a fixed reference location for version lookups (especially since it now supports having multiple jar versions in one client install), so nothing says we can't sniff that file for news on version changes.
I can't help but wonder what the point will be where we exceed what we can realistically accomplish with shell scripts and have to move to an executable, or perhaps a combination of executable and scripts. Maybe even a jar or something that can also capture console output from running servers. An intermediary executable of some sort with a well-documented API would be the best thing since sliced bread for administration...
Webmaka, I finally got around to adding in your mirror-image changes. I've tested it, and it seems to work. It is not as simple as the old code, so here's hoping there are no corner cases that we missed.
Has anyone gotten around to testing my query server script? Here is the latest version as a patch to the script:
We don't really need the information stored in the worlds.conf file. We can find the world names by listing the directories in the /home/minecraft/worlds directory. These world names can be verified by looking for a server.properties file that we could also use to parse out the port and other needed information.
My one question is: If we store our own values in the server.properties file, do they persist through the normal operation of a server? If not, I propose we at least use the same key=value format in a file called something like server.conf to make it simple.
I just got back from Defcon and I plan on testing testing if additional values persist in a server.properties file and the MC query changes as well soon. But now, sleep...
That happened a long time ago when the first Perl one-liner went in the script. Since a good chunk of the functionality in the script is now hidden away in these one-liners, I wouldn't be that opposed to switching over to Perl entirely. However, the current Shell/Perl combo is working and I don't see any reason to suffer a rewrite unless everyone is on board.
Most of the perl one-liners seem to be just regex pattern matching, since bash doesn't have regex.
I would rather not go full executable since that means users would not be able to tweak and adjust the script to their needs. That said, if this were to move into "real" code, I would not be against it, I would rather keep it open like perl or python and not bundle it as a binary executable.
When you deleted "test" from worlds.conf, did you delete the world (test) folder in /home/minecraft/worlds/ as well?
The only place world names are stored is in worlds.conf
If you remove a world from worlds.conf, that world's folder is not removed and the files will remain.
All worlds.conf does is tell the script what worlds exist, and it will create the world folder only if the world folder and a server.properties file does not already exist.
I have no idea why the world gets added back to worlds.conf, I didn't think the script could do that.
The only place world info is stored is in the /home/minecraft/worlds.conf file and in the /home/minecraft/worlds/<world name>/ directory. If you stop (or maybe better force-stop) the world server, remove the world name from the worlds.conf file, and delete the world directory in /home/minecraft/worlds/ the world will cease to exist.
If you are sure that you have done all three (a server reboot might be in order too), and you are still getting those errors then there might be a problem with the script. Are you running the latest version?
Note: We are talking about dropping the worlds.conf file in a future version of the script, but that hasn't happened yet. I just want you to know that this will get simpler once someone has the time to write the code.
It can't do that. If it's happening, something else is doing it.
I'm glad that force-stop worked for you. Although it does leave me curious as to how the server got to the state of needing force-stop in the first place (when stop should be enough).
I'll update the text to read 'apt-get default-jre' so that it will work with whatever version Ubuntu/Debian is currently serving.
Github wiki page is updated as well