That got me to thinking about ways I could do this automatically as a sort of teleportation. I ended up coming up with this script:
#!C:\Perl\bin\perl -w
use IPC::Open2;
use strict;
use warnings;
my $minecraft_server_path = 'c:\MineCraft';
my $world_name = 'blockatopia';
my $pid = open2(*OUTPUT, *INPUT, 'java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui 2>&1');
while (my $line = <OUTPUT>){
print $line;
if ($line =~ /(\w+) issued server command: ([\w\s]+$)/){
my $player_name = $1;
my $command = $2;
if (defined $command){
if ($command =~ /^save secret location ([0-9a-zA-Z_]+)\s*$/){
my $save_point_name = $1;
#Kick the player so their dat file gets updated
send_command("kick $player_name\n");
#give the sever time to perform the kick
sleep 1;
#Make sure the location directory exists
unless ( -e "$minecraft_server_path\\$world_name\\players\\secret_locations" ) {
system("mkdir $minecraft_server_path\\$world_name\\players\\secret_locations");
}
#Make a copy of that user's .dat file and rename it to the save location listed above
system("copy $minecraft_server_path\\$world_name\\players\\$player_name.dat $minecraft_server_path\\$world_name\\players\\secret_locations\\$save_point_name.dat");
}
if ($command =~ /^save location ([0-9a-zA-Z_]+)\s*$/){
my $save_point_name = $1;
#Kick the player so their dat file gets updated
send_command("kick $player_name\n");
#give the sever time to perform the kick
sleep 1;
#Make sure the location directory exists
unless ( -e "$minecraft_server_path\\$world_name\\players\\location" ) {
system("mkdir $minecraft_server_path\\$world_name\\players\\location");
}
#Make a copy of that user's .dat file and rename it to the save location listed above
system("copy $minecraft_server_path\\$world_name\\players\\$player_name.dat $minecraft_server_path\\$world_name\\players\\location\\$save_point_name.dat");
}
if ($command =~ /^be ([0-9a-zA-Z_]+)\s*$/){
my $save_point_name = $1;
#Kick the player so they'll have to load it when they log back in
send_command("kick $player_name\n");
#give the sever time to perform the kick
sleep 1;
#Overwrite the user's dat file with the player's dat file if it exists
if ( -e "$minecraft_server_path\\$world_name\\players\\$save_point_name.dat" ) {
system("copy $minecraft_server_path\\$world_name\\players\\$save_point_name.dat $minecraft_server_path\\$world_name\\players\\$player_name.dat");
}
else {
send_command("tell $player_name That player does not exist.\n");
}
}
if ($command =~ /^secret warp ([0-9a-zA-Z_]+)\s*$/){
my $save_point_name = $1;
#Kick the player so they'll have to load it when they log back in
send_command("kick $player_name\n");
#give the sever time to perform the kick
sleep 1;
#Make sure the location directory exists
unless ( -e "$minecraft_server_path\\$world_name\\players\\secret_locations" ) {
system("mkdir $minecraft_server_path\\$world_name\\players\\secret_locations");
}
#Overwrite the user's dat file with the saved location dat file if it exists
if ( -e "$minecraft_server_path\\$world_name\\players\\secret_locations\\$save_point_name.dat" ) {
system("copy $minecraft_server_path\\$world_name\\players\\secret_locations\\$save_point_name.dat $minecraft_server_path\\$world_name\\players\\$player_name.dat");
}
else {
send_command("tell $player_name That location does not exist.\n");
}
}
if ($command =~ /^warp ([0-9a-zA-Z_]+)\s*$/){
my $save_point_name = $1;
#Kick the player so they'll have to load it when they log back in
send_command("kick $player_name\n");
#give the sever time to perform the kick
sleep 1;
#Make sure the location directory exists
unless ( -e "$minecraft_server_path\\$world_name\\players\\location" ) {
system("mkdir $minecraft_server_path\\$world_name\\players\\location");
}
#Overwrite the user's dat file with the saved location dat file if it exists
if ( -e "$minecraft_server_path\\$world_name\\players\\location\\$save_point_name.dat" ) {
system("copy $minecraft_server_path\\$world_name\\players\\location\\$save_point_name.dat $minecraft_server_path\\$world_name\\players\\$player_name.dat");
}
else {
send_command("tell $player_name That location does not exist.\n");
}
}
if ($command =~ /^list locations\s*$/){
my $save_point_name = $1;
#give the sever time to perform the kick
sleep 1;
#Make sure the location directory exists
unless ( -e "$minecraft_server_path\\$world_name\\players\\location" ) {
system("mkdir $minecraft_server_path\\$world_name\\players\\location");
}
my $files = `dir /B $minecraft_server_path\\$world_name\\players\\location`;
my @files = split "\n", $files;
foreach my $file (@files) {
$file =~ s/\.dat//g;
send_command("tell $player_name [$file]\n");
}
}
if ($command =~ /^help\s*$/){
#give the server time to finish it's help command
sleep 1;
#Now send our help information
send_command("tell $player_name /save location name_of_place\n");
send_command("tell $player_name /warp name_of_place\n");
send_command("tell $player_name /list locations\n");
}
}
}
if ($line =~ /poop/g){
send_command("say Not allowed to say poop!\n");
print "[Wrapper sent]say You said poop!\n";
}
}
sub send_command {
my $command = shift;
print INPUT $command;
print "[Wrapper sent]$command";
}
This script is a wrapper around the actual minecraft executable. It uses a module to read the output of the server (all of player's entered commands and chatting) and based on strings that it sees, sends commands back to the server along with copying and moving files around on the server side. Once this script is running your minecraft server, you can execute the following commands in game by hitting 't' and then one of the commands below:
/save secret location name_of_warp_location
/save location name_of_warp_location
/secret warp name_of_warp_location
/warp name_of_warp_location
/be player_name
/list locations
Command Definitions are as follows:
/save secret location name_of_warp_location - saves the current location (and inventory) of the player under the name specified by "name_of_warp_location", but does not add this location to the public list. (See the /list command below)
/save location name_of_warp_location - saves the current location (and inventory) of the player under the name specified by "name_of_warp_location", and adds it to the public locations list (See the /list command below)
/secret warp name_of_warp_location - teleports the current player to the location specified (and gives them the inventory saved at that location). Only works with secret locations
/warp name_of_warp_location - teleports the current player to the location specified (and gives them the inventory saved at that location). Only works with non-secret locations
/be player_name - teleports the current player to the last known location of player_name (and gives current player a copy of everything that was in player_name's inventory)
/list locations - lists all publicly saved locations on this server. (Also lists the users currently logged on because this command piggy-backs the normal /list command)
NOTE: All of the "save location" commands will perform a kick of the player that performs them in order to force the players location to be saved in the back-end file before it gets copied. Don't freak out when you get kicked, just log back in and the location will be in /list locations. Also, all of the "warp" commands will kick a player before copying their file because the login process is the only way that I know of to load a player.dat file back into a player :).
Now, the more complicated problem of making Perl run on your windows machine is a different story. I used the Active Perl installation from the folks over at ActiveState.com in order to get it running on my machine and it was fairly straight-forward. If not, well..there are plenty of howto's on the internet that can help you out with that ;). Once Perl's installed, just modify these two variables to match your Minecraft server location and world name:
my $minecraft_server_path = 'c:\MineCraft';
my $world_name = 'blockatopia';
For those of you who have a nice Linux box that will run the Minecraft server worth a darn (Sadly, mine is in dire need of an upgrade) this script should work just fine for you as long as you reverse all of the \\ to //. I didn't feel like putting the time into this to make it truly cross-platform... my bad ;).
Hope this helps you all enjoy Minecraft a little more. It's certainly made my time more "productive"...wait...scratch that ;)
Hey I want to ask you a question. This post was cool, but I'm pursuing something different.
ReplyDeleteI'm interested in figuring out how to issue commands into the JVM session itself via a command execution from outside the environment.
Specific use case: seeking how to leverage an external log management tool to detect and correlate certain logged events, and respond with an execution response issuing commands utilizing variables set from the logged event.
For example: define a certain area, and someone tries to grief it. Logged event! Event log is parsed, event is normalized, correlated, and response execution utilizes username to execute a megastrike.
Wie? Because, like you, I am a nerd. Also, a father. Five kids. 15, 13, 3, and two twin girls, aged four months. In other words; NERD