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.279 by root, Mon Jun 11 22:16:53 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);
1018 }; 1195 };
1019} 1196}
1020 1197
1021############################################################################# 1198#############################################################################
1022 1199
1200=back
1201
1023=head2 CORE EXTENSIONS 1202=head2 CORE EXTENSIONS
1024 1203
1025Functions and methods that extend core crossfire objects. 1204Functions and methods that extend core crossfire objects.
1026 1205
1027=cut 1206=cut
1214 } 1393 }
1215 1394
1216 \@paths 1395 \@paths
1217} 1396}
1218 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
1219=item $player->ext_reply ($msgid, %msg) 1449=item $player->ext_reply ($msgid, %msg)
1220 1450
1221Sends an ext reply to the player. 1451Sends an ext reply to the player.
1222 1452
1223=cut 1453=cut
1224 1454
1225sub ext_reply($$%) { 1455sub ext_reply($$%) {
1226 my ($self, $id, %msg) = @_; 1456 my ($self, $id, %msg) = @_;
1227 1457
1228 $msg{msgid} = $id; 1458 $msg{msgid} = $id;
1229 1459 $self->send ("ext " . $self->ns->{json_coder}->encode (\%msg));
1230 $self->send ("ext " . cf::to_json \%msg);
1231} 1460}
1232 1461
1233=item $player->ext_event ($type, %msg) 1462=item $player->ext_event ($type, %msg)
1234 1463
1235Sends an ext event to the client. 1464Sends an ext event to the client.
1250 1479
1251package cf::region; 1480package cf::region;
1252 1481
1253=item cf::region::find_by_path $path 1482=item cf::region::find_by_path $path
1254 1483
1255Tries 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.
1256 1485
1257=cut 1486=cut
1258 1487
1259sub find_by_path($) { 1488sub find_by_path($) {
1260 my ($path) = @_; 1489 my ($path) = @_;
1912 2141
1913 $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4; 2142 $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1914 2143
1915 if ($self->{record_replies}) { 2144 if ($self->{record_replies}) {
1916 push @{ $self->{record_replies} }, [$npc, $msg, $flags]; 2145 push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2146
1917 } 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 {
1918 $msg = $npc->name . " says: $msg" if $npc; 2160 $msg = $npc->name . " says: $msg" if $npc;
1919 $self->message ($msg, $flags); 2161 $self->message ($msg, $flags);
2162 }
1920 } 2163 }
1921} 2164}
1922 2165
1923=item $player_object->may ("access") 2166=item $player_object->may ("access")
1924 2167
2133 my $hp = $exit->stats->hp; 2376 my $hp = $exit->stats->hp;
2134 my $sp = $exit->stats->sp; 2377 my $sp = $exit->stats->sp;
2135 2378
2136 $self->enter_link; 2379 $self->enter_link;
2137 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
2138 (async { 2385 (async {
2139 $self->deactivate_recursive; # just to be sure 2386 $self->deactivate_recursive; # just to be sure
2140 unless (eval { 2387 unless (eval {
2141 $self->goto ($slaying, $hp, $sp); 2388 $self->goto ($slaying, $hp, $sp);
2142 2389
2169 2416
2170 utf8::encode $text; 2417 utf8::encode $text;
2171 $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);
2172} 2419}
2173 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
2174=item $client->ext_event ($type, %msg) 2461=item $client->ext_event ($type, %msg)
2175 2462
2176Sends an exti event to the client. 2463Sends an ext event to the client.
2177 2464
2178=cut 2465=cut
2179 2466
2180sub cf::client::ext_event($$%) { 2467sub cf::client::ext_event($$%) {
2181 my ($self, $type, %msg) = @_; 2468 my ($self, $type, %msg) = @_;
2182 2469
2183 $msg{msgtype} = "event_$type"; 2470 $msg{msgtype} = "event_$type";
2184 $self->send_packet ("ext " . cf::to_json \%msg); 2471 $self->send_packet ("ext " . $self->{json_coder}->encode (\%msg));
2185} 2472}
2186 2473
2187=item $success = $client->query ($flags, "text", \&cb) 2474=item $success = $client->query ($flags, "text", \&cb)
2188 2475
2189Queues a query to the client, calling the given callback with 2476Queues a query to the client, calling the given callback with
2190the 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>,
2191C<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>.
2192 2479
2193Queries 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
2194reliable at some point in the future. 2481become reliable at some point in the future.
2195 2482
2196=cut 2483=cut
2197 2484
2198sub cf::client::query { 2485sub cf::client::query {
2199 my ($self, $flags, $text, $cb) = @_; 2486 my ($self, $flags, $text, $cb) = @_;
2207 utf8::encode $text; 2494 utf8::encode $text;
2208 push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb]; 2495 push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2209 2496
2210 $self->send_packet ($self->{query_queue}[0][0]) 2497 $self->send_packet ($self->{query_queue}[0][0])
2211 if @{ $self->{query_queue} } == 1; 2498 if @{ $self->{query_queue} } == 1;
2499
2500 1
2212} 2501}
2213 2502
2214cf::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 },
2215 on_reply => sub { 2509 on_reply => sub {
2216 my ($ns, $msg) = @_; 2510 my ($ns, $msg) = @_;
2217 2511
2218 # this weird shuffling is so that direct followup queries 2512 # this weird shuffling is so that direct followup queries
2219 # get handled first 2513 # get handled first
2231 } else { 2525 } else {
2232 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM; 2526 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2233 } 2527 }
2234 } 2528 }
2235 }, 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 },
2236); 2548);
2237 2549
2238=item $client->async (\&cb) 2550=item $client->async (\&cb)
2239 2551
2240Create a new coroutine, running the specified callback. The coroutine will 2552Create a new coroutine, running the specified callback. The coroutine will
2289 2601
2290=pod 2602=pod
2291 2603
2292The following functions and methods are available within a safe environment: 2604The following functions and methods are available within a safe environment:
2293 2605
2294 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
2295 cf::object::player player 2610 cf::object::player
2296 cf::player peaceful 2611 player
2297 cf::map trigger 2612
2613 cf::player
2614 peaceful
2615
2616 cf::map
2617 trigger
2298 2618
2299=cut 2619=cut
2300 2620
2301for ( 2621for (
2302 ["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)],
2303 ["cf::object::player" => qw(player)], 2624 ["cf::object::player" => qw(player)],
2304 ["cf::player" => qw(peaceful)], 2625 ["cf::player" => qw(peaceful)],
2305 ["cf::map" => qw(trigger)], 2626 ["cf::map" => qw(trigger)],
2306) { 2627) {
2307 no strict 'refs'; 2628 no strict 'refs';
2376} 2697}
2377 2698
2378=back 2699=back
2379 2700
2380=cut 2701=cut
2381
2382#############################################################################
2383
2384=head2 EXTENSION DATABASE SUPPORT
2385
2386Crossfire maintains a very simple database for extension use. It can
2387currently store binary data only (use Compress::LZF::sfreeze_cr/sthaw to
2388convert to/from binary).
2389
2390The parameter C<$family> should best start with the name of the extension
2391using it, it should be unique.
2392
2393=over 4
2394
2395=item $value = cf::db_get $family => $key
2396
2397Returns a single value from the database.
2398
2399=item cf::db_put $family => $key => $value
2400
2401Stores the given C<$value> in the family.
2402
2403=cut
2404
2405our $DB;
2406
2407sub db_init {
2408 unless ($DB) {
2409 $DB = BDB::db_create $DB_ENV;
2410
2411 cf::sync_job {
2412 eval {
2413 $DB->set_flags (BDB::CHKSUM);
2414
2415 BDB::db_open $DB, undef, "db", undef, BDB::BTREE,
2416 BDB::CREATE | BDB::AUTO_COMMIT, 0666;
2417 cf::cleanup "db_open(db): $!" if $!;
2418 };
2419 cf::cleanup "db_open(db): $@" if $@;
2420 };
2421 }
2422}
2423
2424sub db_get($$) {
2425 my $key = "$_[0]/$_[1]";
2426
2427 cf::sync_job {
2428 BDB::db_get $DB, undef, $key, my $data;
2429
2430 $! ? ()
2431 : $data
2432 }
2433}
2434
2435sub db_put($$$) {
2436 BDB::dbreq_pri 4;
2437 BDB::db_put $DB, undef, "$_[0]/$_[1]", $_[2], 0, sub { };
2438}
2439
2440=item cf::cache $id => [$paths...], $processversion => $process
2441
2442Generic caching function that returns the value of the resource $id,
2443caching and regenerating as required.
2444
2445This function can block.
2446
2447=cut
2448
2449sub cache {
2450 my ($id, $src, $processversion, $process) = @_;
2451
2452 my $meta =
2453 join "\x00",
2454 $processversion,
2455 map {
2456 aio_stat $_
2457 and Carp::croak "$_: $!";
2458
2459 ($_, (stat _)[7,9])
2460 } @$src;
2461
2462 my $dbmeta = db_get cache => "$id/meta";
2463 if ($dbmeta ne $meta) {
2464 # changed, we may need to process
2465
2466 my @data;
2467 my $md5;
2468
2469 for (0 .. $#$src) {
2470 0 <= aio_load $src->[$_], $data[$_]
2471 or Carp::croak "$src->[$_]: $!";
2472 }
2473
2474 # if processing is expensive, check
2475 # checksum first
2476 if (1) {
2477 $md5 =
2478 join "\x00",
2479 $processversion,
2480 map {
2481 Coro::cede;
2482 ($src->[$_], Digest::MD5::md5_hex $data[$_])
2483 } 0.. $#$src;
2484
2485
2486 my $dbmd5 = db_get cache => "$id/md5";
2487 if ($dbmd5 eq $md5) {
2488 db_put cache => "$id/meta", $meta;
2489
2490 return db_get cache => "$id/data";
2491 }
2492 }
2493
2494 my $t1 = Time::HiRes::time;
2495 my $data = $process->(\@data);
2496 my $t2 = Time::HiRes::time;
2497
2498 warn "cache: '$id' processed in ", $t2 - $t1, "s\n";
2499
2500 db_put cache => "$id/data", $data;
2501 db_put cache => "$id/md5" , $md5;
2502 db_put cache => "$id/meta", $meta;
2503
2504 return $data;
2505 }
2506
2507 db_get cache => "$id/data"
2508}
2509
2510=item fork_call { }, $args
2511
2512Executes the given code block with the given arguments in a seperate
2513process, returning the results. Everything must be serialisable with
2514Coro::Storable. May, of course, block. Note that the executed sub may
2515never block itself or use any form of Event handling.
2516
2517=cut
2518
2519sub fork_call(&@) {
2520 my ($cb, @args) = @_;
2521
2522# socketpair my $fh1, my $fh2, Socket::AF_UNIX, Socket::SOCK_STREAM, Socket::PF_UNSPEC
2523# or die "socketpair: $!";
2524 pipe my $fh1, my $fh2
2525 or die "pipe: $!";
2526
2527 if (my $pid = fork) {
2528 close $fh2;
2529
2530 my $res = (Coro::Handle::unblock $fh1)->readline (undef);
2531 $res = Coro::Storable::thaw $res;
2532
2533 waitpid $pid, 0; # should not block anymore, we expect the child to simply behave
2534
2535 die $$res unless "ARRAY" eq ref $res;
2536
2537 return wantarray ? @$res : $res->[-1];
2538 } else {
2539 reset_signals;
2540 local $SIG{__WARN__};
2541 local $SIG{__DIE__};
2542 eval {
2543 close $fh1;
2544
2545 my @res = eval { $cb->(@args) };
2546 syswrite $fh2, Coro::Storable::freeze +($@ ? \"$@" : \@res);
2547 };
2548
2549 warn $@ if $@;
2550 _exit 0;
2551 }
2552}
2553 2702
2554############################################################################# 2703#############################################################################
2555# the server's init and main functions 2704# the server's init and main functions
2556 2705
2557sub load_facedata($) { 2706sub load_facedata($) {
2615 load_facedata "$DATADIR/facedata" 2764 load_facedata "$DATADIR/facedata"
2616 or die "unable to load facedata\n"; 2765 or die "unable to load facedata\n";
2617} 2766}
2618 2767
2619sub 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)
2620 load_resource_file "$DATADIR/archetypes" 2774 load_resource_file "$DATADIR/archetypes"
2621 or die "unable to load archetypes\n"; 2775 or die "unable to load archetypes\n";
2622} 2776}
2623 2777
2624sub reload_treasures { 2778sub reload_treasures {
2696 }, 2850 },
2697 ); 2851 );
2698 } 2852 }
2699} 2853}
2700 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
2701sub emergency_save() { 2893sub emergency_save() {
2702 my $freeze_guard = cf::freeze_mainloop; 2894 my $freeze_guard = cf::freeze_mainloop;
2703 2895
2704 warn "enter emergency perl save\n"; 2896 warn "enter emergency perl save\n";
2705 2897
2780 %EXT_CORO = (); 2972 %EXT_CORO = ();
2781 2973
2782 warn "removing commands"; 2974 warn "removing commands";
2783 %COMMAND = (); 2975 %COMMAND = ();
2784 2976
2785 warn "removing ext commands"; 2977 warn "removing ext/exti commands";
2786 %EXTCMD = (); 2978 %EXTCMD = ();
2979 %EXTICMD = ();
2787 2980
2788 warn "unloading/nuking all extensions"; 2981 warn "unloading/nuking all extensions";
2789 for my $pkg (@EXTS) { 2982 for my $pkg (@EXTS) {
2790 warn "... unloading $pkg"; 2983 warn "... unloading $pkg";
2791 2984

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines