I was thinking it would be nice to have a proxy built into the script. Here is my work-in-progress script that only currently functions as a proxy between two ports. It does have a couple of simple parsers to output the traffic to the screen, but not much else yet. The ultimate goal would be to allow users to jump between servers with a command, but that is a long ways from be realized.
#!/bin/sh
CLIENT_PORT=25565
SERVER_PORT=25566
SERVER_IP=127.0.0.1
BACK_PIPE=pipe.back
CLIENT_PIPE=pipe.client
SERVER_PIPE=pipe.server
rm -Rf "$BACK_PIPE" "$CLIENT_PIPE" "$SERVER_PIPE"
mkfifo -m 0600 "$BACK_PIPE" "$CLIENT_PIPE" "$SERVER_PIPE"
while true; do
# Parse the client and server pipes.
perl -ne '
$raw = $_;
$hex .= sprintf "%.2x", $_ foreach (unpack "C*", $raw);
print "\nClient - " . $raw . "\n" . $hex . "\n";
' "$CLIENT_PIPE" &
perl -ne '
$raw = $_;
$hex .= sprintf "%.2x", $_ foreach (unpack "C*", $raw);
print "\nServer - " . $raw . "\n" . $hex . "\n";
' "$SERVER_PIPE" &
# Listen for the next connection.
nc -l $CLIENT_PORT < "$BACK_PIPE" | tee -a "$CLIENT_PIPE" |
nc -q 1 $SERVER_IP $SERVER_PORT | tee -a "$SERVER_PIPE" > "$BACK_PIPE"
done
I might be able to get around to working on it, I've been very busy the last month or so. Haven't had much time for extra projects. Things seem to be slowing down though.
I was able to get my MC server back up and running, so I've been playing with the code.
I've placed a new branch called 'world-admin' on github that has the create/delete and disable/enable options on the command line. The create world logic is also used to restore the ability of the script to function on its first run by creating a default world if no others are found. This branch might need some more work before I consider it done, and it definitely needs some more testing before I push it to master.
I haven't gotten around to adding in a auto port or name generator, but that logic could easily be added in now.
I think we could scan the current active worlds .properties file for the next available port.
I guess we could come up with some logic that would find the highest used port number in a list of active worlds, then increment that by one if the port was not supplied on the command line. In the case of no active worlds, the default port would be used. We could create default world names by starting with 'world', then 'world1', 'world2', etc, but maybe it is best to require at least one argument to the create world command.
My MC server died, hopefully the hdd is still ok. I'm not going to be able to test anything until I get one of my other boxes set up as a server, so I probably won't make any changes to the script until I can.
Yeah, I don't think the command line need confirmations. Just do it.
I would keep the active worlds in worlds/ and create the worlds-disabled/ directory. It should be pretty obvious what each are.
I'm still working on a backup/restore interface. It's been kinda slow since I'm still learning how to use option.
If you need me to review code, feel free to post it here. I check this thread periodically, but infrequently when there is no activity. I check more often when I notice the activity. Maybe I should set the forum to email me when there is a new post in this thread (if that is possible).
Creating a world might be a little more involved than the command line might be able to handle. We need to know the world name, port, and IP address (usually blank) to populate an initial server.properties file. Although, a command like this might work:
I knew it was the Perl command in the querySendPacket function, just had no idea how to fix it.
Now that we have no worlds.conf file, we need a command to add / disable / remove worlds
Yeah, you were right. The command worked fine with sudo, but when run as a user it broke on the unescaped quotes. I usually do my testing with sudo from a different user and only occasionally test after running the command 'sudo su minecraft'.
I agree, the script needs a way to create/delete and enable/disable worlds. In my mind the create and delete functions are pretty obvious, but I need to figure out how to ask the user questions from the shell script (ie. Are you really sure you want to delete your world? What would you like to name your new world?). The enable and disable functions would also require extra user input, but I need to think about how we want to do this. In my note above I mentioned moving disabled worlds to a worlds-disabled directory, which I could easily codify. The question is, do we want to rename the worlds directory to worlds-enabled to make it more obvious or leave it the way it is for backwards compatibility?
edit: On second thought, maybe the script should just do what it is told on the command line and not worry about the consequences. If a web-facing app is ever linked to this script, it could take care of the 'Are you sure?' type of questions.
I went ahead and removed the worlds.conf file. All worlds in the /home/minecraft/worlds directory will now be controllable by this script. If you have any disabled worlds in that directory, I suggest that you move them to a different directory (say worlds-disabled), because they will now be considered active.
With the new version of the controlscript I keep getting this error
I downloaded the script from the link in the first post here 10th of september and again today. I ran a diff and they are the same. Any hints to what could be wrong?
Running on a debian server with all packages needed installed and updated to latest version. The same server ran the control script for version minecraft 1.5 without any problem. This started happening when I upgraded to this version of the script and v1.6.2
And since this is my first post I think I should also mention I have run the script ever since I started the first minecraft server. I have been really happy with it and hope you keep up the great work :-)
Those are some weird errors; I'm not sure what is causing them.
minecraft@minecraft:~$ /etc/init.d/minecraft_server start world
Starting Minecraft Server:.
This leads me to believe that your worlds.conf is malformed. Could you post a copy? Also, what happens if you omit the world name?
minecraft@minecraft:~$ /etc/init.d/minecraft_server status creative
Minecraft Server Status:
creative: Bareword found where operator expected at -e line 2, near "/etc/init"
(Missing operator before nit?)
syntax error at -e line 2, near "/etc/init"
Execution of -e aborted due to compilation errors.
I'm confused by the "/etc/init" bit, the script does not make any calls to itself and the only time the variable $0 is used is for printing error messages.
If you figure out the cause, let me know and I can add code to make this kind of issue easier to diagnose in the future.
edit: I was able to reproduce the problem and track it down. The fix has been posted.
I haven't had the opportunity to test the query branch yet mainly due to life
I have been able to look it over and it looks good on paper. Guess users of the script will be the testers
No worries.
I am seeing one potential issue. The query server doesn't always respond, and the retry while loop doesn't seem to do the trick, at least in some cases. When I get the chance, I'll try to find a solution.
Although I never received any test reports for my query branch, I decided to go ahead and merge the branch. It has been stable for me, so hopefully this doesn't cause any problems for anyone.
Nice, it is a little long though.
Here's my ugly perl version with a slightly different countdown interval pattern (the foreach line).
This could be done in shell just as easily using the same logic. If we want to eventually switch to perl, then I say we go with something like this code. Otherwise, I can come up with a shell only patch that does the same thing. I'm a perl programmer, so I find it easier to prototype a function using perl.
edit: I went ahead and translated the perl into shell. It is harder to compress shell down, so it's not much of a savings over your version.
#!/bin/sh
countdownPerl() {
perl -e '
my @interval = (864000, 86400, 21600, 3600, 600, 60, 10, 1);
my $c = $ARGV[1];
while ($c) {
# Calculate the number of days, hours, minutes, and seconds remaining.
my $d = int ($c / 86400); # 60*60*24 seconds in a day.
my $h = ($c / 3600) % 24; # 60*60 seconds in a hour.
my $m = ($c / 60) % 60; # 60 seconds in a minute.
my $s = $c % 60;
# Calculate the interval until the next countdown warning.
foreach (@interval) {
if ($c > $_) { $i = ($c % $_ ? $c % $_ : $_); last; }
}
# Start off with an empty time text.
my $t = "";
# Add the days remaining to the time text.
if ($d) { $t .= sprintf "%d day", $d; $t .= "s" if ($d > 1); }
# Handle cases where there are hours remaining, use these up first.
if ($d && $h) { $t .= ", "; $i = $h * 3600; }
# Add the hours remaining to the time text.
if ($h) { $t .= sprintf "%d hour", $h; $t .= "s" if ($h > 1); }
# Handle cases where there are minutes remaining, use these up first.
if (($d || $h) && $m) { $t .= ", "; $i = $m * 60; }
# Add the minutes remaining to the time text.
if ($m) { $t .= sprintf "%d minute", $m; $t .= "s" if ($m > 1); }
# Handle cases where there are seconds remaining, use these up first.
if (($d || $h || $m) && $s) { $t .= ", "; $i = $s; }
# Add the seconds remaining to the time text.
if ($s) { $t .= sprintf "%d second", $s; $t .= "s" if ($s > 1); }
# Output the countdown message and sleep for the calculated interval.
printf "$ARGV[0]\n", $t;
sleep $i;
$c -= $i;
}
' "$1" $2
}
countdownShell() {
local C D H M S T I J INTERVAL
INTERVAL="864000 86400 21600 3600 600 60 10 1"
C=$2
while [ $C -gt 0 ]; do
# Calculate the number of days, hours, minutes, and seconds remaining.
D=$(($C / 86400)) # 60*60*24 seconds in a day.
H=$((($C / 3600) % 24)) # 60*60 seconds in a hour.
M=$((($C / 60) % 60 )) # 60 seconds in a minute.
S=$(($C % 60))
# Calculate the interval until the next countdown warning.
for J in $INTERVAL; do
if [ $C -gt $J ]; then
if [ $(($C % $J)) -gt 0 ]; then
I=$(($C % $J))
else
I=$J
fi
break
fi
done
# Start off with an empty time text.
T="";
# Add the days remaining to the time text.
if [ $D -gt 0 ]; then
T=$(printf "%s%d day" "$T" $D)
if [ $D -gt 1 ]; then
T=$(printf "%ss" "$T")
fi
fi
# Handle cases where there are hours remaining, use these up first.
if [ $D -gt 0 ] && [ $H -gt 0 ]; then
T=$(printf "%s, " "$T")
I=$(($H * 3600))
fi
# Add the hours remaining to the time text.
if [ $H -gt 0 ]; then
T=$(printf "%s%d hour" "$T" $H)
if [ $H -gt 1 ]; then
T=$(printf "%ss" "$T")
fi
fi
# Handle cases where there are minutes remaining, use these up first.
if [ $D -gt 0 ] || [ $H -gt 0 ] && [ $M -gt 0 ]; then
T=$(printf "%s, " "$T")
I=$(($M * 60))
fi
# Add the minutes remaining to the time text.
if [ $M -gt 0 ]; then
T=$(printf "%s%d minute" "$T" $M)
if [ $M -gt 1 ]; then
T=$(printf "%ss" "$T")
fi
fi
# Handle cases where there are seconds remaining, use these up first.
if [ $D -gt 0 ] || [ $H -gt 0 ] || [ $M -gt 0 ] && [ $S -gt 0 ]; then
T=$(printf "%s, " "$T")
I=$S
fi
# Add the seconds remaining to the time text.
if [ $S -gt 0 ]; then
T=$(printf "%s%d second" "$T" $S)
if [ $S -gt 1 ]; then
T=$(printf "%ss" "$T")
fi
fi
# Output the countdown message and sleep for the calculated interval.
printf "$1\n" "$T"
sleep $I
C=$(($C - $I))
done
}
#countdownPerl "$1" $2
countdownShell "$1" $2
edit: Reduce the number of operations in the calculation of day, hours, minutes, and seconds remaining.
It will take a message and time left in seconds as parameters.
It will only print messages at certain intervals
Countdown longer than 5 minutes, print message at 5 minute intervals
Countdown longer than 1 minute, print message at 1 minute intervals
Countdown less than 1 minute, print message at 10 second intervals
Countdown less than 15 seconds, print message every second
It's kinda ugly but I wanted to get the logic down.
Nice, it is a little long though.
Here's my ugly perl version with a slightly different countdown interval pattern (the foreach line).
#!/bin/sh
countdown() {
perl -e '
my $c = $ARGV[1];
while ($c) {
# Calculate the number of days, hours, minutes, and seconds remaining.
my $d = int ($c / 86400);
my $h = int (($c - ($d * 86400)) / 3600);
my $m = int (($c - ($d * 86400) - ($h * 3600)) / 60);
my $s = $c % 60;
# Calculate the interval until the next countdown warning.
foreach (864000, 86400, 21600, 3600, 600, 60, 10, 1) {
if ($c > $_) { $i = ($c % $_ ? $c % $_ : $_); last; }
}
# Start off with an empty time text.
my $t = "";
# Add the days remaining to the time text.
if ($d) { $t .= sprintf "%d day", $d; $t .= "s" if ($d > 1); }
# Handle cases where there are hours remaining, use these up first.
if ($d && $h) { $t .= ", "; $i = $h * 3600; }
# Add the hours remaining to the time text.
if ($h) { $t .= sprintf "%d hour", $h; $t .= "s" if ($h > 1); }
# Handle cases where there are minutes remaining, use these up first.
if (($d || $h) && $m) { $t .= ", "; $i = $m * 60; }
# Add the minutes remaining to the time text.
if ($m) { $t .= sprintf "%d minute", $m; $t .= "s" if ($m > 1); }
# Handle cases where there are seconds remaining, use these up first.
if (($d || $h || $m) && $s) { $t .= ", "; $i = $s; }
# Add the seconds remaining to the time text.
if ($s) { $t .= sprintf "%d second", $s; $t .= "s" if ($s > 1); }
# Output the countdown message and sleep for the calculated interval.
printf "$ARGV[0]\n", $t;
sleep $i;
$c -= $i;
}
' "$1" $2
}
countdown "$1" $2
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.
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.
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.
0
0
I was able to get my MC server back up and running, so I've been playing with the code.
I've placed a new branch called 'world-admin' on github that has the create/delete and disable/enable options on the command line. The create world logic is also used to restore the ability of the script to function on its first run by creating a default world if no others are found. This branch might need some more work before I consider it done, and it definitely needs some more testing before I push it to master.
I haven't gotten around to adding in a auto port or name generator, but that logic could easily be added in now.
0
I guess we could come up with some logic that would find the highest used port number in a list of active worlds, then increment that by one if the port was not supplied on the command line. In the case of no active worlds, the default port would be used. We could create default world names by starting with 'world', then 'world1', 'world2', etc, but maybe it is best to require at least one argument to the create world command.
My MC server died, hopefully the hdd is still ok. I'm not going to be able to test anything until I get one of my other boxes set up as a server, so I probably won't make any changes to the script until I can.
0
If you need me to review code, feel free to post it here. I check this thread periodically, but infrequently when there is no activity. I check more often when I notice the activity. Maybe I should set the forum to email me when there is a new post in this thread (if that is possible).
Creating a world might be a little more involved than the command line might be able to handle. We need to know the world name, port, and IP address (usually blank) to populate an initial server.properties file. Although, a command like this might work:
This would have to create an error if either world or port is not specified (or uses default values that are already in use).
0
Yeah, you were right. The command worked fine with sudo, but when run as a user it broke on the unescaped quotes. I usually do my testing with sudo from a different user and only occasionally test after running the command 'sudo su minecraft'.
I agree, the script needs a way to create/delete and enable/disable worlds. In my mind the create and delete functions are pretty obvious, but I need to figure out how to ask the user questions from the shell script (ie. Are you really sure you want to delete your world? What would you like to name your new world?). The enable and disable functions would also require extra user input, but I need to think about how we want to do this. In my note above I mentioned moving disabled worlds to a worlds-disabled directory, which I could easily codify. The question is, do we want to rename the worlds directory to worlds-enabled to make it more obvious or leave it the way it is for backwards compatibility?
edit: On second thought, maybe the script should just do what it is told on the command line and not worry about the consequences. If a web-facing app is ever linked to this script, it could take care of the 'Are you sure?' type of questions.
0
0
Those are some weird errors; I'm not sure what is causing them.
This leads me to believe that your worlds.conf is malformed. Could you post a copy? Also, what happens if you omit the world name?
I'm not sure what is causing this error:
I'm confused by the "/etc/init" bit, the script does not make any calls to itself and the only time the variable $0 is used is for printing error messages.
If you figure out the cause, let me know and I can add code to make this kind of issue easier to diagnose in the future.
edit: I was able to reproduce the problem and track it down. The fix has been posted.
0
No worries.
I am seeing one potential issue. The query server doesn't always respond, and the retry while loop doesn't seem to do the trick, at least in some cases. When I get the chance, I'll try to find a solution.
0
0
This could be done in shell just as easily using the same logic. If we want to eventually switch to perl, then I say we go with something like this code. Otherwise, I can come up with a shell only patch that does the same thing. I'm a perl programmer, so I find it easier to prototype a function using perl.
edit: I went ahead and translated the perl into shell. It is harder to compress shell down, so it's not much of a savings over your version.
edit: Reduce the number of operations in the calculation of day, hours, minutes, and seconds remaining.
0
Nice, it is a little long though.
Here's my ugly perl version with a slightly different countdown interval pattern (the foreach line).
0
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.
0
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.
0
0
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.