First, big disclaimer: I'm not a professional. There are no doubt many better ways to code for Achaea than this one. Use any of my advice at your peril.
So...
There is no hardcoded log support, so you have to code your own (better) logging. While you can do this with triggers, you can also redefine $onTextReceived, $onTextEntered, and $onKeyPressed to cover more. I have logging code embedded right into my system, since calling a subroutine to send multiple commands to Achaea isn't covered by the usual "alias: command, replacement" syntax that addAlias offers.
To ensure that logging is available throughout my system, I start defining variables and pulling in modules in achaea-live.pl, the file called from my Achaea bookmark in Xpertmud, like so:
CODE
#addIncludeDir('/home/cw/achaea/system');
#parse('achaea-live.pl');
This file doesn't have any actual code, it just wraps everything up nice and snug:
CODE
###
### Main Achaea wrapper script
###
use strict;
use warnings;
use DBI;
use Data::Dumper;
use IO::File;
use File::Slurp;
use Time::HiRes qw(time);
use vars qw($linePart $winstatus $winmain $winprompt $wintracking $cwlogging $cwlogprefix $id %vtracking %settings);
# parse the various system files
parse("system-datastructures.pl");
parse("system-subroutines.pl");
parse("aliases-settings.pl");
parse("aliases-combat.pl");
parse("aliases-noncombat.pl");
parse("aliases-skills.pl");
parse("aliases-apostate.pl");
parse("triggers-combat.pl");
parse("triggers-event.pl");
parse("client-xpertmud.pl");
# ensure everything is loaded and ready go to
loadsettings();
loadeyes();
resettracking();
racechange($settings{race});
ton();
llp("Achaea system fully loaded.");
The file "client-xpertmud.pl" contains my redefinitions of Xpertmud internals, usually found in the Xpertmud source in xpertmud/scripting/perl/sysinit.pl. Previously I just modified that file and recompiled, but it's far better to define your own in your system, then you're not potentially recoding everytime Globbi comes out with a new Xpertmud.
I also have to do my own logging since I want things to go to
my own windows, not just statusWindow(). You can also see the secret to how well I ignore illusions that lifevision catches - I simply don't pass known illusions to triggers.
Of course, use the file below as an example - it's not worth much without the rest of my system included. It may not nest correctly in the Achaea forums, but checking the page's html source may make things clearer.
CODE
###
### client settings pertaining to how I want xpertmud to run
###
# window definitions
$winstatus = statusWindow();
#$winstatus->minimize();
#$winmain->resizeChars(80, 24);
$winstatus->hide();
$winmain = new XMTextBufferWindow();
$winmain->move(0, 0);
$winmain->setTitle('Main');
$winmain->resizeChars(85, 32);
$winmain->setCursor(0,0);
$winmain->show();
$winmain->raise();
$winprompt = new XMTextWindow();
$winprompt->setTitle('Prompt');
$winprompt->resizeChars(56, 1);
$winprompt->move(0,498);
$winprompt->setCursor(0,0);
$winprompt->show();
$winprompt->raise();
$wintracking = new XMTextWindow();
$wintracking->setTitle('Tracking');
$wintracking->resizeChars(24, 36);
$wintracking->move(610,0);
$wintracking->setCursor(0,0);
$wintracking->show();
$wintracking->raise();
# setting up logging
# this should make it so that things don't echo to StatusWindow, I hope
$isEcho = 1;
# this is the connection number, since we only play Achaea it's always 0
$id = 0;
# I hate this
delAlias("LOCAL_ECHO");
{
# setting up the logging filehandle object, using >> to append
my $cwlogdir = '/home/cw/achaea/logs';
# get the date string, via system date
chomp(my $cwdate = `/bin/date +%F_%k-%M-%S`);
# remove any ugly spaces
$cwdate =~ s/ //g;
# so this is where we log to
my $cwfilename;
if ($cwlogprefix) {
$cwfilename = "${cwlogdir}/${cwlogprefix}${cwdate}.$$.log";
} else {
$cwfilename = "${cwlogdir}/${cwdate}.$$.log";
}
# and now we open the filehandle object
$cwlogging = new IO::File ">> $cwfilename";
}
# --------------------------------------------------
# personalized onTextReceived sub
# temp for chunk testing
#chomp(my $cwdate = `/bin/date +%F_%k-%M-%S`);
#$cwdate =~ s/ //og;
#my $xlogging = new IO::File ">> /home/cw/achaea/logs/chunklog${cwdate}.log";
#my $xlogging = new IO::File ">> /home/cw/achaea/logs/timelog${cwdate}.log";
resetWriteRegExps();
$onTextReceived = sub {
my $text=shift;
my $id = shift;
return undef if $text =~ /<IAC><GA>/o;
# for achaea chunk testing
#print $xlogging "\n------------------------------------------\n";
#if ($text =~ /(\d+)h,.+ (\d+)m.+ ([ebdxkc]+)-$/oms) {
#print $xlogging "CHUNK ENDS WITH PROMPT\n";
#}
#print $xlogging $text;
if(ref $triggerPreprocessing eq "CODE") {
$text = &$triggerPreprocessing($text);
}
#my $xtime = sprintf("%.5f",time);
if (defined $text) {
$text = $linePart . $text if $linePart;
foreach my $line (split /(?<=\n)/, $text) {
$linePart = "";
if($line =~ s/\r?\n$/\n/o) {
if(ref $onLineReceived eq "CODE") {
$line = &$onLineReceived($line);
}
# don't use ^ here, as there may be ansi colouring
$line = executeTriggers($line) unless $text =~ /\*\* Illusion \*\*/oms;
#if(defined $line && ($line ne "" || $line !~ /^\r$/o)) {
if(defined $line && $line ne "") {
$winmain->print($line);
# strip nasty ansi
$line =~ s/\e[^m]*m//og;
print $cwlogging $line;
#print $xlogging "$xtime: $line";
}
} else {
if(($line =~ /$writeAtOnceRegExp/) && ($line !~ /$dontWriteAtOnceRegExp/)) {
# don't use ^ here, as there may be ansi colouring
$line = executeTriggers($line) unless $text =~ /\*\* Illusion \*\*/oms;
#if(defined $line && ($line ne "" || $line !~ /^\r$/o)) {
if(defined $line && $line ne "") {
$winmain->print($line);
# strip nasty ansi
$line =~ s/\e[^m]*m//og;
print $cwlogging $line;
#print $xlogging "$xtime: $line";
}
} else {
$linePart = $line;
}
}
}
}
};
$onTextEntered = sub {
my $text = shift;
if(ref $aliasPreprocessing eq "CODE") {
$text = &$aliasPreprocessing($text);
}
$text = executeAliases($text);
if(defined $text) {
$winmain->print("$text\n");
print $cwlogging "$text\n";
connection($id)->send("$text\n");
}
};
$onKeyPressed = sub {
my $key = shift;
my $ascii = shift;
if (ref $keyPreprocessing eq "CODE") {
( $key, $ascii ) = &$keyPreprocessing($key, $ascii);
}
if(defined $key) {
if(executeKeyBindings($key)) {
return 1;
}
if($isEcho) {
return undef;
} else {
if ($key =~ /KP_Enter/ or $key =~ /Return/) {
print $cwlogging "\n";
connection($id)->send("\n");
} elsif(defined $ascii && $ascii =~ /^.$/) {
print $cwlogging "$ascii\n";
connection($id)->send("$ascii\n");
}
sendKey($key, $ascii);
return 1;
}
}
return undef;
};
delKeyBinding(qr/^Scroll-/o);
addKeyBinding('Scroll-Up', '.000.... PgUp', sub {
$winmain->scrollLines(-int($winmain->getLines()/2));
return undef;
});
addKeyBinding('Scroll-Down', '.000.... PgDown', sub {
$winmain->scrollLines(int($winmain->getLines()/2));
return undef;
});
This stuff is straightforward as long as you're only typing and receiving text, or doing simple aliases. When I want to have a subroutine to do multiple things at once, I have to do things differently. I define lower level subroutines to handle sending/logging/displaying text, rather than call the xpertmud internals (the XM:: routines) right in my system. Here's an example, the standard lp (log and print to screen/Achaea) subroutine:
CODE
# one to log and lp every sent line
sub lp {
my $text = "@_";
$text =~ s/[\r\n]+$//o;
print $cwlogging "$text\n";
$winmain->print("$text\n");
XM::send("$text\n");
# not hyperventilating when I chain commands
setalarm('holdbreath_sent', time);
return undef;
}
That means that my simple versus complicated aliases tend to look like this:
CODE
addAlias("lep", qr/^lep$/o, "leprosy", 1);
addAlias("bw", qr/^bw$/o, sub {
lp("stand");
lp("config prompt hmw");
lp("blackwind");
return undef;
}, 1);
Note the return undef so that Xpertmud doesn't send any more text to Achaea than I want it to. In the first alias example, "leprosy" is the text returned from the alias, and is then sent to the world.
The <IAC><GA> is part of telnet, and I asked about a hardcoded solution, though nothing yet. I can't code C++, so I can't do anything about it myself. The sequence is always sent as its own chunk of data from Achaea, so you can ignore it by the line you've seen above in $onTextReceived:
CODE
return undef if $text =~ /<IAC><GA>/o;
You could also likely handle this through your prompt trigger, but this is the solution I chose for myself. If you want to see more about how Achaea sends data to you, some of my commented bits in $onTextReceived are what I once used to examine how Achaea sends text data.
One of my prompt triggers is as follows:
CODE
# everything and the kitchen sink winds up here
addTrigger("trig_promptfull", qr/^(.*[0-9]+h,.+[0-9]+m.+-) ?$/o, \&prompt, 1, 0);
You'll notice above how I strip ansi before I log incoming text, but after I check triggers, so I have to account for ansi colour in my triggers. Your regex likely isn't taking into account that there are ansi codes in the coloured prompt text you see. The ansi is what gives everything its colour, so you just have to work around it.
My only other trigger tip is to use the qr//o regex construct for pattern matching instead of "" quotes. The reason for the former is that with the o modifier the regex is only compiled once when your script is parsed, rather than every time your client checks the trigger in question. With many triggers, this can knock hundreds of milliseconds of processing time off your system's reactions when you're trying to get out of an axewhoring or similar tactic.