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.301 by root, Tue Jul 10 07:01:51 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 $_
1441}
1442
1443sub hintmode {
1444 $_[0]{hintmode} = $_[1] if @_ > 1;
1445 $_[0]{hintmode}
1446}
1447
1190=item $player->ext_reply ($msgid, %msg) 1448=item $player->ext_reply ($msgid, %msg)
1191 1449
1192Sends an ext reply to the player. 1450Sends an ext reply to the player.
1193 1451
1194=cut 1452=cut
1195 1453
1196sub ext_reply($$%) { 1454sub ext_reply($$%) {
1197 my ($self, $id, %msg) = @_; 1455 my ($self, $id, %msg) = @_;
1198 1456
1199 $msg{msgid} = $id; 1457 $msg{msgid} = $id;
1200 1458 $self->send ("ext " . $self->ns->{json_coder}->encode (\%msg));
1201 $self->send ("ext " . cf::to_json \%msg);
1202} 1459}
1203 1460
1204=item $player->ext_event ($type, %msg) 1461=item $player->ext_event ($type, %msg)
1205 1462
1206Sends an ext event to the client. 1463Sends an ext event to the client.
1221 1478
1222package cf::region; 1479package cf::region;
1223 1480
1224=item cf::region::find_by_path $path 1481=item cf::region::find_by_path $path
1225 1482
1226Tries to decuce the probable region for a map knowing only its path. 1483Tries to decuce the likely region for a map knowing only its path.
1227 1484
1228=cut 1485=cut
1229 1486
1230sub find_by_path($) { 1487sub find_by_path($) {
1231 my ($path) = @_; 1488 my ($path) = @_;
1779} 2036}
1780 2037
1781=item $maps = cf::map::tmp_maps 2038=item $maps = cf::map::tmp_maps
1782 2039
1783Returns an arrayref with all map paths of currently instantiated and saved 2040Returns an arrayref with all map paths of currently instantiated and saved
1784maps. 2041maps. May block.
1785 2042
1786=cut 2043=cut
1787 2044
1788sub tmp_maps() { 2045sub tmp_maps() {
1789 [ 2046 [
1790 map { 2047 map {
1791 utf8::decode $_; 2048 utf8::decode $_;
1792 !/\.(?:pst|meta)$/ && /^$PATH_SEP/o 2049 /\.map$/
1793 ? normalise $_ 2050 ? normalise $_
1794 : () 2051 : ()
1795 } @{ aio_readdir $TMPDIR or [] } 2052 } @{ aio_readdir $TMPDIR or [] }
1796 ] 2053 ]
1797} 2054}
1798 2055
2056=item $maps = cf::map::random_maps
2057
2058Returns an arrayref with all map paths of currently instantiated and saved
2059random maps. May block.
2060
2061=cut
2062
2063sub random_maps() {
2064 [
2065 map {
2066 utf8::decode $_;
2067 /\.map$/
2068 ? normalise "?random/$_"
2069 : ()
2070 } @{ aio_readdir $RANDOMDIR or [] }
2071 ]
2072}
2073
1799=item cf::map::unique_maps 2074=item cf::map::unique_maps
1800 2075
1801Returns an arrayref of paths of all shared maps that have 2076Returns an arrayref of paths of all shared maps that have
1802instantiated unique items. May block. 2077instantiated unique items. May block.
1803 2078
1805 2080
1806sub unique_maps() { 2081sub unique_maps() {
1807 [ 2082 [
1808 map { 2083 map {
1809 utf8::decode $_; 2084 utf8::decode $_;
1810 !/\.(?:pst|meta)$/ && /^$PATH_SEP/o 2085 /\.map$/
1811 ? normalise $_ 2086 ? normalise $_
1812 : () 2087 : ()
1813 } @{ aio_readdir $UNIQUEDIR or [] } 2088 } @{ aio_readdir $UNIQUEDIR or [] }
1814 ] 2089 ]
1815} 2090}
1865 2140
1866 $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4; 2141 $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1867 2142
1868 if ($self->{record_replies}) { 2143 if ($self->{record_replies}) {
1869 push @{ $self->{record_replies} }, [$npc, $msg, $flags]; 2144 push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2145
1870 } else { 2146 } else {
2147 my $pl = $self->contr;
2148
2149 if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2150 my $diag = $pl->{npc_dialog};
2151 $diag->{pl}->ext_reply (
2152 $diag->{id},
2153 msgtype => "reply",
2154 msg => $diag->{pl}->expand_cfpod ($msg),
2155 add_topics => []
2156 );
2157
2158 } else {
1871 $msg = $npc->name . " says: $msg" if $npc; 2159 $msg = $npc->name . " says: $msg" if $npc;
1872 $self->message ($msg, $flags); 2160 $self->message ($msg, $flags);
2161 }
1873 } 2162 }
1874} 2163}
1875 2164
1876=item $player_object->may ("access") 2165=item $player_object->may ("access")
1877 2166
2086 my $hp = $exit->stats->hp; 2375 my $hp = $exit->stats->hp;
2087 my $sp = $exit->stats->sp; 2376 my $sp = $exit->stats->sp;
2088 2377
2089 $self->enter_link; 2378 $self->enter_link;
2090 2379
2380 # if exit is damned, update players death & WoR home-position
2381 $self->contr->savebed ($slaying, $hp, $sp)
2382 if $exit->flag (FLAG_DAMNED);
2383
2091 (async { 2384 (async {
2092 $self->deactivate_recursive; # just to be sure 2385 $self->deactivate_recursive; # just to be sure
2093 unless (eval { 2386 unless (eval {
2094 $self->goto ($slaying, $hp, $sp); 2387 $self->goto ($slaying, $hp, $sp);
2095 2388
2122 2415
2123 utf8::encode $text; 2416 utf8::encode $text;
2124 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text); 2417 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2125} 2418}
2126 2419
2420=item $client->send_msg ($color, $type, $msg, [extra...])
2421
2422Send a drawinfo or msg packet to the client, formatting the msg for the
2423client if neccessary. C<$type> should be a string identifying the type of
2424the message, with C<log> being the default. If C<$color> is negative, suppress
2425the message unless the client supports the msg packet.
2426
2427=cut
2428
2429sub cf::client::send_msg {
2430 my ($self, $color, $type, $msg, @extra) = @_;
2431
2432 $msg = $self->pl->expand_cfpod ($msg);
2433
2434 return unless @extra || length $msg;
2435
2436 if ($self->can_msg) {
2437 $self->send_packet ("msg " . $self->{json_coder}->encode ([$color, $type, $msg, @extra]));
2438 } else {
2439 # replace some tags by gcfclient-compatible ones
2440 for ($msg) {
2441 1 while
2442 s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2443 || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2444 || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2445 || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2446 || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2447 }
2448
2449 if ($color >= 0) {
2450 if (0 && $msg =~ /\[/) {
2451 $self->send_packet ("drawextinfo $color 4 0 $msg")
2452 } else {
2453 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2454 $self->send_packet ("drawinfo $color $msg")
2455 }
2456 }
2457 }
2458}
2459
2127=item $client->ext_event ($type, %msg) 2460=item $client->ext_event ($type, %msg)
2128 2461
2129Sends an exti event to the client. 2462Sends an ext event to the client.
2130 2463
2131=cut 2464=cut
2132 2465
2133sub cf::client::ext_event($$%) { 2466sub cf::client::ext_event($$%) {
2134 my ($self, $type, %msg) = @_; 2467 my ($self, $type, %msg) = @_;
2135 2468
2136 $msg{msgtype} = "event_$type"; 2469 $msg{msgtype} = "event_$type";
2137 $self->send_packet ("ext " . cf::to_json \%msg); 2470 $self->send_packet ("ext " . $self->{json_coder}->encode (\%msg));
2138} 2471}
2139 2472
2140=item $success = $client->query ($flags, "text", \&cb) 2473=item $success = $client->query ($flags, "text", \&cb)
2141 2474
2142Queues a query to the client, calling the given callback with 2475Queues a query to the client, calling the given callback with
2143the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>, 2476the 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>. 2477C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2145 2478
2146Queries can fail, so check the return code. Or don't, as queries will become 2479Queries can fail, so check the return code. Or don't, as queries will
2147reliable at some point in the future. 2480become reliable at some point in the future.
2148 2481
2149=cut 2482=cut
2150 2483
2151sub cf::client::query { 2484sub cf::client::query {
2152 my ($self, $flags, $text, $cb) = @_; 2485 my ($self, $flags, $text, $cb) = @_;
2160 utf8::encode $text; 2493 utf8::encode $text;
2161 push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb]; 2494 push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2162 2495
2163 $self->send_packet ($self->{query_queue}[0][0]) 2496 $self->send_packet ($self->{query_queue}[0][0])
2164 if @{ $self->{query_queue} } == 1; 2497 if @{ $self->{query_queue} } == 1;
2498
2499 1
2165} 2500}
2166 2501
2167cf::client->attach ( 2502cf::client->attach (
2503 on_connect => sub {
2504 my ($ns) = @_;
2505
2506 $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2507 },
2168 on_reply => sub { 2508 on_reply => sub {
2169 my ($ns, $msg) = @_; 2509 my ($ns, $msg) = @_;
2170 2510
2171 # this weird shuffling is so that direct followup queries 2511 # this weird shuffling is so that direct followup queries
2172 # get handled first 2512 # get handled first
2184 } else { 2524 } else {
2185 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM; 2525 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2186 } 2526 }
2187 } 2527 }
2188 }, 2528 },
2529 on_exticmd => sub {
2530 my ($ns, $buf) = @_;
2531
2532 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2533
2534 if (ref $msg) {
2535 if (my $cb = $EXTICMD{$msg->{msgtype}}) {
2536 if (my %reply = $cb->($ns, $msg)) {
2537 $reply{msgid} = $msg->{msgid};
2538 $ns->send ("ext " . $ns->{json_coder}->encode (\%reply));
2539 }
2540 }
2541 } else {
2542 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2543 }
2544
2545 cf::override;
2546 },
2189); 2547);
2190 2548
2191=item $client->async (\&cb) 2549=item $client->async (\&cb)
2192 2550
2193Create a new coroutine, running the specified callback. The coroutine will 2551Create a new coroutine, running the specified callback. The coroutine will
2242 2600
2243=pod 2601=pod
2244 2602
2245The following functions and methods are available within a safe environment: 2603The following functions and methods are available within a safe environment:
2246 2604
2247 cf::object contr pay_amount pay_player map 2605 cf::object
2606 contr pay_amount pay_player map x y force_find force_add
2607 insert remove
2608
2248 cf::object::player player 2609 cf::object::player
2249 cf::player peaceful 2610 player
2250 cf::map trigger 2611
2612 cf::player
2613 peaceful
2614
2615 cf::map
2616 trigger
2251 2617
2252=cut 2618=cut
2253 2619
2254for ( 2620for (
2255 ["cf::object" => qw(contr pay_amount pay_player map)], 2621 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2622 insert remove)],
2256 ["cf::object::player" => qw(player)], 2623 ["cf::object::player" => qw(player)],
2257 ["cf::player" => qw(peaceful)], 2624 ["cf::player" => qw(peaceful)],
2258 ["cf::map" => qw(trigger)], 2625 ["cf::map" => qw(trigger)],
2259) { 2626) {
2260 no strict 'refs'; 2627 no strict 'refs';
2329} 2696}
2330 2697
2331=back 2698=back
2332 2699
2333=cut 2700=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 2701
2507############################################################################# 2702#############################################################################
2508# the server's init and main functions 2703# the server's init and main functions
2509 2704
2510sub load_facedata($) { 2705sub load_facedata($) {
2568 load_facedata "$DATADIR/facedata" 2763 load_facedata "$DATADIR/facedata"
2569 or die "unable to load facedata\n"; 2764 or die "unable to load facedata\n";
2570} 2765}
2571 2766
2572sub reload_archetypes { 2767sub reload_archetypes {
2768 load_resource_file "$DATADIR/archetypes"
2769 or die "unable to load archetypes\n";
2770 #d# NEED to laod twice to resolve forward references
2771 # this really needs to be done in an extra post-pass
2772 # (which needs to be synchronous, so solve it differently)
2573 load_resource_file "$DATADIR/archetypes" 2773 load_resource_file "$DATADIR/archetypes"
2574 or die "unable to load archetypes\n"; 2774 or die "unable to load archetypes\n";
2575} 2775}
2576 2776
2577sub reload_treasures { 2777sub reload_treasures {
2649 }, 2849 },
2650 ); 2850 );
2651 } 2851 }
2652} 2852}
2653 2853
2854sub write_runtime {
2855 my $runtime = "$LOCALDIR/runtime";
2856
2857 # first touch the runtime file to show we are still running:
2858 # the fsync below can take a very very long time.
2859
2860 IO::AIO::aio_utime $runtime, undef, undef;
2861
2862 my $guard = cf::lock_acquire "write_runtime";
2863
2864 my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
2865 or return;
2866
2867 my $value = $cf::RUNTIME + 90 + 10;
2868 # 10 is the runtime save interval, for a monotonic clock
2869 # 60 allows for the watchdog to kill the server.
2870
2871 (aio_write $fh, 0, (length $value), $value, 0) <= 0
2872 and return;
2873
2874 # always fsync - this file is important
2875 aio_fsync $fh
2876 and return;
2877
2878 # touch it again to show we are up-to-date
2879 aio_utime $fh, undef, undef;
2880
2881 close $fh
2882 or return;
2883
2884 aio_rename "$runtime~", $runtime
2885 and return;
2886
2887 warn "runtime file written.\n";
2888
2889 1
2890}
2891
2654sub emergency_save() { 2892sub emergency_save() {
2655 my $freeze_guard = cf::freeze_mainloop; 2893 my $freeze_guard = cf::freeze_mainloop;
2656 2894
2657 warn "enter emergency perl save\n"; 2895 warn "enter emergency perl save\n";
2658 2896
2733 %EXT_CORO = (); 2971 %EXT_CORO = ();
2734 2972
2735 warn "removing commands"; 2973 warn "removing commands";
2736 %COMMAND = (); 2974 %COMMAND = ();
2737 2975
2738 warn "removing ext commands"; 2976 warn "removing ext/exti commands";
2739 %EXTCMD = (); 2977 %EXTCMD = ();
2978 %EXTICMD = ();
2740 2979
2741 warn "unloading/nuking all extensions"; 2980 warn "unloading/nuking all extensions";
2742 for my $pkg (@EXTS) { 2981 for my $pkg (@EXTS) {
2743 warn "... unloading $pkg"; 2982 warn "... unloading $pkg";
2744 2983

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines