--- deliantra/server/lib/cf.pm 2007/10/04 11:36:54 1.378 +++ deliantra/server/lib/cf.pm 2007/11/08 19:43:25 1.394 @@ -6,13 +6,13 @@ use Symbol; use List::Util; use Socket; -use Storable; use Event; use Opcode; use Safe; use Safe::Hole; +use Storable (); -use Coro 3.64 (); +use Coro 4.1 (); use Coro::State; use Coro::Handle; use Coro::Event; @@ -29,7 +29,7 @@ use Digest::MD5; use Fcntl; use YAML::Syck (); -use IO::AIO 2.32 (); +use IO::AIO 2.51 (); use Time::HiRes; use Compress::LZF; use Digest::MD5 (); @@ -375,7 +375,7 @@ } if (@SLOT_QUEUE) { - # we do not use wait_For_tick() as it returns immediately when tick is inactive + # we do not use wait_for_tick() as it returns immediately when tick is inactive push @cf::WAIT_FOR_TICK, $signal; $signal->wait; } else { @@ -408,8 +408,8 @@ =item cf::sync_job { BLOCK } -The design of Crossfire TRT requires that the main coroutine ($Coro::main) -is always able to handle events or runnable, as Crossfire TRT is only +The design of Deliantra requires that the main coroutine ($Coro::main) +is always able to handle events or runnable, as Deliantra is only partly reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable. @@ -429,6 +429,8 @@ # this is the main coro, too bad, we have to block # till the operation succeeds, freezing the server :/ + LOG llevError, Carp::longmess "sync job";#d# + # TODO: use suspend/resume instead # (but this is cancel-safe) my $freeze_guard = freeze_mainloop; @@ -444,7 +446,11 @@ })->prio (Coro::PRIO_MAX); while ($busy) { - Coro::cede or Event::one_event; + if (Coro::nready) { + Coro::cede_notself; + } else { + Event::one_event; + } } $time = Event::time - $time; @@ -658,7 +664,7 @@ In the following description, CLASS can be any of C, C C, C or C (i.e. the attachable objects in -Crossfire TRT). +Deliantra). =over 4 @@ -966,16 +972,47 @@ ############################################################################# # object support -# +sub _object_equal($$); +sub _object_equal($$) { + my ($a, $b) = @_; + + return 0 unless (ref $a) eq (ref $b); + + if ("HASH" eq ref $a) { + my @ka = keys %$a; + my @kb = keys %$b; + + return 0 if @ka != @kb; + + for (0 .. $#ka) { + return 0 unless $ka[$_] eq $kb[$_]; + return 0 unless _object_equal $a->{$ka[$_]}, $b->{$kb[$_]}; + } + + } elsif ("ARRAY" eq ref $a) { + + return 0 if @$a != @$b; + + for (0 .. $#$a) { + return 0 unless _object_equal $a->[$_], $b->[$_]; + } + + } elsif ($a ne $b) { + return 0; + } + + 1 +} + +our $SLOW_MERGES;#d# sub _can_merge { my ($ob1, $ob2) = @_; - local $Storable::canonical = 1; - my $fob1 = Storable::freeze $ob1; - my $fob2 = Storable::freeze $ob2; + ++$SLOW_MERGES;#d# - $fob1 eq $fob2 + # we do the slow way here + return _object_equal $ob1, $ob2 } sub reattach { @@ -1046,7 +1083,7 @@ if (@$objs) { if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) { chmod SAVE_MODE, $fh; - my $data = Storable::nfreeze { version => 1, objs => $objs }; + my $data = Coro::Storable::nfreeze { version => 1, objs => $objs }; aio_write $fh, 0, (length $data), $data, 0; aio_fsync $fh if $cf::USE_FSYNC; close $fh; @@ -1087,7 +1124,8 @@ (aio_load "$filename.pst", $av) >= 0 or return; - $av = eval { (Storable::thaw $av)->{objs} }; + my $st = eval { Coro::Storable::thaw $av }; + $av = $st->{objs}; } utf8::decode (my $decname = $filename); @@ -1512,46 +1550,88 @@ =cut +use re 'eval'; + +my $group; +my $interior; $interior = qr{ + # match a pod interior sequence sans C<< >> + (?: + \ (.*?)\ (?{ $group = $^N }) + | < (??{$interior}) > + ) +}x; + sub expand_cfpod { - ((my $self), (local $_)) = @_; + my ($self, $pod) = @_; + + my $xml; - # escape & and < - s/&/&/g; - s/(?, I<>, U<> etc. - s/B<([^\>]*)>/$1<\/b>/ - || s/I<([^\>]*)>/$1<\/i>/ - || s/U<([^\>]*)>/$1<\/u>/ - || s/T<([^\>]*)>/$1<\/b><\/big>/ - # replace G tags - || s{G<([^>|]*)\|([^>]*)>}{ - $self->gender ? $2 : $1 - }ge - # replace H - || s{H<([^\>]*)>} - { - ("[$1 (Use hintmode to suppress hints)]", - "[Hint suppressed, see hintmode]", - "") - [$self->{hintmode}] - }ge; - - # create single paragraphs (very hackish) - s/(?<=\S)\n(?=\w)/ /g; - - # compress some whitespace - s/\s+\n/\n/g; # ws line-ends - s/\n\n+/\n/g; # double lines - s/^\n+//; # beginning lines - s/\n+$//; # ending lines + while () { + if ($pod =~ /\G( (?: [^BCGHITU]+ | .(?!<) )+ )/xgcs) { + $group = $1; + + $group =~ s/&/&/g; + $group =~ s/]*) (?{ $group = $^N }) + | < $interior > + ) + > + %gcsx + ) { + my ($code, $data) = ($1, $group); + + if ($code eq "B") { + $xml .= "" . expand_cfpod ($self, $data) . ""; + } elsif ($code eq "I") { + $xml .= "" . expand_cfpod ($self, $data) . ""; + } elsif ($code eq "U") { + $xml .= "" . expand_cfpod ($self, $data) . ""; + } elsif ($code eq "C") { + $xml .= "" . expand_cfpod ($self, $data) . ""; + } elsif ($code eq "T") { + $xml .= "" . expand_cfpod ($self, $data) . ""; + } elsif ($code eq "G") { + my ($male, $female) = split /\|/, $data; + $data = $self->gender ? $female : $male; + $xml .= expand_cfpod ($self, $data); + } elsif ($code eq "H") { + $xml .= ("[" . expand_cfpod ($self, $data) . " (Use hintmode to suppress hints)]", + "[Hint suppressed, see hintmode]", + "") + [$self->{hintmode}]; + } else { + $xml .= "error processing '$code($data)' directive"; + } + } else { + if ($pod =~ /\G(.+)/) { + warn "parse error while expanding $pod (at $1)"; + } + last; + } + } + + for ($xml) { + # create single paragraphs (very hackish) + s/(?<=\S)\n(?=\w)/ /g; + + # compress some whitespace + s/\s+\n/\n/g; # ws line-ends + s/\n\n+/\n/g; # double lines + s/^\n+//; # beginning lines + s/\n+$//; # ending lines + } + + $xml } +no re 'eval'; + sub hintmode { $_[0]{hintmode} = $_[1] if @_ > 1; $_[0]{hintmode} @@ -2630,6 +2710,7 @@ =cut +# non-persistent channels (usually the info channel) our %CHANNEL = ( "c/identify" => { id => "infobox", @@ -2643,12 +2724,42 @@ reply => undef, tooltip => "Signs and other items you examined", }, + "c/book" => { + id => "infobox", + title => "Book", + reply => undef, + tooltip => "The contents of a note or book", + }, "c/lookat" => { id => "infobox", title => "Look", reply => undef, tooltip => "What you saw there", }, + "c/who" => { + id => "infobox", + title => "Players", + reply => undef, + tooltip => "Shows players who are currently online", + }, + "c/body" => { + id => "infobox", + title => "Body Parts", + reply => undef, + tooltip => "Shows which body parts you posess and are available", + }, + "c/uptime" => { + id => "infobox", + title => "Uptime", + reply => undef, + tooltip => "How long the server has been running since last restart", + }, + "c/mapinfo" => { + id => "infobox", + title => "Map Info", + reply => undef, + tooltip => "Information related to the maps", + }, ); sub cf::client::send_msg { @@ -2894,7 +3005,7 @@ The following functions and methods are available within a safe environment: cf::object - contr pay_amount pay_player map x y force_find force_add + contr pay_amount pay_player map x y force_find force_add destroy insert remove name archname title slaying race decrease_ob_nr cf::object::player @@ -2911,7 +3022,7 @@ for ( ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y insert remove inv name archname title slaying race - decrease_ob_nr)], + decrease_ob_nr destroy)], ["cf::object::player" => qw(player)], ["cf::player" => qw(peaceful)], ["cf::map" => qw(trigger)], @@ -3278,6 +3389,7 @@ for my $login (keys %cf::PLAYER) { my $pl = $cf::PLAYER{$login} or next; $pl->valid or next; + delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt $pl->save; } warn "end emergency player save\n"; @@ -3335,7 +3447,7 @@ for (;;) { BDB::flush; IO::AIO::flush; - Coro::cede; + Coro::cede_notself; last unless IO::AIO::nreqs || BDB::nreqs; warn "iterate..."; }