ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
(Generate patch)

Comparing deliantra/server/lib/cf.pm (file contents):
Revision 1.276 by root, Sun Jun 10 04:05:47 2007 UTC vs.
Revision 1.300 by root, Tue Jul 10 06:44:29 2007 UTC

20use Coro::Signal; 20use Coro::Signal;
21use Coro::Semaphore; 21use Coro::Semaphore;
22use Coro::AIO; 22use Coro::AIO;
23use Coro::Storable; 23use Coro::Storable;
24 24
25use JSON::XS 1.4 ();
25use BDB (); 26use BDB ();
26use Data::Dumper; 27use Data::Dumper;
27use Digest::MD5; 28use Digest::MD5;
28use Fcntl; 29use Fcntl;
29use YAML::Syck (); 30use YAML::Syck ();
49our %COMMAND = (); 50our %COMMAND = ();
50our %COMMAND_TIME = (); 51our %COMMAND_TIME = ();
51 52
52our @EXTS = (); # list of extension package names 53our @EXTS = (); # list of extension package names
53our %EXTCMD = (); 54our %EXTCMD = ();
55our %EXTICMD = ();
54our %EXT_CORO = (); # coroutines bound to extensions 56our %EXT_CORO = (); # coroutines bound to extensions
55our %EXT_MAP = (); # pluggable maps 57our %EXT_MAP = (); # pluggable maps
56 58
57our $RELOAD; # number of reloads so far 59our $RELOAD; # number of reloads so far
58our @EVENT; 60our @EVENT;
206} 208}
207 209
208$Event::DIED = sub { 210$Event::DIED = sub {
209 warn "error in event callback: @_"; 211 warn "error in event callback: @_";
210}; 212};
213
214#############################################################################
211 215
212=head2 UTILITY FUNCTIONS 216=head2 UTILITY FUNCTIONS
213 217
214=over 4 218=over 4
215 219
234 $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge; 238 $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge;
235 $d 239 $d
236 } || "[unable to dump $_[0]: '$@']"; 240 } || "[unable to dump $_[0]: '$@']";
237} 241}
238 242
239use JSON::XS qw(to_json from_json); # TODO# replace by JSON::PC once working
240
241=item $ref = cf::from_json $json 243=item $ref = cf::from_json $json
242 244
243Converts a JSON string into the corresponding perl data structure. 245Converts a JSON string into the corresponding perl data structure.
244 246
245=item $json = cf::to_json $ref 247=item $json = cf::to_json $ref
246 248
247Converts a perl data structure into its JSON representation. 249Converts a perl data structure into its JSON representation.
250
251=cut
252
253our $json_coder = JSON::XS->new->utf8->max_size (1e6); # accept ~1mb max
254
255sub to_json ($) { $json_coder->encode ($_[0]) }
256sub from_json ($) { $json_coder->decode ($_[0]) }
248 257
249=item cf::lock_wait $string 258=item cf::lock_wait $string
250 259
251Wait until the given lock is available. See cf::lock_acquire. 260Wait until the given lock is available. See cf::lock_acquire.
252 261
318 327
319BEGIN { *async = \&Coro::async_pool } 328BEGIN { *async = \&Coro::async_pool }
320 329
321=item cf::sync_job { BLOCK } 330=item cf::sync_job { BLOCK }
322 331
323The design of crossfire+ requires that the main coro ($Coro::main) is 332The design of Crossfire TRT requires that the main coroutine ($Coro::main)
324always able to handle events or runnable, as crossfire+ is only partly 333is always able to handle events or runnable, as Crossfire TRT is only
325reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable. 334partly reentrant. Thus "blocking" it by e.g. waiting for I/O is not
335acceptable.
326 336
327If it must be done, put the blocking parts into C<sync_job>. This will run 337If it must be done, put the blocking parts into C<sync_job>. This will run
328the given BLOCK in another coroutine while waiting for the result. The 338the given BLOCK in another coroutine while waiting for the result. The
329server will be frozen during this time, so the block should either finish 339server will be frozen during this time, so the block should either finish
330fast or be very important. 340fast or be very important.
390 $EXT_CORO{$coro+0} = $coro; 400 $EXT_CORO{$coro+0} = $coro;
391 401
392 $coro 402 $coro
393} 403}
394 404
395sub write_runtime { 405=item fork_call { }, $args
396 my $runtime = "$LOCALDIR/runtime";
397 406
398 # first touch the runtime file to show we are still running: 407Executes the given code block with the given arguments in a seperate
399 # the fsync below can take a very very long time. 408process, returning the results. Everything must be serialisable with
409Coro::Storable. May, of course, block. Note that the executed sub may
410never block itself or use any form of Event handling.
400 411
401 IO::AIO::aio_utime $runtime, undef, undef; 412=cut
402 413
403 my $guard = cf::lock_acquire "write_runtime"; 414sub _store_scalar {
404 415 open my $fh, ">", \my $buf
405 my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644 416 or die "fork_call: cannot open fh-to-buf in child : $!";
406 or return; 417 Storable::store_fd $_[0], $fh;
407
408 my $value = $cf::RUNTIME + 90 + 10;
409 # 10 is the runtime save interval, for a monotonic clock
410 # 60 allows for the watchdog to kill the server.
411
412 (aio_write $fh, 0, (length $value), $value, 0) <= 0
413 and return;
414
415 # always fsync - this file is important
416 aio_fsync $fh
417 and return;
418
419 # touch it again to show we are up-to-date
420 aio_utime $fh, undef, undef;
421
422 close $fh 418 close $fh;
423 or return;
424 419
425 aio_rename "$runtime~", $runtime 420 $buf
426 and return; 421}
427 422
428 warn "runtime file written.\n"; 423sub fork_call(&@) {
424 my ($cb, @args) = @_;
429 425
426# socketpair my $fh1, my $fh2, Socket::AF_UNIX, Socket::SOCK_STREAM, Socket::PF_UNSPEC
427# or die "socketpair: $!";
428 pipe my $fh1, my $fh2
429 or die "pipe: $!";
430
431 if (my $pid = fork) {
432 close $fh2;
433
434 my $res = (Coro::Handle::unblock $fh1)->readline (undef);
435 warn "pst<$res>" unless $res =~ /^pst/;
436 $res = Coro::Storable::thaw $res;
437
438 waitpid $pid, 0; # should not block anymore, we expect the child to simply behave
439
440 Carp::confess $$res unless "ARRAY" eq ref $res;
441
442 return wantarray ? @$res : $res->[-1];
443 } else {
444 reset_signals;
445 local $SIG{__WARN__};
446 local $SIG{__DIE__};
447 # just in case, this hack effectively disables event
448 # in the child. cleaner and slower would be canceling all watchers,
449 # but this works for the time being.
450 local $Coro::idle;
451 $Coro::current->prio (Coro::PRIO_MAX);
452
453 eval {
454 close $fh1;
455
456 my @res = eval { $cb->(@args) };
457
458 syswrite $fh2, _store_scalar $@ ? \"$@" : \@res;
459 close $fh2;
460 };
461
462 warn $@ if $@;
463 _exit 0;
430 1 464 }
465}
466
467=item $value = cf::db_get $family => $key
468
469Returns a single value from the environment database.
470
471=item cf::db_put $family => $key => $value
472
473Stores the given C<$value> in the family. It can currently store binary
474data only (use Compress::LZF::sfreeze_cr/sthaw to convert to/from binary).
475
476=cut
477
478our $DB;
479
480sub db_init {
481 unless ($DB) {
482 $DB = BDB::db_create $DB_ENV;
483
484 cf::sync_job {
485 eval {
486 $DB->set_flags (BDB::CHKSUM);
487
488 BDB::db_open $DB, undef, "db", undef, BDB::BTREE,
489 BDB::CREATE | BDB::AUTO_COMMIT, 0666;
490 cf::cleanup "db_open(db): $!" if $!;
491 };
492 cf::cleanup "db_open(db): $@" if $@;
493 };
494 }
495}
496
497sub db_get($$) {
498 my $key = "$_[0]/$_[1]";
499
500 cf::sync_job {
501 BDB::db_get $DB, undef, $key, my $data;
502
503 $! ? ()
504 : $data
505 }
506}
507
508sub db_put($$$) {
509 BDB::dbreq_pri 4;
510 BDB::db_put $DB, undef, "$_[0]/$_[1]", $_[2], 0, sub { };
511}
512
513=item cf::cache $id => [$paths...], $processversion => $process
514
515Generic caching function that returns the value of the resource $id,
516caching and regenerating as required.
517
518This function can block.
519
520=cut
521
522sub cache {
523 my ($id, $src, $processversion, $process) = @_;
524
525 my $meta =
526 join "\x00",
527 $processversion,
528 map {
529 aio_stat $_
530 and Carp::croak "$_: $!";
531
532 ($_, (stat _)[7,9])
533 } @$src;
534
535 my $dbmeta = db_get cache => "$id/meta";
536 if ($dbmeta ne $meta) {
537 # changed, we may need to process
538
539 my @data;
540 my $md5;
541
542 for (0 .. $#$src) {
543 0 <= aio_load $src->[$_], $data[$_]
544 or Carp::croak "$src->[$_]: $!";
545 }
546
547 # if processing is expensive, check
548 # checksum first
549 if (1) {
550 $md5 =
551 join "\x00",
552 $processversion,
553 map {
554 Coro::cede;
555 ($src->[$_], Digest::MD5::md5_hex $data[$_])
556 } 0.. $#$src;
557
558
559 my $dbmd5 = db_get cache => "$id/md5";
560 if ($dbmd5 eq $md5) {
561 db_put cache => "$id/meta", $meta;
562
563 return db_get cache => "$id/data";
564 }
565 }
566
567 my $t1 = Time::HiRes::time;
568 my $data = $process->(\@data);
569 my $t2 = Time::HiRes::time;
570
571 warn "cache: '$id' processed in ", $t2 - $t1, "s\n";
572
573 db_put cache => "$id/data", $data;
574 db_put cache => "$id/md5" , $md5;
575 db_put cache => "$id/meta", $meta;
576
577 return $data;
578 }
579
580 db_get cache => "$id/data"
431} 581}
432 582
433=item cf::datalog type => key => value, ... 583=item cf::datalog type => key => value, ...
434 584
435Log a datalog packet of the given type with the given key-value pairs. 585Log a datalog packet of the given type with the given key-value pairs.
453attach callbacks/event handlers (a collection of which is called an "attachment") 603attach callbacks/event handlers (a collection of which is called an "attachment")
454to it. All such attachable objects support the following methods. 604to it. All such attachable objects support the following methods.
455 605
456In the following description, CLASS can be any of C<global>, C<object> 606In the following description, CLASS can be any of C<global>, C<object>
457C<player>, C<client> or C<map> (i.e. the attachable objects in 607C<player>, C<client> or C<map> (i.e. the attachable objects in
458crossfire+). 608Crossfire TRT).
459 609
460=over 4 610=over 4
461 611
462=item $attachable->attach ($attachment, key => $value...) 612=item $attachable->attach ($attachment, key => $value...)
463 613
665 _attach $registry, $klass, @attach; 815 _attach $registry, $klass, @attach;
666 } 816 }
667 817
668 $obj->{$name} = \%arg; 818 $obj->{$name} = \%arg;
669 } else { 819 } else {
670 warn "object uses attachment '$name' that is not available, postponing.\n"; 820 warn "object uses attachment '$name' which is not available, postponing.\n";
671 } 821 }
672 822
673 $obj->{_attachment}{$name} = undef; 823 $obj->{_attachment}{$name} = undef;
674} 824}
675 825
877 warn sprintf "loading %s (%d)\n", 1027 warn sprintf "loading %s (%d)\n",
878 $filename, length $data, scalar @{$av || []}; 1028 $filename, length $data, scalar @{$av || []};
879 return ($data, $av); 1029 return ($data, $av);
880} 1030}
881 1031
1032=head2 COMMAND CALLBACKS
1033
1034=over 4
1035
1036=cut
1037
882############################################################################# 1038#############################################################################
883# command handling &c 1039# command handling &c
884 1040
885=item cf::register_command $name => \&callback($ob,$args); 1041=item cf::register_command $name => \&callback($ob,$args);
886 1042
898 push @{ $COMMAND{$name} }, [$caller, $cb]; 1054 push @{ $COMMAND{$name} }, [$caller, $cb];
899} 1055}
900 1056
901=item cf::register_extcmd $name => \&callback($pl,$packet); 1057=item cf::register_extcmd $name => \&callback($pl,$packet);
902 1058
903Register a callbackf ro execution when the client sends an extcmd packet. 1059Register a callback for execution when the client sends an (synchronous)
1060extcmd packet. Ext commands will be processed in the order they are
1061received by the server, like other user commands. The first argument is
1062the logged-in player. Ext commands can only be processed after a player
1063has logged in successfully.
904 1064
905If the callback returns something, it is sent back as if reply was being 1065If the callback returns something, it is sent back as if reply was being
906called. 1066called.
907 1067
1068=item cf::register_exticmd $name => \&callback($ns,$packet);
1069
1070Register a callback for execution when the client sends an (asynchronous)
1071exticmd packet. Exti commands are processed by the server as soon as they
1072are received, i.e. out of order w.r.t. other commands. The first argument
1073is a client socket. Exti commands can be received anytime, even before
1074log-in.
1075
1076If the callback returns something, it is sent back as if reply was being
1077called.
1078
908=cut 1079=cut
909 1080
910sub register_extcmd { 1081sub register_extcmd {
911 my ($name, $cb) = @_; 1082 my ($name, $cb) = @_;
912 1083
913 $EXTCMD{$name} = $cb; 1084 $EXTCMD{$name} = $cb;
1085}
1086
1087sub register_exticmd {
1088 my ($name, $cb) = @_;
1089
1090 $EXTICMD{$name} = $cb;
914} 1091}
915 1092
916cf::player->attach ( 1093cf::player->attach (
917 on_command => sub { 1094 on_command => sub {
918 my ($pl, $name, $params) = @_; 1095 my ($pl, $name, $params) = @_;
927 cf::override; 1104 cf::override;
928 }, 1105 },
929 on_extcmd => sub { 1106 on_extcmd => sub {
930 my ($pl, $buf) = @_; 1107 my ($pl, $buf) = @_;
931 1108
932 my $msg = eval { from_json $buf }; 1109 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) };
933 1110
934 if (ref $msg) { 1111 if (ref $msg) {
935 if (my $cb = $EXTCMD{$msg->{msgtype}}) { 1112 if (my $cb = $EXTCMD{$msg->{msgtype}}) {
936 if (my %reply = $cb->($pl, $msg)) { 1113 if (my %reply = $cb->($pl, $msg)) {
937 $pl->ext_reply ($msg->{msgid}, %reply); 1114 $pl->ext_reply ($msg->{msgid}, %reply);
943 1120
944 cf::override; 1121 cf::override;
945 }, 1122 },
946); 1123);
947 1124
948sub load_extension {
949 my ($path) = @_;
950
951 $path =~ /([^\/\\]+)\.ext$/ or die "$path";
952 my $base = $1;
953 my $pkg = $1;
954 $pkg =~ s/[^[:word:]]/_/g;
955 $pkg = "ext::$pkg";
956
957 warn "... loading '$path' into '$pkg'\n";
958
959 open my $fh, "<:utf8", $path
960 or die "$path: $!";
961
962 my $source =
963 "package $pkg; use strict; use utf8;\n"
964 . "#line 1 \"$path\"\n{\n"
965 . (do { local $/; <$fh> })
966 . "\n};\n1";
967
968 unless (eval $source) {
969 my $msg = $@ ? "$path: $@\n"
970 : "extension disabled.\n";
971 if ($source =~ /^#!.*perl.*#.*MANDATORY/m) { # ugly match
972 warn $@;
973 warn "mandatory extension failed to load, exiting.\n";
974 exit 1;
975 }
976 die $@;
977 }
978
979 push @EXTS, $pkg;
980}
981
982sub load_extensions { 1125sub load_extensions {
1126 cf::sync_job {
1127 my %todo;
1128
983 for my $ext (<$LIBDIR/*.ext>) { 1129 for my $path (<$LIBDIR/*.ext>) {
984 next unless -r $ext; 1130 next unless -r $path;
985 eval { 1131
986 load_extension $ext; 1132 $path =~ /([^\/\\]+)\.ext$/ or die "$path";
1133 my $base = $1;
1134 my $pkg = $1;
1135 $pkg =~ s/[^[:word:]]/_/g;
1136 $pkg = "ext::$pkg";
1137
1138 open my $fh, "<:utf8", $path
1139 or die "$path: $!";
1140
1141 my $source = do { local $/; <$fh> };
1142
1143 my %ext = (
1144 path => $path,
1145 base => $base,
1146 pkg => $pkg,
1147 );
1148
1149 $ext{meta} = { map { (split /=/, $_, 2)[0, 1] } split /\s+/, $1 }
1150 if $source =~ /\A#!.*?perl.*?#\s*(.*)$/m;
1151
1152 $ext{source} =
1153 "package $pkg; use strict; use utf8;\n"
1154 . "#line 1 \"$path\"\n{\n"
1155 . $source
1156 . "\n};\n1";
1157
1158 $todo{$base} = \%ext;
1159 }
1160
1161 my %done;
1162 while (%todo) {
1163 my $progress;
1164
1165 while (my ($k, $v) = each %todo) {
1166 for (split /,\s*/, $v->{meta}{depends}) {
1167 goto skip
1168 unless exists $done{$_};
1169 }
1170
1171 warn "... loading '$k' into '$v->{pkg}'\n";
1172
1173 unless (eval $v->{source}) {
1174 my $msg = $@ ? "$v->{path}: $@\n"
1175 : "$v->{base}: extension inactive.\n";
1176
1177 if (exists $v->{meta}{mandatory}) {
1178 warn $msg;
1179 warn "mandatory extension failed to load, exiting.\n";
1180 exit 1;
1181 }
1182
1183 warn $msg;
1184 }
1185
1186 $done{$k} = delete $todo{$k};
1187 push @EXTS, $v->{pkg};
1188 $progress = 1;
987 1 1189 }
988 } or warn "$ext not loaded: $@"; 1190
1191 skip:
1192 die "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n"
1193 unless $progress;
1194 }
989 } 1195 };
990} 1196}
991 1197
992############################################################################# 1198#############################################################################
1199
1200=back
993 1201
994=head2 CORE EXTENSIONS 1202=head2 CORE EXTENSIONS
995 1203
996Functions and methods that extend core crossfire objects. 1204Functions and methods that extend core crossfire objects.
997 1205
1185 } 1393 }
1186 1394
1187 \@paths 1395 \@paths
1188} 1396}
1189 1397
1398=item $protocol_xml = $player->expand_cfpod ($crossfire_pod)
1399
1400Expand crossfire pod fragments into protocol xml.
1401
1402=cut
1403
1404sub expand_cfpod {
1405 ((my $self), (local $_)) = @_;
1406
1407 # escape & and <
1408 s/&/&amp;/g;
1409 s/(?<![BIUGH])</&lt;/g;
1410
1411 # this is buggy, it needs to properly take care of nested <'s
1412
1413 1 while
1414 # replace B<>, I<>, U<> etc.
1415 s/B<([^\>]*)>/<b>$1<\/b>/
1416 || s/I<([^\>]*)>/<i>$1<\/i>/
1417 || s/U<([^\>]*)>/<u>$1<\/u>/
1418 # replace G<male|female> tags
1419 || s{G<([^>|]*)\|([^>]*)>}{
1420 $self->gender ? $2 : $1
1421 }ge
1422 # replace H<hint text>
1423 || s{H<([^\>]*)>}
1424 {
1425 ("<fg name=\"lightblue\">[$1 (Use hintmode to suppress hints)]</fg>",
1426 "<fg name=\"lightblue\">[Hint suppressed, see hintmode]</fg>",
1427 "")
1428 [$self->{hintmode}]
1429 }ge;
1430
1431 # create single paragraphs (very hackish)
1432 s/(?<=\S)\n(?=\w)/ /g;
1433
1434 # compress some whitespace
1435 s/\s+\n/\n/g; # ws line-ends
1436 s/\n\n+/\n/g; # double lines
1437 s/^\n+//; # beginning lines
1438 s/\n+$//; # ending lines
1439
1440 warn $_;#d#
1441 $_
1442}
1443
1444sub hintmode {
1445 $_[0]{hintmode} = $_[1] if @_ > 1;
1446 $_[0]{hintmode}
1447}
1448
1190=item $player->ext_reply ($msgid, %msg) 1449=item $player->ext_reply ($msgid, %msg)
1191 1450
1192Sends an ext reply to the player. 1451Sends an ext reply to the player.
1193 1452
1194=cut 1453=cut
1195 1454
1196sub ext_reply($$%) { 1455sub ext_reply($$%) {
1197 my ($self, $id, %msg) = @_; 1456 my ($self, $id, %msg) = @_;
1198 1457
1199 $msg{msgid} = $id; 1458 $msg{msgid} = $id;
1200 1459 $self->send ("ext " . $self->ns->{json_coder}->encode (\%msg));
1201 $self->send ("ext " . cf::to_json \%msg);
1202} 1460}
1203 1461
1204=item $player->ext_event ($type, %msg) 1462=item $player->ext_event ($type, %msg)
1205 1463
1206Sends an ext event to the client. 1464Sends an ext event to the client.
1221 1479
1222package cf::region; 1480package cf::region;
1223 1481
1224=item cf::region::find_by_path $path 1482=item cf::region::find_by_path $path
1225 1483
1226Tries to decuce the probable region for a map knowing only its path. 1484Tries to decuce the likely region for a map knowing only its path.
1227 1485
1228=cut 1486=cut
1229 1487
1230sub find_by_path($) { 1488sub find_by_path($) {
1231 my ($path) = @_; 1489 my ($path) = @_;
1779} 2037}
1780 2038
1781=item $maps = cf::map::tmp_maps 2039=item $maps = cf::map::tmp_maps
1782 2040
1783Returns an arrayref with all map paths of currently instantiated and saved 2041Returns an arrayref with all map paths of currently instantiated and saved
1784maps. 2042maps. May block.
1785 2043
1786=cut 2044=cut
1787 2045
1788sub tmp_maps() { 2046sub tmp_maps() {
1789 [ 2047 [
1790 map { 2048 map {
1791 utf8::decode $_; 2049 utf8::decode $_;
1792 !/\.(?:pst|meta)$/ && /^$PATH_SEP/o 2050 /\.map$/
1793 ? normalise $_ 2051 ? normalise $_
1794 : () 2052 : ()
1795 } @{ aio_readdir $TMPDIR or [] } 2053 } @{ aio_readdir $TMPDIR or [] }
1796 ] 2054 ]
1797} 2055}
1798 2056
2057=item $maps = cf::map::random_maps
2058
2059Returns an arrayref with all map paths of currently instantiated and saved
2060random maps. May block.
2061
2062=cut
2063
2064sub random_maps() {
2065 [
2066 map {
2067 utf8::decode $_;
2068 /\.map$/
2069 ? normalise "?random/$_"
2070 : ()
2071 } @{ aio_readdir $RANDOMDIR or [] }
2072 ]
2073}
2074
1799=item cf::map::unique_maps 2075=item cf::map::unique_maps
1800 2076
1801Returns an arrayref of paths of all shared maps that have 2077Returns an arrayref of paths of all shared maps that have
1802instantiated unique items. May block. 2078instantiated unique items. May block.
1803 2079
1805 2081
1806sub unique_maps() { 2082sub unique_maps() {
1807 [ 2083 [
1808 map { 2084 map {
1809 utf8::decode $_; 2085 utf8::decode $_;
1810 !/\.(?:pst|meta)$/ && /^$PATH_SEP/o 2086 /\.map$/
1811 ? normalise $_ 2087 ? normalise $_
1812 : () 2088 : ()
1813 } @{ aio_readdir $UNIQUEDIR or [] } 2089 } @{ aio_readdir $UNIQUEDIR or [] }
1814 ] 2090 ]
1815} 2091}
1865 2141
1866 $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4; 2142 $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1867 2143
1868 if ($self->{record_replies}) { 2144 if ($self->{record_replies}) {
1869 push @{ $self->{record_replies} }, [$npc, $msg, $flags]; 2145 push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2146
1870 } else { 2147 } else {
2148 my $pl = $self->contr;
2149
2150 if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2151 my $diag = $pl->{npc_dialog};
2152 $diag->{pl}->ext_reply (
2153 $diag->{id},
2154 msgtype => "reply",
2155 msg => $diag->{pl}->expand_cfpod ($msg),
2156 add_topics => []
2157 );
2158
2159 } else {
1871 $msg = $npc->name . " says: $msg" if $npc; 2160 $msg = $npc->name . " says: $msg" if $npc;
1872 $self->message ($msg, $flags); 2161 $self->message ($msg, $flags);
2162 }
1873 } 2163 }
1874} 2164}
1875 2165
1876=item $player_object->may ("access") 2166=item $player_object->may ("access")
1877 2167
2086 my $hp = $exit->stats->hp; 2376 my $hp = $exit->stats->hp;
2087 my $sp = $exit->stats->sp; 2377 my $sp = $exit->stats->sp;
2088 2378
2089 $self->enter_link; 2379 $self->enter_link;
2090 2380
2381 # if exit is damned, update players death & WoR home-position
2382 $self->contr->savebed ($slaying, $hp, $sp)
2383 if $exit->flag (FLAG_DAMNED);
2384
2091 (async { 2385 (async {
2092 $self->deactivate_recursive; # just to be sure 2386 $self->deactivate_recursive; # just to be sure
2093 unless (eval { 2387 unless (eval {
2094 $self->goto ($slaying, $hp, $sp); 2388 $self->goto ($slaying, $hp, $sp);
2095 2389
2122 2416
2123 utf8::encode $text; 2417 utf8::encode $text;
2124 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text); 2418 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2125} 2419}
2126 2420
2421=item $client->send_msg ($color, $type, $msg, [extra...])
2422
2423Send a drawinfo or msg packet to the client, formatting the msg for the
2424client if neccessary. C<$type> should be a string identifying the type of
2425the message, with C<log> being the default. If C<$color> is negative, suppress
2426the message unless the client supports the msg packet.
2427
2428=cut
2429
2430sub cf::client::send_msg {
2431 my ($self, $color, $type, $msg, @extra) = @_;
2432
2433 $msg = $self->pl->expand_cfpod ($msg);
2434
2435 return unless @extra || length $msg;
2436
2437 if ($self->can_msg) {
2438 $self->send_packet ("msg " . $self->{json_coder}->encode ([$color, $type, $msg, @extra]));
2439 } else {
2440 # replace some tags by gcfclient-compatible ones
2441 for ($msg) {
2442 1 while
2443 s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2444 || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2445 || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2446 || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2447 || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2448 }
2449
2450 if ($color >= 0) {
2451 if (0 && $msg =~ /\[/) {
2452 $self->send_packet ("drawextinfo $color 4 0 $msg")
2453 } else {
2454 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2455 $self->send_packet ("drawinfo $color $msg")
2456 }
2457 }
2458 }
2459}
2460
2127=item $client->ext_event ($type, %msg) 2461=item $client->ext_event ($type, %msg)
2128 2462
2129Sends an exti event to the client. 2463Sends an ext event to the client.
2130 2464
2131=cut 2465=cut
2132 2466
2133sub cf::client::ext_event($$%) { 2467sub cf::client::ext_event($$%) {
2134 my ($self, $type, %msg) = @_; 2468 my ($self, $type, %msg) = @_;
2135 2469
2136 $msg{msgtype} = "event_$type"; 2470 $msg{msgtype} = "event_$type";
2137 $self->send_packet ("ext " . cf::to_json \%msg); 2471 $self->send_packet ("ext " . $self->{json_coder}->encode (\%msg));
2138} 2472}
2139 2473
2140=item $success = $client->query ($flags, "text", \&cb) 2474=item $success = $client->query ($flags, "text", \&cb)
2141 2475
2142Queues a query to the client, calling the given callback with 2476Queues a query to the client, calling the given callback with
2143the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>, 2477the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2144C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>. 2478C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2145 2479
2146Queries can fail, so check the return code. Or don't, as queries will become 2480Queries can fail, so check the return code. Or don't, as queries will
2147reliable at some point in the future. 2481become reliable at some point in the future.
2148 2482
2149=cut 2483=cut
2150 2484
2151sub cf::client::query { 2485sub cf::client::query {
2152 my ($self, $flags, $text, $cb) = @_; 2486 my ($self, $flags, $text, $cb) = @_;
2160 utf8::encode $text; 2494 utf8::encode $text;
2161 push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb]; 2495 push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2162 2496
2163 $self->send_packet ($self->{query_queue}[0][0]) 2497 $self->send_packet ($self->{query_queue}[0][0])
2164 if @{ $self->{query_queue} } == 1; 2498 if @{ $self->{query_queue} } == 1;
2499
2500 1
2165} 2501}
2166 2502
2167cf::client->attach ( 2503cf::client->attach (
2504 on_connect => sub {
2505 my ($ns) = @_;
2506
2507 $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2508 },
2168 on_reply => sub { 2509 on_reply => sub {
2169 my ($ns, $msg) = @_; 2510 my ($ns, $msg) = @_;
2170 2511
2171 # this weird shuffling is so that direct followup queries 2512 # this weird shuffling is so that direct followup queries
2172 # get handled first 2513 # get handled first
2184 } else { 2525 } else {
2185 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM; 2526 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2186 } 2527 }
2187 } 2528 }
2188 }, 2529 },
2530 on_exticmd => sub {
2531 my ($ns, $buf) = @_;
2532
2533 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2534
2535 if (ref $msg) {
2536 if (my $cb = $EXTICMD{$msg->{msgtype}}) {
2537 if (my %reply = $cb->($ns, $msg)) {
2538 $reply{msgid} = $msg->{msgid};
2539 $ns->send ("ext " . $ns->{json_coder}->encode (\%reply));
2540 }
2541 }
2542 } else {
2543 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2544 }
2545
2546 cf::override;
2547 },
2189); 2548);
2190 2549
2191=item $client->async (\&cb) 2550=item $client->async (\&cb)
2192 2551
2193Create a new coroutine, running the specified callback. The coroutine will 2552Create a new coroutine, running the specified callback. The coroutine will
2242 2601
2243=pod 2602=pod
2244 2603
2245The following functions and methods are available within a safe environment: 2604The following functions and methods are available within a safe environment:
2246 2605
2247 cf::object contr pay_amount pay_player map 2606 cf::object
2607 contr pay_amount pay_player map x y force_find force_add
2608 insert remove
2609
2248 cf::object::player player 2610 cf::object::player
2249 cf::player peaceful 2611 player
2250 cf::map trigger 2612
2613 cf::player
2614 peaceful
2615
2616 cf::map
2617 trigger
2251 2618
2252=cut 2619=cut
2253 2620
2254for ( 2621for (
2255 ["cf::object" => qw(contr pay_amount pay_player map)], 2622 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2623 insert remove)],
2256 ["cf::object::player" => qw(player)], 2624 ["cf::object::player" => qw(player)],
2257 ["cf::player" => qw(peaceful)], 2625 ["cf::player" => qw(peaceful)],
2258 ["cf::map" => qw(trigger)], 2626 ["cf::map" => qw(trigger)],
2259) { 2627) {
2260 no strict 'refs'; 2628 no strict 'refs';
2329} 2697}
2330 2698
2331=back 2699=back
2332 2700
2333=cut 2701=cut
2334
2335#############################################################################
2336
2337=head2 EXTENSION DATABASE SUPPORT
2338
2339Crossfire maintains a very simple database for extension use. It can
2340currently store binary data only (use Compress::LZF::sfreeze_cr/sthaw to
2341convert to/from binary).
2342
2343The parameter C<$family> should best start with the name of the extension
2344using it, it should be unique.
2345
2346=over 4
2347
2348=item $value = cf::db_get $family => $key
2349
2350Returns a single value from the database.
2351
2352=item cf::db_put $family => $key => $value
2353
2354Stores the given C<$value> in the family.
2355
2356=cut
2357
2358our $DB;
2359
2360sub db_init {
2361 unless ($DB) {
2362 $DB = BDB::db_create $DB_ENV;
2363
2364 cf::sync_job {
2365 eval {
2366 $DB->set_flags (BDB::CHKSUM);
2367
2368 BDB::db_open $DB, undef, "db", undef, BDB::BTREE,
2369 BDB::CREATE | BDB::AUTO_COMMIT, 0666;
2370 cf::cleanup "db_open(db): $!" if $!;
2371 };
2372 cf::cleanup "db_open(db): $@" if $@;
2373 };
2374 }
2375}
2376
2377sub db_get($$) {
2378 my $key = "$_[0]/$_[1]";
2379
2380 cf::sync_job {
2381 BDB::db_get $DB, undef, $key, my $data;
2382
2383 $! ? ()
2384 : $data
2385 }
2386}
2387
2388sub db_put($$$) {
2389 BDB::dbreq_pri 4;
2390 BDB::db_put $DB, undef, "$_[0]/$_[1]", $_[2], 0, sub { };
2391}
2392
2393=item cf::cache $id => [$paths...], $processversion => $process
2394
2395Generic caching function that returns the value of the resource $id,
2396caching and regenerating as required.
2397
2398This function can block.
2399
2400=cut
2401
2402sub cache {
2403 my ($id, $src, $processversion, $process) = @_;
2404
2405 my $meta =
2406 join "\x00",
2407 $processversion,
2408 map {
2409 aio_stat $_
2410 and Carp::croak "$_: $!";
2411
2412 ($_, (stat _)[7,9])
2413 } @$src;
2414
2415 my $dbmeta = db_get cache => "$id/meta";
2416 if ($dbmeta ne $meta) {
2417 # changed, we may need to process
2418
2419 my @data;
2420 my $md5;
2421
2422 for (0 .. $#$src) {
2423 0 <= aio_load $src->[$_], $data[$_]
2424 or Carp::croak "$src->[$_]: $!";
2425 }
2426
2427 # if processing is expensive, check
2428 # checksum first
2429 if (1) {
2430 $md5 =
2431 join "\x00",
2432 $processversion,
2433 map {
2434 Coro::cede;
2435 ($src->[$_], Digest::MD5::md5_hex $data[$_])
2436 } 0.. $#$src;
2437
2438
2439 my $dbmd5 = db_get cache => "$id/md5";
2440 if ($dbmd5 eq $md5) {
2441 db_put cache => "$id/meta", $meta;
2442
2443 return db_get cache => "$id/data";
2444 }
2445 }
2446
2447 my $t1 = Time::HiRes::time;
2448 my $data = $process->(\@data);
2449 my $t2 = Time::HiRes::time;
2450
2451 warn "cache: '$id' processed in ", $t2 - $t1, "s\n";
2452
2453 db_put cache => "$id/data", $data;
2454 db_put cache => "$id/md5" , $md5;
2455 db_put cache => "$id/meta", $meta;
2456
2457 return $data;
2458 }
2459
2460 db_get cache => "$id/data"
2461}
2462
2463=item fork_call { }, $args
2464
2465Executes the given code block with the given arguments in a seperate
2466process, returning the results. Everything must be serialisable with
2467Coro::Storable. May, of course, block. Note that the executed sub may
2468never block itself or use any form of Event handling.
2469
2470=cut
2471
2472sub fork_call(&@) {
2473 my ($cb, @args) = @_;
2474
2475# socketpair my $fh1, my $fh2, Socket::AF_UNIX, Socket::SOCK_STREAM, Socket::PF_UNSPEC
2476# or die "socketpair: $!";
2477 pipe my $fh1, my $fh2
2478 or die "pipe: $!";
2479
2480 if (my $pid = fork) {
2481 close $fh2;
2482
2483 my $res = (Coro::Handle::unblock $fh1)->readline (undef);
2484 $res = Coro::Storable::thaw $res;
2485
2486 waitpid $pid, 0; # should not block anymore, we expect the child to simply behave
2487
2488 die $$res unless "ARRAY" eq ref $res;
2489
2490 return wantarray ? @$res : $res->[-1];
2491 } else {
2492 reset_signals;
2493 local $SIG{__WARN__};
2494 local $SIG{__DIE__};
2495 eval {
2496 close $fh1;
2497
2498 my @res = eval { $cb->(@args) };
2499 syswrite $fh2, Coro::Storable::freeze +($@ ? \"$@" : \@res);
2500 };
2501
2502 warn $@ if $@;
2503 _exit 0;
2504 }
2505}
2506 2702
2507############################################################################# 2703#############################################################################
2508# the server's init and main functions 2704# the server's init and main functions
2509 2705
2510sub load_facedata($) { 2706sub load_facedata($) {
2568 load_facedata "$DATADIR/facedata" 2764 load_facedata "$DATADIR/facedata"
2569 or die "unable to load facedata\n"; 2765 or die "unable to load facedata\n";
2570} 2766}
2571 2767
2572sub reload_archetypes { 2768sub reload_archetypes {
2769 load_resource_file "$DATADIR/archetypes"
2770 or die "unable to load archetypes\n";
2771 #d# NEED to laod twice to resolve forward references
2772 # this really needs to be done in an extra post-pass
2773 # (which needs to be synchronous, so solve it differently)
2573 load_resource_file "$DATADIR/archetypes" 2774 load_resource_file "$DATADIR/archetypes"
2574 or die "unable to load archetypes\n"; 2775 or die "unable to load archetypes\n";
2575} 2776}
2576 2777
2577sub reload_treasures { 2778sub reload_treasures {
2649 }, 2850 },
2650 ); 2851 );
2651 } 2852 }
2652} 2853}
2653 2854
2855sub write_runtime {
2856 my $runtime = "$LOCALDIR/runtime";
2857
2858 # first touch the runtime file to show we are still running:
2859 # the fsync below can take a very very long time.
2860
2861 IO::AIO::aio_utime $runtime, undef, undef;
2862
2863 my $guard = cf::lock_acquire "write_runtime";
2864
2865 my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
2866 or return;
2867
2868 my $value = $cf::RUNTIME + 90 + 10;
2869 # 10 is the runtime save interval, for a monotonic clock
2870 # 60 allows for the watchdog to kill the server.
2871
2872 (aio_write $fh, 0, (length $value), $value, 0) <= 0
2873 and return;
2874
2875 # always fsync - this file is important
2876 aio_fsync $fh
2877 and return;
2878
2879 # touch it again to show we are up-to-date
2880 aio_utime $fh, undef, undef;
2881
2882 close $fh
2883 or return;
2884
2885 aio_rename "$runtime~", $runtime
2886 and return;
2887
2888 warn "runtime file written.\n";
2889
2890 1
2891}
2892
2654sub emergency_save() { 2893sub emergency_save() {
2655 my $freeze_guard = cf::freeze_mainloop; 2894 my $freeze_guard = cf::freeze_mainloop;
2656 2895
2657 warn "enter emergency perl save\n"; 2896 warn "enter emergency perl save\n";
2658 2897
2733 %EXT_CORO = (); 2972 %EXT_CORO = ();
2734 2973
2735 warn "removing commands"; 2974 warn "removing commands";
2736 %COMMAND = (); 2975 %COMMAND = ();
2737 2976
2738 warn "removing ext commands"; 2977 warn "removing ext/exti commands";
2739 %EXTCMD = (); 2978 %EXTCMD = ();
2979 %EXTICMD = ();
2740 2980
2741 warn "unloading/nuking all extensions"; 2981 warn "unloading/nuking all extensions";
2742 for my $pkg (@EXTS) { 2982 for my $pkg (@EXTS) {
2743 warn "... unloading $pkg"; 2983 warn "... unloading $pkg";
2744 2984

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines