More work on player operations

This commit is contained in:
Jamie Cameron
2012-12-09 19:58:42 -08:00
parent 17ddea0114
commit ec0b8b45b4
16 changed files with 561 additions and 4 deletions

35
minecraft/command.cgi Normal file
View File

@ -0,0 +1,35 @@
#!/usr/local/bin/perl
# Show a field for running a console command
use strict;
use warnings;
require './minecraft-lib.pl';
our (%in, %text);
&ReadParse();
my @history = &get_command_history();
$in{'command'} ||= $in{'old'};
if ($in{'command'}) {
# Run the given command
&send_server_command($in{'command'});
@history = &unique($in{'command'}, @history);
while(@history > 10) {
pop(@history);
}
&save_command_history(\@history);
}
&PrintHeader();
print "<body onLoad='document.forms[0].command.focus()'>\n";
print &ui_form_start("command.cgi", "post");
my @grid = ( "<b>$text{'console_run'}</b>",
&ui_textbox("command", undef, 80)." ".
&ui_submit($text{'console_ok'}) );
if (@history) {
push(@grid, "<b>$text{'console_old'}</b>",
&ui_select("old", undef, \@history));
}
print &ui_grid_table(\@grid, 2);
print &ui_form_end();
print "</body>\n";

17
minecraft/console.cgi Normal file
View File

@ -0,0 +1,17 @@
#!/usr/local/bin/perl
# Show the console, with a command field
use strict;
use warnings;
require './minecraft-lib.pl';
our (%in, %text, %config);
&ReadParse();
&ui_print_header(undef, $text{'console_title'}, "");
&is_minecraft_server_running() || &error($text{'console_edown'});
print "<iframe src=output.cgi width=100% height=70%></iframe>\n";
print "<iframe src=command.cgi width=100% height=70 border=0></iframe>\n";
&ui_print_footer("", $text{'index_return'});

BIN
minecraft/images/conns.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
minecraft/images/items.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -14,11 +14,14 @@ if ($err) {
}
my @links = ( "edit_conf.cgi", "edit_users.cgi",
"view_logs.cgi", "edit_manual.gif" );
"view_logs.cgi", "list_conns.cgi",
"console.cgi", "edit_manual.gif" );
my @titles = ( $text{'conf_title'}, $text{'users_title'},
$text{'logs_title'}, $text{'manual_title'} );
$text{'logs_title'}, $text{'conns_title'},
$text{'console_title'}, $text{'manual_title'} );
my @icons = ( "images/conf.gif", "images/users.gif",
"images/logs.gif", "images/manual.gif" );
"images/logs.gif", "images/conns.gif",
"images/console.gif", "images/manual.gif" );
&icons_table(\@links, \@titles, \@icons);
print &ui_hr();

View File

@ -0,0 +1,52 @@
#!/usr/local/bin/perl
# Output a list for choosing a Minecraft item
use strict;
use warnings;
our $trust_unknown_referers = 1;
require './minecraft-lib.pl';
our (%text, %in);
&ReadParse(undef, undef, 2);
&popup_header($text{'chooser_title'});
print "<script>\n";
print "function select(f)\n";
print "{\n";
print "top.opener.ifield.value = f;\n";
print "top.close();\n";
print "return false;\n";
print "}\n";
print "</script>\n";
# Show all items
print &ui_form_start("item_chooser.cgi");
print "<b>$text{'chooser_search'}</b> ",
&ui_textbox("search", $in{'search'}, 20)," ",
&ui_submit($text{'chooser_ok'});
print &ui_form_end(),"<br>\n";
# Get the item list, and apply search
my @items = &list_minecraft_items();
if ($in{'search'}) {
@items = grep { $_->{'name'} =~ /\Q$in{'search'}\E/i } @items;
}
if (@items) {
print &ui_columns_start([ $text{'chooser_id'},
$text{'chooser_name'} ]);
foreach my $i (@items) {
print &ui_columns_row([
"<a href='' onClick='return select(\"$i->{'id'}\")'>".
$i->{'id'}."</a>",
&html_escape($i->{'name'}),
]);
}
print &ui_columns_end();
}
else {
print "<b>$text{'chooser_none'}</b><p>\n";
}
&popup_footer();

View File

@ -18,9 +18,59 @@ conf_title=Server Configuration
users_title=Users and Operators
logs_title=View Log File
conns_title=Connected Players
conns_desc=The following players are currently connected to your server. Click on a player name to perform actions on it.
conns_edown=Connected players cannot be managed unless the server is running
conns_none=No players are currently connected to the server.
conns_disc=Disconnect Selected
conns_enter=Manage player named:
conns_ok=OK
conns_return=player list
logs_title=Search Log File
logs_lines=Show last
logs_matching=lines matching
logs_ok=Search
console_title=Server Console
console_run=Enter command
console_ok=OK
console_old=or select previous
console_edown=The Minecraft server console cannot be used unless the server is running
conn_title=Manage Player
conn_header=Player details and actions
conn_name=Player name
conn_state=Current state
conn_yes=Connected
conn_no=Disconnected
conn_lastin=Logged in from
conn_lastin2=Last logged in from
conn_lastout=Logged out at
conn_at=$1 at $2
conn_msg=Send direct message
conn_pos=Position at login
conn_msgb=Send
conn_kill=Terminate player
conn_killb=Kill Now
conn_ename=Missing or invalid player name
conn_give=Grant item with ID
conn_count=count
conn_giveb=Give
conn_never=This player has not logged into the server recently, and may not even exist.
conn_err=Player action failed
conn_etext=Missing message text
conn_msgdone=Sent message to player
conn_killdone=Killed player
conn_eid=Missing or invalid item ID
conn_ecount=Missing or non-numeric item count
conn_givedone=Gave $2 of item $1 to player
chooser_title=Select Item
chooser_id=Item ID
chooser_name=Item name
chooser_none=No items matched your search
chooser_search=Show items matching
chooser_ok=Search
manual_title=Edit Configuration File

41
minecraft/list_conns.cgi Normal file
View File

@ -0,0 +1,41 @@
#!/usr/local/bin/perl
# Show all connected players
use strict;
use warnings;
require './minecraft-lib.pl';
our (%in, %text, %config);
&ui_print_header(undef, $text{'conns_title'}, "");
&is_minecraft_server_running() || &error($text{'conns_edown'});
my @conns = &list_connected_players();
if (@conns) {
print $text{'conns_desc'},"<p>\n";
my @grid;
@grid = map { &ui_checkbox("d", $_)." ".
"<a href='view_conn.cgi?name=".&urlize($_)."'>".
&html_escape($_)."</a>" } @conns;
print &ui_form_start("mass_conns.cgi", "post");
my @links = ( &select_all_link("d"),
&select_invert_link("d") );
print &ui_links_row(\@links);
print &ui_grid_table(\@grid, 8, "100%");
print &ui_links_row(\@links);
print &ui_form_end([
[ "disc", $text{'conns_disc'} ],
]);
}
else {
print "<b>$text{'conns_none'}</b><p>\n";
}
print &ui_form_start("view_conn.cgi");
print "<b>$text{'conns_enter'}</b> ",
&ui_textbox("name", undef, 20)." ".
&ui_submit($text{'conns_ok'});
print &ui_form_end();
&ui_print_footer("", $text{'index_return'});

View File

@ -8,10 +8,12 @@ BEGIN { push(@INC, ".."); };
use strict;
use warnings;
use WebminCore;
use Time::Local;
&init_config();
our ($module_root_directory, %text, %gconfig, $root_directory, %config,
$module_name, $remote_user, $base_remote_user, $gpgpath,
$module_config_directory, @lang_order_list, @root_directories);
our $history_file = "$module_config_directory/history.txt";
# check_minecraft_server()
# Returns an error message if the Minecraft server is not installed
@ -69,4 +71,204 @@ close($fh);
return \@rv;
}
# get_start_command()
# Returns a command to start the server
sub get_start_command
{
my $jar = $config{'minecraft_jar'} ||
$config{'minecraft_dir'}."/"."minecraft_server.jar";
my $ififo = &get_input_fifo();
my $rv = "(test -e ".$ififo." || mkfifo ".$ififo.") ; ".
"cd ".$config{'minecraft_dir'}." && ".
"(tail -f ".$ififo." | ".
&has_command($config{'java_cmd'})." ".
$config{'java_args'}." ".
" -jar ".$jar." nogui ".
$config{'jar_args'}." ".
">> server.out 2>&1 )";
return $rv;
}
sub get_output_fifo
{
return $config{'minecraft_dir'}."/output.fifo";
}
sub get_input_fifo
{
return $config{'minecraft_dir'}."/input.fifo";
}
# start_minecraft_server()
# Launch the minecraft server in the background
sub start_minecraft_server
{
my $cmd = &get_start_command();
&system_logged("$cmd &");
sleep(1);
if (!&is_minecraft_server_running()) {
my $out = &backquote_command(
"tail -2 ".$config{'minecraft_dir'}."/server.out");
return $out || "Unknown error - no output produced";
}
return undef;
}
# stop_minecraft_server()
# Kill the server, if running
sub stop_minecraft_server
{
my $pid = &is_minecraft_server_running();
$pid || return "Not running!";
# Try graceful shutdown
&send_server_command("stop");
for(my $i=0; $i<10; $i++) {
last if (!&is_minecraft_server_running());
sleep(1);
}
# Clean kill
if (&is_minecraft_server_running()) {
kill('TERM', $pid);
for(my $i=0; $i<10; $i++) {
last if (!&is_minecraft_server_running());
sleep(1);
}
}
# Fatal kill
if (&is_minecraft_server_running()) {
kill('KILL', $pid);
}
return undef;
}
# send_server_command(command)
# Just sends a command to the server
sub send_server_command
{
my ($cmd) = @_;
my $ififo = &get_input_fifo();
my $fh = "FIFO";
&open_tempfile($fh, ">$ififo", 1, 1, 1);
&print_tempfile($fh, $cmd."\n");
&close_tempfile($fh);
}
# execute_minecraft_command(command)
# Run a command, and return output from the server log
sub execute_minecraft_command
{
my ($cmd) = @_;
my $logfile = $config{'minecraft_dir'}."/server.log";
my $fh = "LOG";
&open_readfile($fh, $logfile);
seek($fh, 0, 2);
my $pos = tell($fh);
&send_server_command($cmd);
for(my $i=0; $i<10; $i++) {
sleep(1);
my @st = stat($logfile);
last if ($st[7] > $pos);
}
my $out;
while(<$fh>) {
$out .= $_;
}
close($fh);
return wantarray ? split(/\r?\n/, $out) : $out;
}
# get_command_history()
# Returns the history of commands run
sub get_command_history
{
my $lref = &read_file_lines($history_file);
return @$lref;
}
# save_command_history(&commands)
sub save_command_history
{
my ($cmds) = @_;
my $lref = &read_file_lines($history_file);
@$lref = @$cmds;
&flush_file_lines($history_file);
}
# list_connected_players()
# Returns a list of players currently online
sub list_connected_players
{
my @out = &execute_minecraft_command("/list");
my @rv;
foreach my $l (@out) {
if ($l =~ /\[INFO\]\s+(\S+)$/) {
push(@rv, $1);
}
}
return @rv;
}
# get_login_logout_times(player)
# Returns the last login IP, time, X, Y, Z and logout time (if any)
sub get_login_logout_times
{
my ($name) = @_;
my ($ip, $intime, $xx, $yy, $zz, $outtime);
my $logfile = $config{'minecraft_dir'}."/server.log";
my $fh = "TAIL";
&open_execute_command($fh, "tail -10000 $logfile", 1, 1);
while(<$fh>) {
if (/^(\d+)\-(\d+)\-(\d+)\s+(\d+):(\d+):(\d+)\s+\[\S+\]\s+(.*)/) {
my ($y, $mo, $d, $h, $m, $s, $msg) =($1, $2, $3, $4, $5, $6, $7);
if ($msg =~ /^\Q$name\E\[.*\/([0-9\.]+):(\d+)\]\s+logged\s+in.*\((\-?[0-9\.]+),\s+(\-?[0-9\.]+),\s+(\-?[0-9\.]+)\)/) {
# Login message
$ip = $1;
($xx, $yy, $zz) = ($3, $4, $5);
$intime = &parse_log_time($y, $m, $d, $h, $mo, $s);
}
elsif ($msg =~ /^\Q$name\E\s+lost/) {
# Logout message
$outtime = &parse_log_time($y, $m, $d, $h, $mo, $s);
}
}
}
close($fh);
return ( $ip, $intime, $xx, $yy, $zz, $outtime );
}
sub parse_log_time
{
my ($y, $m, $d, $h, $mo, $s) = @_;
return timelocal($s, $m, $h, $d, $mo-1, $y-1900);
}
# item_chooser_button(fieldname)
sub item_chooser_button
{
my ($field) = @_;
return "<input type=button onClick='ifield = document.forms[0].$field; chooser = window.open(\"item_chooser.cgi?item=\"+escape(ifield.value), \"chooser\", \"toolbar=no,menubar=no,scrollbars=yes,width=400,height=600\"); chooser.ifield = ifield; window.ifield = ifield' value=\"...\">\n";
}
# list_minecraft_items()
# Returns a list of hash refs with id and name keys
# CSV generated with :
# wget -O - http://minecraft-ids.grahamedgecombe.com/ | grep /items/ | perl -ne 's/.*items.([0-9:]+)">([^<]+)<.*/$1,$2/; print ' > items.csv
sub list_minecraft_items
{
my $fh = "ITEMS";
&open_readfile($fh, "$module_root_directory/items.csv");
my @rv;
while(<$fh>) {
s/\r|\n//g;
my ($id, $name) = split(/,/, $_);
push(@rv, { 'id' => $id,
'name' => $name });
}
close($fh);
return @rv;
}
1;

18
minecraft/output.cgi Executable file
View File

@ -0,0 +1,18 @@
#!/usr/local/bin/perl
# Tail the console log
use strict;
use warnings;
require './minecraft-lib.pl';
our (%config);
my $logfile = $config{'minecraft_dir'}."/server.log";
$| = 1;
print "Content-type: text/plain\n\n";
my $fh = "OUT";
&open_execute_command($fh, "tail -20f ".$logfile, 1, 1);
select($fh); $| = 1; select(STDOUT);
while(<$fh>) {
print $_;
}
close($fh);

42
minecraft/save_conn.cgi Normal file
View File

@ -0,0 +1,42 @@
#!/usr/local/bin/perl
# Perform some action on a player
use strict;
use warnings;
require './minecraft-lib.pl';
our (%in, %text);
&ReadParse();
&error_setup($text{'conn_err'});
my $msg;
if ($in{'msg'}) {
# Send a message
$in{'text'} =~ /\S/ || &error($text{'conn_etext'});
&send_server_command("/say $in{'name'} $in{'text'}");
$msg = $text{'conn_msgdone'};
}
elsif ($in{'kill'}) {
# Kill this player
&send_server_command("/kill $in{'name'}");
$msg = $text{'conn_killdone'};
}
elsif ($in{'give'}) {
# Give an item
$in{'id'} =~ /^\d+$/ || &error($text{'conn_eid'});
$in{'count'} =~ /^\d+$/ || &error($text{'conn_ecount'});
my ($i) = grep { $_->{'id'} eq $in{'id'} }
&list_minecraft_items();
my $out = &execute_minecraft_command(
"/give $in{'name'} $in{'id'} $in{'count'}");
&error($out);
$msg = &text('conn_givedone', $i ? $i->{'name'} : $in{'id'},
$in{'count'});
}
else {
# No button clicked!
&error($text{'conn_ebutton'});
}
&redirect("view_conn.cgi?name=".&urlize($in{'name'})."&msg=".
&urlize($in{'msg'}));

12
minecraft/start.cgi Normal file
View File

@ -0,0 +1,12 @@
#!/usr/local/bin/perl
# Start the Minecraft server
use strict;
use warnings;
require './minecraft-lib.pl';
our (%text);
&error_setup($text{'start_err'});
my $err = &start_minecraft_server();
&error("<tt>".&html_escape($err)."</tt>") if ($err);
&webmin_log("start");
&redirect("");

12
minecraft/stop.cgi Executable file
View File

@ -0,0 +1,12 @@
#!/usr/local/bin/perl
# Kill the running minecraft server process
use strict;
use warnings;
require './minecraft-lib.pl';
our (%text);
&error_setup($text{'stop_err'});
my $err = &stop_minecraft_server();
&error($err) if ($err);
&webmin_log("stop");
&redirect("");

72
minecraft/view_conn.cgi Normal file
View File

@ -0,0 +1,72 @@
#!/usr/local/bin/perl
# Show details of a player, with buttons to perform actions
use strict;
use warnings;
require './minecraft-lib.pl';
our (%in, %text);
&ReadParse();
&ui_print_header(undef, $text{'conn_title'}, "");
$in{'name'} =~ /^\S+$/ || &error($text{'conn_ename'});
print &ui_form_start("save_conn.cgi", "post");
print &ui_hidden("name", $in{'name'});
print &ui_table_start($text{'conn_header'}, undef, 2);
# Player name
print &ui_table_row($text{'conn_name'},
&html_escape($in{'name'}));
# Current state
my @conns = &list_connected_players();
my ($c) = grep { $_ eq $in{'name'} } @conns;
print &ui_table_row($text{'conn_state'},
$c ? $text{'conn_yes'} : "<font color=red>$text{'conn_no'}</font>");
# Last login IP and time
my ($ip, $intime, $x, $y, $z, $outtime) = &get_login_logout_times($in{'name'});
if ($ip) {
print &ui_table_row($c ? $text{'conn_lastin'} : $text{'conn_lastin2'},
&text('conn_at', $ip, &make_date($intime)));
print &ui_table_row($text{'conn_pos'},
"X:$x Y:$y Z:$z");
}
if (!$c && $outtime) {
# Logged out at
print &ui_table_row($text{'conn_lastout'},
&make_date($outtime));
}
if ($c || 1) {
print &ui_table_hr();
# Send message
print &ui_table_row($text{'conn_msg'},
&ui_textbox("text", undef, 40)." ".
&ui_submit($text{'conn_msgb'}, 'msg'));
# Kill player
print &ui_table_row($text{'conn_kill'},
&ui_submit($text{'conn_killb'}, 'kill'));
# Grant item
print &ui_table_row($text{'conn_give'},
&ui_textbox("id", undef, 5).
&item_chooser_button("id")." ".
$text{'conn_count'}." ".
&ui_textbox("count", 1, 5)." ".
&ui_submit($text{'conn_giveb'}, 'give'));
}
print &ui_table_end();
print &ui_form_end();
if (!$c && !$ip && !$outtime) {
print "<b>$text{'conn_never'}</b><p>\n";
}
&ui_print_footer("list_conns.cgi", $text{'conns_return'});

View File

@ -37,6 +37,7 @@ my $fh = "OUT";
while(<$fh>) {
print &html_escape($_);
}
close($fh);
print "</pre>";
&ui_print_footer("", $text{'index_return'});