crossfire:bot:scribe
CFLogBot
CFLogBot (aka Scribe on Metalforge server) is a modified version of LogBot (aka Seer). It is designed to collect communication on the in-game public channels such as shout and chat.
Source Code
#!/usr/bin/perl -w
#
# -------------------------------------------------------------------------
#
# Copyright (C) 2003 Jochen Suckfuell <crossfire@suckfuell.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# -----------------------------------------------------------------------
#
# TODO
#
# - fix inventory logging
# - check if event_wait and event_listen loops work
#
#
# Changelog:
#
# 2005-05-28 0.9.9
# - added the setup flag "bot 1" to tell the server that this is a bot
#
# 2004-03-11 0.9.8
# - new commands: add_admin, rem_admin, admins
# - added the admin commands to the help output
# - removed the "host" command, since 'who doesn't show the IP any more
# - fixed parsing changed 'who output format
# - save the is_admin flag with the players
# - allow several admin users
# - implement numdeaths, numkills
# - 'forget <script>' and 'stop <script>' commands implemented
# - implemented storing more stats
# - added events_stats callbacks
# - we now log the players that enter a map whose name matches a pattern in
# the predefined array @check_maps
#
# 2003-02-13 Release 0.9.7
#
# - 'last <player>' now also shows the host name
# - implemented the 'host <player>' command which tells the player's hostname
# - Don't answer to "hi" if not addressed directly.
#
# 2003-02-04 Release 0.9.6
#
# - implemented script command "when hearing <whatever>"
#
#
# 2003-02-03 Release 0.9.5
#
# - implemented simple scripting commands, conditions are still missing
# - slowed down the decay of map scores
# - output integer values for map scores
# - use the 'ncom' protocol command instead of 'command'
# - only reply to "hello|hi" to players that talked to me before
#
#
#
#
#
#
#
# ====================== configuration section ========================
use vars qw/$buffer0 $logspool $remote_host $player_name $player_password $retry_interval $admin $leave_cmd %players %kills %maps $socket $recvbuf $quit $upsince $getting_who_answer $last_maps_decay_time $version $pkg_sent $pkg_ackd @cmds_waiting $learning %scripts @events_wait @events_listen @events_stats %script_stack %stats %inv %checked_map @check_map/;
$logspool = 'crossfirechatarchive.txt';
$remote_host = "XXXXXXXXXXXXXXXXXXX";
$player_name = "XXXXXXXXXXXXXXXXXXX";
$player_password = "XXXXXXXXXXXXXXX";
$retry_interval = 30; # time in seconds
$admin = "XXXXXXXXXXXXXXXXXXXXXXXXX";
$leave_cmd = "gohome";
# We keep a player log for these maps:
@check_map = ( "^/guilds/" );
# =================== no configuration below ==========================
$version = "0.9.6";
use POSIX;
use IO::Socket;
use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
$events_stats{'maxhp'} = [];
$events_stats{'maxsp'} = [];
$events_stats{'maxgrace'} = [];
$events_stats{'lowfood'} = [];
load();
$socket = '';
init_connection();
$recvbuf = '';
$quit = 0;
my $save_minutes = 10; # This will be counted down and reset to 10, below.
my $last_time = time;
$getting_who_answer = 0;
$last_maps_decay_time = time;
$SIG{INT} = sub { $quit = 1; print STDERR "SIGINT\n"; };
# main event loop
while(! $quit)
{
my $r_in = '';
vec($r_in, $socket->fileno, 1) = 1;
my $rv = select($r_in, undef, undef, 1);
if(!defined($rv) || $rv < 0)
{
unless($! == EINTR) { die "select failed: $!"; }
last;
}
if($rv && vec($r_in, $socket->fileno, 1) == 1)
{
my $rv = $socket->recv($buf, POSIX::BUFSIZ, 0);
unless (defined($rv))
{
print STDERR "recv failed: $!\n";
init_connection();
$recvbuf = '';
next;
}
if(length($buf) == 0)
{
print STDERR "Connection closed.\n";
init_connection();
$recvbuf = '';
next;
}
$recvbuf .= $buf;
while(length($recvbuf) >= 2)
{
my $len = unpack("n", $recvbuf);
#print "DEBUG len $len , recvbuf length is ".length($recvbuf)."\n";
if(length($recvbuf) < 2 + $len) { last; }
#print unpack("H*", $recvbuf)."\n";
my $data = substr($recvbuf, 2, $len);
handle($data);
$recvbuf = substr($recvbuf, $len + 2);
} # len info
} # $socket is readable
my $now = time;
next if $last_time == $now;
if($now - $last_time > 60)
{
# This is processed once per minute.
$last_time = $now;
$save_minutes--;
if($save_minutes == 0)
{
save();
$save_minutes = 10;
}
if($now - $last_maps_decay_time > 24*60*60)
{
# once per day
# We halve the map score values once per day:
foreach my $map (keys %maps)
{
$maps{$map} *= 0.25;
if($maps{$map} == 0) { delete $maps{$map}; }
}
$last_maps_decay_time = $now;
}
cf_send_cmd("who"); # update maps' popularity
}
for(my $i = 0; $i < scalar @events_wait; $i++)
{
my $event_ref = $events_wait[$i];
if($event_ref->{"continue_at"} <= $now)
{
splice @events_wait, $i, 1; # remove the event from the list
do_execute($event_ref->{"script"}, $event_ref->{"pc"});
}
}
}
save();
exit 0;
# ===============================================================
sub init_connection
{
if($socket) { $socket->close(); }
while(!($socket = IO::Socket::INET->new(PeerAddr => $remote_host, PeerPort => 13327, Proto => "tcp", Type => SOCK_STREAM)))
{
print STDERR "Couldn't connect to $remote_host:13327 : $@\n";
print STDERR "Retrying in $retry_interval seconds.\n";
sleep $retry_interval;
}
my $flags = fcntl($socket, F_GETFL, 0) or die "Can't get flags for socket: $!\n";
fcntl($socket, F_SETFL, $flags | O_NONBLOCK) or die "Can't make socket nonblocking: $!\n";
$pkg_sent = 0;
$pkg_ackd = 0;
cf_send("version 1027 1027 Perl Bot");
cf_send("setup map1cmd 1 map1acmd 1 sound 0 sexp 0 darkness 0 newmapcmd 0 faceset 0 facecache 1 itemcmd 1 bot 1");
cf_send("addme");
$upsince = time;
print "Login at ".localtime($upsince)."\n";
}
sub handle
{
my $line = shift;
$line =~ /^(\S+)\s*(.*)$/s or die "Cannot match '$line'";
my $cmd = $1;
my $args = $2;
if($cmd =~ /^drawinfo$/)
{
$args =~ /^(\S+)\s*(.*)$/s;
my $color = $1;
my $info = $2;
if($info =~ /^(\S+) tells you: /)
{
#handle_player_request($1, $2, "tell $1");
print"$info\n";
$buffer0 = $info;
databaseprint();
return;
}
if($info =~ /^(\S+) shouts: /)
{
#handle_player_request($1, $2, "shout");
print"$info\n";
$buffer0 = $info;
databaseprint();
return;
}
if($info =~ /^(\S+) chats: /)
{
#handle_player_request($1, $2, "shout");
print"$info\n";
$buffer0 = $info;
databaseprint();
return;
}
if($info =~ /^Welcome Back!$/)
{
cf_send_cmd("listen 15");
cf_send_cmd("who");
if(defined $scripts{"autorun"})
{
$script_stack{"autorun"} = [];
do_execute("autorun");
}
return;
}
# Blue color text (in cfclient at least) is for NPC speech and other
# messages from the map.
if($color == 2)
{
for(my $i = 0; $i < scalar @events_listen; $i++)
{
my $event_ref = $events_listen[$i];
if($info =~ /$event_ref->{"listen_text"}/ms)
{
splice @events_listen, $i, 1; # remove the event from the list
do_execute($event_ref->{"script"}, $event_ref->{"pc"});
}
}
}
#print "INFO: $color $info\n";
return;
}
if($cmd =~ /^query$/)
{
print "$args ";
if($args =~ /What is your name/)
{
cf_send("reply $player_name");
return;
}
if($args =~ /What is your password/)
{
cf_send("reply $player_password");
return;
}
if($args =~ /Do you want to play again/)
{
cf_send("reply a");
return;
}
my $answer = <STDIN>;
chomp $answer;
cf_send("reply $answer");
return;
}
if($cmd =~ /^comc$/)
{
($pkg_ackd) = unpack("n", $args);
if(scalar @cmds_waiting)
{
$pkg_sent++;
if($pkg_sent == 256) { $pkg_sent = 0; }
cf_send("ncom ".pack("n", $pkg_sent)."\0\0\0\1".(shift @cmds_waiting));
}
return;
}
if($cmd =~ /^stats$/)
{
while($args)
{
my $s;
($s, $args) = unpack ('C a*', $args);
last if $s > 26;
if($s == 18) # food
{
($stats{'food'}, $args) = unpack('n a*', $args);
#print "food: $stats{food}\n";
if($stats{'food'} < 80)
{
foreach my $event_ref (@{$events_stats{'lowfood'}})
{
do_execute($event_ref->{"script"}, $event_ref->{"pc"});
}
$events_stats{ 'lowfood'} = [];
}
}
elsif($s == 1) # HP
{
($stats{'hp'}, $args) = unpack('n a*', $args);
#print "hp: $stats{hp}\n";
if(defined $stats{'maxhp'} && $stats{'hp'} == $stats{'maxhp'})
{
@events = @{$events_stats{'maxhp'}};
$events_stats{ 'maxhp'} = [];
foreach my $event_ref (@events)
{
do_execute($event_ref->{"script"}, $event_ref->{"pc"});
}
$events_stats{ 'maxhp'} = [];
}
}
elsif($s == 2) # max HP
{
($stats{'maxhp'}, $args) = unpack('n a*', $args);
#print "maxhp: $stats{maxhp}\n";
}
elsif($s == 3) # SP
{
($stats{'sp'}, $args) = unpack('n a*', $args);
#print "sp: $stats{sp}\n";
if(defined $stats{'maxsp'} && $stats{'sp'} == $stats{'maxsp'})
{
@events = @{$events_stats{'maxsp'}};
$events_stats{ 'maxsp'} = [];
foreach my $event_ref (@events)
{
do_execute($event_ref->{"script"}, $event_ref->{"pc"});
}
}
}
elsif($s == 4) # max SP
{
($stats{'maxsp'}, $args) = unpack('n a*', $args);
#print "maxsp: $stats{maxsp}\n";
}
elsif($s == 23) # grace
{
($stats{'grace'}, $args) = unpack('n a*', $args);
#print "grace: $stats{grace}\n";
if(defined $stats{'maxgrace'} && $stats{'grace'} == $stats{'maxgrace'})
{
@events = @{$events_stats{'grace'}};
$events_stats{ 'grace'} = [];
foreach my $event_ref (@events)
{
do_execute($event_ref->{"script"}, $event_ref->{"pc"});
}
$events_stats{ 'maxgrace'} = [];
}
}
elsif($s == 24) # max SP
{
($stats{'maxgrace'}, $args) = unpack('n a*', $args);
#print "maxgrace: $stats{maxgrace}\n";
}
elsif($s == 11) # exp
{
($stats{'exp'}, $args) = unpack('N a*', $args);
#print "exp: $stats{exp}\n";
}
elsif($s == 12) # level
{
($stats{'level'}, $args) = unpack('n a*', $args);
print "level: $stats{level}\n";
}
elsif($s == 13) # WC
{
my $wc;
($wc, $args) = unpack('n a*', $args);
$stats{'wc'} = ($wc > 32767 ? $wc - 65536 : $wc);
print "wc: $stats{wc}\n";
}
elsif($s == 14) # AC
{
my $ac;
($ac, $args) = unpack('n a*', $args);
$stats{'ac'} = ($ac > 32767 ? $ac - 65536 : $ac);
print "ac: $stats{ac}\n";
}
elsif($s == 17 || $s == 19 || $s == 26)
{
(undef, $args) = unpack('N a*', $args);
}
else
{
(undef, $args) = unpack('n a*', $args);
}
}
return;
}
if($cmd =~ /^item1$/)
{
my ($location, $tag, $flags, $weight, $name, $nrof);
%inv = ();
($location, $args) = unpack ('N a*', $args);
return unless $location;
while($args)
{
($tag, $flags, $weight, undef, $name, undef, undef, $nrof, $args) = unpack ('N N N N C/A n C N a*', $args);
($name, undef) = split /\0/, $name;
$inv{$tag} = { name => $name, flags => $flags, weight => $weight, nrof => $nrof };
#print "INV1: $nrof $name ($weight)\n";
}
return;
}
if($cmd =~ /^item2$/)
{
my ($location, $tag, $flags, $weight, $name, $nrof);
%inv = ();
($location, $args) = unpack ('N a*', $args);
return unless $location;
while($args)
{
($tag, $flags, $weight, undef, $name, undef, undef, $nrof, undef, $args) = unpack ('N N N N C/A n C N n a*', $args);
$inv{$tag} = { name => $name, flags => $flags, weight => $weight, nrof => $nrof };
#print "INV2: $nrof $name ($weight)\n";
}
return;
}
if($cmd =~ /^map|^face2$|^delinv$|^anim$|^player$/)
{
return;
}
print ">$cmd";
if(
$cmd =~ /^setup$/
)
{
print " $args";
}
print "\n";
}
sub do_command
{
my $cmd = shift;
if($cmd =~ /^save$|^north$|^south$|^east$|^west$|^northwest$|^northeast$|^southwest$|^southeast$|^say |^tell |^shout |^get\b|^take\b|^drop\b|^cast |^invoke |^apply\b|^pickup \d+$|^title |^ready_skill |^use_skill |^fire/)
{
# We just pass this through.
cf_send_cmd($cmd);
}
}
sub stop_script
{
my $scr = shift;
return unless defined $scripts{$scr};
return unless defined $script_stack{$scr};
foreach my $events_array_ref (\@events_listen, \@events_wait, \@events_stats)
{
for(my $i = 0; $i < scalar @$events_array_ref; $i++)
{
my $event_ref = $events_array_ref->[$i];
if($scr eq $event_ref->{"script"})
{
splice @$events_array_ref, $i, 1; # remove the event from the list
}
}
}
delete $script_stack{$scr};
}
sub do_execute
{
my $scriptname = shift;
my $pc = shift || 0;
for(; $pc < scalar @{$scripts{$scriptname}}; $pc++)
{
$cmd = $scripts{$scriptname}[$pc];
print "executing: $cmd (stack size: ".(scalar @{$script_stack{$scriptname}}).")\n";
if($cmd =~ /^save$|^north$|^south$|^east$|^west$|^northwest$|^northeast$|^southwest$|^southeast$|^say |^tell |^shout |^get\b|^take\b|^drop\b|^cast |^invoke |^apply\b|^pickup \d+$|^title |^ready_skill |^use_skill |^fire/)
{
# We just pass this through.
cf_send_cmd($cmd);
next;
}
if($cmd =~ /^execute (\S+)$/)
{
my $scr = $1;
next unless defined $scripts{$scr};
next if defined $script_stack{$scr};
$script_stack{$scr} = [];
do_execute($scr);
next;
}
if($cmd =~ /^stop (\S+)$/)
{
stop_script($1);
last;
}
if($cmd =~ /^wait (\d+)$/)
{
push @events_wait, { script => $scriptname, pc => ($pc+1), continue_at => time + $1 };
last;
}
if($cmd =~ /^for (\d+) times$/)
{
push @{$script_stack{$scriptname}}, { context => 'for', pc => $pc, count => $1 };
next;
}
if($cmd =~ /^end_for$/)
{
if(scalar @{$script_stack{$scriptname}} == 0)
{
print "Stack underflow in end_for!\n";
stop_script($scriptname);
last;
}
$stack_last = $script_stack{$scriptname}[0];
unless($stack_last->{"context"} eq 'for')
{
print "Script error: end_for found, but no for on stack.\n";
stop_script($scriptname);
return;
}
$stack_last->{"count"}--;
if($stack_last->{"count"} == 0)
{
shift @{$script_stack{$scriptname}};
next;
}
$pc = $stack_last->{"pc"};
next;
}
if($cmd =~ /^forever$/)
{
push @{$script_stack{$scriptname}}, { context => 'forever', pc => $pc };
next;
}
if($cmd =~ /^end_forever$/)
{
if(scalar @{$script_stack{$scriptname}} == 0)
{
print "Stack underflow in end_forever!\n";
stop_script($scriptname);
return;
}
$stack_last = $script_stack{$scriptname}[0];
unless($stack_last->{"context"} eq 'forever')
{
print "Script error: end_forever found, but no forever on stack.\n";
stop_script($scriptname);
return;
}
$pc = $stack_last->{"pc"};
next;
}
if($cmd =~ /^when hearing\s+(\S.+)$/)
{
push @events_listen, { script => $scriptname, pc => ($pc+1), listen_text => "$1" };
last;
}
if($cmd =~ /^when stats_event\s+(maxhp|maxsp|maxgrace|lowfood)$/)
{
push @{$events_stats{$1}}, { script => $scriptname, pc => ($pc+1) };
last;
}
if($cmd eq "end")
{
stop_script($scriptname);
last;
}
if($cmd =~ /^assert (.*)$/)
{
last unless script_condition($1);
next;
}
cf_send_cmd("tell Zorag Script error: unknown command '$cmd'");
last;
}
if($pc == scalar @{$scripts{$scriptname}})
{
stop_script($scriptname);
}
}
sub script_condition
{
my $cond = shift;
my @words = split (/\s+/, $cond);
my @stack = ( );
while (my $word = shift(@words))
{
if($word eq "not")
{
return 0 if scalar @stack < 1;
$stack[$#stack] = !$stack[$#stack];
next;
}
if($word eq "and")
{
return 0 if scalar @stack < 2;
splice @stack, $#stack-1, 2, ($stack[$#stack] && $stack[$#stack-1]);
next;
}
if($word eq "or")
{
return 0 if scalar @stack < 2;
splice @stack, $#stack-1, 2, ($stack[$#stack] || $stack[$#stack-1]);
next;
}
if($word eq "xor")
{
return 0 if scalar @stack < 2;
splice @stack, $#stack-1, 2, ($stack[$#stack] ^ $stack[$#stack-1]);
next;
}
# implement some conditions here XXX
}
return pop @stack;
}
sub parse_who
{
my $line = shift;
unless ($line =~ /^(\S+) the ([^\]]+)\[([^\]]+)\]/)
{
#print "WHO next line: $line\n";
return 0;
}
my $pl = $1;
return 1 if $pl eq $player_name; # Don't log ourselves.
my $title = $2;
my $map = $3;
$title =~ s/ $//;
#print ">WHO Player: $pl the $title on map $map\n";
# Set this player's is_here:
my $player_ref = $players{$pl};
if(defined $player_ref)
{
if(! $player_ref->{"is_here"})
{
$player_ref->{"is_here"} = 1;
if($player_ref->{"message"})
{
my $msg = $player_ref->{"message"};
$msg =~ s/_-/\n/g;
cf_send_info("command tell $pl", "Hi $pl!$msg");
$player_ref->{"message"} = "";
}
}
}
else
{
$player_ref = { asked_me => 0, is_here => 1, message => "", is_admin => 0 };
$players{$pl} = $player_ref;
}
$player_ref->{"last_seen"} = time;
# Do we log this map's usage?
foreach my $map_pat (@check_map)
{
if($map =~ m#$map_pat#)
{
if(defined $checked_map{$map}{$pl})
{
$checked_map{$map}{$pl}++;
}
else
{
$checked_map{$map}{$pl} = 1;
}
}
}
if($map =~ m#/_city_apartment_[Aa]partments.?$|/_santo_dominion_sdomino_appartment$|^/guilds/|^/city/city$|^/world/world_..$|^/dragonisland/housebrxzl$#)
{
# We don't log these maps.
return 1;
}
# remove number from random maps:
if($map =~ m#^/random/#)
{
$map =~ s/\d\d\d\d$//;
}
# Add to the map popularity:
if(defined $maps{$map})
{
$maps{$map}++;
}
else
{
$maps{$map} = 1;
}
return 1;
}
sub admin_msg
{
for my $adm (split /\s+/, $admin)
{
next unless $adm;
my $admin_ref = $players{$adm};
return unless defined $admin_ref;
my $msg = shift;
if($admin_ref->{"is_here"})
{
cf_send_cmd("tell $adm $msg");
return;
}
$msg =~ s#\n#_-#g;
$admin_ref->{"message"} .= $msg;
}
}
sub cf_send_info
{
my $answer_command = shift;
my $info = shift;
my @lines = split(/\n/, $info);
if(! @lines) { return; }
my $chunk = shift @lines;
foreach $line (@lines)
{
if(length($chunk) + length($line) < 220)
{
$chunk .= "\n".$line;
}
else
{
if(! $chunk)
{
die "Text chunk is too large (".length($chunk)." bytes)";
}
cf_send_cmd("$answer_command $chunk");
$chunk = "\n$line";
}
}
if($chunk)
{
cf_send_cmd("$answer_command $chunk");
}
}
sub cf_send_cmd
{
push @cmds_waiting, shift;
my $pending = $pkg_sent - $pkg_ackd;
if($pending < 0) { $pending += 256; }
while($pending < 3)
{
my $msg = shift @cmds_waiting;
# send this command immediately
$pkg_sent++;
if($pkg_sent == 256) { $pkg_sent = 0; }
cf_send("ncom ".pack("n", $pkg_sent)."\0\0\0\1$msg");
last unless scalar @cmds_waiting;
$pending++;
}
}
sub cf_send
{
my $msg = shift;
#print "<$msg\n";
my $out = pack("n/a*", $msg);
#print unpack("H*", $out)."\n";
print $socket $out;
$socket->flush();
}
sub save
{
open(KILLS, "> cf_kills") or die "Can't write file 'cf_kills': $!";
foreach my $key (keys %kills)
{
print KILLS $key.":".$kills{$key}."\n";
}
close KILLS;
open(SCRIPTS, "> cf_scripts") or die "Can't write file 'cf_scripts': $!";
foreach my $scriptname (keys %scripts)
{
#print "Saving script '$scriptname'.\n";
print SCRIPTS "$scriptname\n";
foreach my $line (@{$scripts{$scriptname}})
{
print SCRIPTS "$line\n";
}
print SCRIPTS "\n";
}
close SCRIPTS;
open(PLS, ">cf_players") or die "Can't write file 'cf_players': $!";
foreach my $key (keys %players)
{
#print "Saving player '$key'.\n";
print PLS "$key\n";
foreach my $plkey (keys %{$players{$key}})
{
print PLS "$plkey:$players{$key}{$plkey}\n";
}
}
close PLS;
open(MAPS, ">cf_maps") or die "Can't write file 'cf_maps': $!";
print MAPS "$last_maps_decay_time\n";
foreach my $map (keys %maps)
{
#print "Saving map info '$map'.\n";
print MAPS "$map $maps{$map}\n";
}
print MAPS "\n";
foreach my $map (keys %checked_map)
{
print MAPS "$map\n";
foreach my $pl (keys %{$checked_map{$map}})
{
print MAPS "$pl:$checked_map{$map}{$pl}\n";
}
}
close MAPS;
print STDERR "Data saved.\n";
}
sub load
{
%kills = ();
unless(open(KILLS, "< cf_kills"))
{
print STDERR "Can't read file 'cf_kills': $!\n";
}
else
{
print "Loading kills.\n";
while(<KILLS>)
{
chomp;
my ($key, $value) = split(/:/, $_);
$kills{$key} = $value;
}
close KILLS;
}
%scripts = ();
unless(open(SCRIPTS, "< cf_scripts"))
{
print STDERR "Can't read file 'cf_scripts': $!\n";
}
else
{
print "Loading scripts.\n";
while(<SCRIPTS>)
{
chomp;
my $scriptname = $_;
print "Loading script '$scriptname'.\n";
$scripts{$scriptname} = [];
for(;;)
{
my $line = <SCRIPTS>;
chomp $line;
last unless $line;
print ":$line\n";
push @{$scripts{$scriptname}}, $line;
}
}
close SCRIPTS;
}
%players = ();
unless(open(PLS, "< cf_players"))
{
print STDERR "Can't read file 'cf_players': $!\n";
}
else
{
$current_name = "";
print "Loading players.\n";
while(<PLS>)
{
chomp;
if(/^([^:]+):(.*)$/)
{
my $key = $1;
my $val = $2;
if($key eq "is_admin" && $val == 1)
{
$admin .= "$current_name ";
}
if($key eq "is_here")
{
# We get the current users from the 'who command.
$val = 0;
}
$players{$current_name}{$key} = $val;
}
else
{
$current_name = $_;
}
}
}
%maps = ();
unless(open(MAPS, "< cf_maps"))
{
print STDERR "Can't read file 'cf_maps': $!\n";
}
else
{
print "Loading maps.\n";
my $last_maps_decay_time = <MAPS>;
chomp $last_maps_decay_time;
while(<MAPS>)
{
chomp;
last unless $_;
my ($key, $value) = split(/ /, $_);
$maps{$key} = $value;
}
$current_name = "";
while(<MAPS>)
{
chomp;
if(/^([^:]+):(.*)$/)
{
$checked_map{$current_name}{$1} = $2;
}
else
{
$current_name = $_;
}
}
close MAPS;
}
}
sub databaseprint {
chomp($buffer0);
$buffer0 =~ s/[^a-zA-Z0-9_ \:\?\.\,\"\;\`\~\\\/\[\]\{\}\!\@\#\$\%\^\&\*\-\_\=\+\(\)]//g;
stamptime();
open FILE,">> $logspool"
or print"\nWARNING: Could Not Open $logspool \n";
print FILE "$timestamp"."$buffer0<br>\n"
or print"\nWARNING: Could Not Write To $logspool \n";
close FILE
or print"\nWARNING: Could Not Even Close $logspool \n";
}
#END OF databaseprint();
sub stamptime {
findtime();
formattime();
}
sub findtime {
($Second, $Minute, $Hour, $Day, $Month, $Year, $WeekDay, $DayOfYear, $IsDST) = localtime(time);
}
sub formattime {
$Month = $Month + 1;
$Year = $Year + 1900;
if ($Month <= 9) {
$Month = "0$Month";
}
if ($Day <= 9) {
$Day = "0$Day";
}
$timestamp = "[$Day/$Month/$Year $Hour:$Minute:$Second]";
}
crossfire/bot/scribe.txt · Last modified: 2025/04/18 12:51 by 127.0.0.1
