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.306 by root, Sat Jul 14 14:33:30 2007 UTC vs.
Revision 1.379 by root, Thu Oct 4 23:59:07 2007 UTC

10use Event; 10use Event;
11use Opcode; 11use Opcode;
12use Safe; 12use Safe;
13use Safe::Hole; 13use Safe::Hole;
14 14
15use Coro 3.61 (); 15use Coro 3.64 ();
16use Coro::State; 16use Coro::State;
17use Coro::Handle; 17use Coro::Handle;
18use Coro::Event; 18use Coro::Event;
19use Coro::Timer; 19use Coro::Timer;
20use Coro::Signal; 20use Coro::Signal;
21use Coro::Semaphore; 21use Coro::Semaphore;
22use Coro::AIO; 22use Coro::AIO;
23use Coro::Storable; 23use Coro::Storable;
24use Coro::Util ();
24 25
25use JSON::XS 1.4 (); 26use JSON::XS ();
26use BDB (); 27use BDB ();
27use Data::Dumper; 28use Data::Dumper;
28use Digest::MD5; 29use Digest::MD5;
29use Fcntl; 30use Fcntl;
30use YAML::Syck (); 31use YAML::Syck ();
79our $NEXT_TICK; 80our $NEXT_TICK;
80our $NOW; 81our $NOW;
81our $USE_FSYNC = 1; # use fsync to write maps - default off 82our $USE_FSYNC = 1; # use fsync to write maps - default off
82 83
83our $BDB_POLL_WATCHER; 84our $BDB_POLL_WATCHER;
85our $BDB_DEADLOCK_WATCHER;
86our $BDB_CHECKPOINT_WATCHER;
87our $BDB_TRICKLE_WATCHER;
84our $DB_ENV; 88our $DB_ENV;
85 89
86our %CFG; 90our %CFG;
87 91
88our $UPTIME; $UPTIME ||= time; 92our $UPTIME; $UPTIME ||= time;
89our $RUNTIME; 93our $RUNTIME;
90 94
91our %PLAYER; # all users 95our (%PLAYER, %PLAYER_LOADING); # all users
92our %MAP; # all maps 96our (%MAP, %MAP_LOADING ); # all maps
93our $LINK_MAP; # the special {link} map, which is always available 97our $LINK_MAP; # the special {link} map, which is always available
94 98
95# used to convert map paths into valid unix filenames by replacing / by ∕ 99# used to convert map paths into valid unix filenames by replacing / by ∕
96our $PATH_SEP = "∕"; # U+2215, chosen purely for visual reasons 100our $PATH_SEP = "∕"; # U+2215, chosen purely for visual reasons
97 101
167 171
168These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only 172These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only
169returns directly I<after> the tick processing (and consequently, can only wake one process 173returns directly I<after> the tick processing (and consequently, can only wake one process
170per tick), while cf::wait_for_tick wakes up all waiters after tick processing. 174per tick), while cf::wait_for_tick wakes up all waiters after tick processing.
171 175
176=item @cf::INVOKE_RESULTS
177
178This array contains the results of the last C<invoke ()> call. When
179C<cf::override> is called C<@cf::INVOKE_RESULTS> is set to the parameters of
180that call.
181
172=back 182=back
173 183
174=cut 184=cut
175 185
176BEGIN { 186BEGIN {
180 $msg .= "\n" 190 $msg .= "\n"
181 unless $msg =~ /\n$/; 191 unless $msg =~ /\n$/;
182 192
183 $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge; 193 $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge;
184 194
185 utf8::encode $msg;
186 LOG llevError, $msg; 195 LOG llevError, $msg;
187 }; 196 };
188} 197}
189 198
190@safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable'; 199@safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
265Wait until the given lock is available and then acquires it and returns 274Wait until the given lock is available and then acquires it and returns
266a Coro::guard object. If the guard object gets destroyed (goes out of scope, 275a Coro::guard object. If the guard object gets destroyed (goes out of scope,
267for example when the coroutine gets canceled), the lock is automatically 276for example when the coroutine gets canceled), the lock is automatically
268returned. 277returned.
269 278
279Locks are *not* recursive, locking from the same coro twice results in a
280deadlocked coro.
281
270Lock names should begin with a unique identifier (for example, cf::map::find 282Lock names should begin with a unique identifier (for example, cf::map::find
271uses map_find and cf::map::load uses map_load). 283uses map_find and cf::map::load uses map_load).
272 284
273=item $locked = cf::lock_active $string 285=item $locked = cf::lock_active $string
274 286
275Return true if the lock is currently active, i.e. somebody has locked it. 287Return true if the lock is currently active, i.e. somebody has locked it.
276 288
277=cut 289=cut
278 290
279our %LOCK; 291our %LOCK;
292our %LOCKER;#d#
280 293
281sub lock_wait($) { 294sub lock_wait($) {
282 my ($key) = @_; 295 my ($key) = @_;
296
297 if ($LOCKER{$key} == $Coro::current) {#d#
298 Carp::cluck "lock_wait($key) for already-acquired lock";#d#
299 return;#d#
300 }#d#
283 301
284 # wait for lock, if any 302 # wait for lock, if any
285 while ($LOCK{$key}) { 303 while ($LOCK{$key}) {
286 push @{ $LOCK{$key} }, $Coro::current; 304 push @{ $LOCK{$key} }, $Coro::current;
287 Coro::schedule; 305 Coro::schedule;
293 311
294 # wait, to be sure we are not locked 312 # wait, to be sure we are not locked
295 lock_wait $key; 313 lock_wait $key;
296 314
297 $LOCK{$key} = []; 315 $LOCK{$key} = [];
316 $LOCKER{$key} = $Coro::current;#d#
298 317
299 Coro::guard { 318 Coro::guard {
319 delete $LOCKER{$key};#d#
300 # wake up all waiters, to be on the safe side 320 # wake up all waiters, to be on the safe side
301 $_->ready for @{ delete $LOCK{$key} }; 321 $_->ready for @{ delete $LOCK{$key} };
302 } 322 }
303} 323}
304 324
316 }; 336 };
317 $TICK_WATCHER->stop; 337 $TICK_WATCHER->stop;
318 $guard 338 $guard
319} 339}
320 340
341=item cf::get_slot $time[, $priority[, $name]]
342
343Allocate $time seconds of blocking CPU time at priority C<$priority>:
344This call blocks and returns only when you have at least C<$time> seconds
345of cpu time till the next tick. The slot is only valid till the next cede.
346
347The optional C<$name> can be used to identify the job to run. It might be
348used for statistical purposes and should identify the same time-class.
349
350Useful for short background jobs.
351
352=cut
353
354our @SLOT_QUEUE;
355our $SLOT_QUEUE;
356
357$SLOT_QUEUE->cancel if $SLOT_QUEUE;
358$SLOT_QUEUE = Coro::async {
359 $Coro::current->desc ("timeslot manager");
360
361 my $signal = new Coro::Signal;
362
363 while () {
364 next_job:
365 my $avail = cf::till_tick;
366 if ($avail > 0.01) {
367 for (0 .. $#SLOT_QUEUE) {
368 if ($SLOT_QUEUE[$_][0] < $avail) {
369 my $job = splice @SLOT_QUEUE, $_, 1, ();
370 $job->[2]->send;
371 Coro::cede;
372 goto next_job;
373 }
374 }
375 }
376
377 if (@SLOT_QUEUE) {
378 # we do not use wait_For_tick() as it returns immediately when tick is inactive
379 push @cf::WAIT_FOR_TICK, $signal;
380 $signal->wait;
381 } else {
382 Coro::schedule;
383 }
384 }
385};
386
387sub get_slot($;$$) {
388 my ($time, $pri, $name) = @_;
389
390 $time = $TICK * .6 if $time > $TICK * .6;
391 my $sig = new Coro::Signal;
392
393 push @SLOT_QUEUE, [$time, $pri, $sig, $name];
394 @SLOT_QUEUE = sort { $b->[1] <=> $a->[1] } @SLOT_QUEUE;
395 $SLOT_QUEUE->ready;
396 $sig->wait;
397}
398
321=item cf::async { BLOCK } 399=item cf::async { BLOCK }
322 400
323Currently the same as Coro::async_pool, meaning you cannot use 401Currently the same as Coro::async_pool, meaning you cannot use
324C<on_destroy>, C<join> or other gimmicks on these coroutines. The only 402C<on_destroy>, C<join> or other gimmicks on these coroutines. The only
325thing you are allowed to do is call C<prio> on it. 403thing you are allowed to do is call C<prio> on it.
349 my $time = Event::time; 427 my $time = Event::time;
350 428
351 # this is the main coro, too bad, we have to block 429 # this is the main coro, too bad, we have to block
352 # till the operation succeeds, freezing the server :/ 430 # till the operation succeeds, freezing the server :/
353 431
432 LOG llevError | logBacktrace, Carp::longmess "sync job";#d#
433
354 # TODO: use suspend/resume instead 434 # TODO: use suspend/resume instead
355 # (but this is cancel-safe) 435 # (but this is cancel-safe)
356 my $freeze_guard = freeze_mainloop; 436 my $freeze_guard = freeze_mainloop;
357 437
358 my $busy = 1; 438 my $busy = 1;
359 my @res; 439 my @res;
360 440
361 (async { 441 (async {
442 $Coro::current->desc ("sync job coro");
362 @res = eval { $job->() }; 443 @res = eval { $job->() };
363 warn $@ if $@; 444 warn $@ if $@;
364 undef $busy; 445 undef $busy;
365 })->prio (Coro::PRIO_MAX); 446 })->prio (Coro::PRIO_MAX);
366 447
410Coro::Storable. May, of course, block. Note that the executed sub may 491Coro::Storable. May, of course, block. Note that the executed sub may
411never block itself or use any form of Event handling. 492never block itself or use any form of Event handling.
412 493
413=cut 494=cut
414 495
415sub _store_scalar {
416 open my $fh, ">", \my $buf
417 or die "fork_call: cannot open fh-to-buf in child : $!";
418 Storable::store_fd $_[0], $fh;
419 close $fh;
420
421 $buf
422}
423
424sub fork_call(&@) { 496sub fork_call(&@) {
425 my ($cb, @args) = @_; 497 my ($cb, @args) = @_;
426 498
427# socketpair my $fh1, my $fh2, Socket::AF_UNIX, Socket::SOCK_STREAM, Socket::PF_UNSPEC 499 # we seemingly have to make a local copy of the whole thing,
428# or die "socketpair: $!"; 500 # otherwise perl prematurely frees the stuff :/
429 pipe my $fh1, my $fh2 501 # TODO: investigate and fix (likely this will be rather laborious)
430 or die "pipe: $!";
431 502
432 if (my $pid = fork) { 503 my @res = Coro::Util::fork_eval {
433 close $fh2;
434
435 my $res = (Coro::Handle::unblock $fh1)->readline (undef);
436 warn "pst<$res>" unless $res =~ /^pst/;
437 $res = Coro::Storable::thaw $res;
438
439 waitpid $pid, 0; # should not block anymore, we expect the child to simply behave
440
441 Carp::confess $$res unless "ARRAY" eq ref $res;
442
443 return wantarray ? @$res : $res->[-1];
444 } else {
445 reset_signals; 504 reset_signals;
446 local $SIG{__WARN__}; 505 &$cb
447 local $SIG{__DIE__}; 506 }, @args;
448 # just in case, this hack effectively disables event
449 # in the child. cleaner and slower would be canceling all watchers,
450 # but this works for the time being.
451 local $Coro::idle;
452 $Coro::current->prio (Coro::PRIO_MAX);
453 507
454 eval { 508 wantarray ? @res : $res[-1]
455 close $fh1;
456
457 my @res = eval { $cb->(@args) };
458
459 syswrite $fh2, _store_scalar $@ ? \"$@" : \@res;
460 close $fh2;
461 };
462
463 warn $@ if $@;
464 _exit 0;
465 }
466} 509}
467 510
468=item $value = cf::db_get $family => $key 511=item $value = cf::db_get $family => $key
469 512
470Returns a single value from the environment database. 513Returns a single value from the environment database.
472=item cf::db_put $family => $key => $value 515=item cf::db_put $family => $key => $value
473 516
474Stores the given C<$value> in the family. It can currently store binary 517Stores the given C<$value> in the family. It can currently store binary
475data only (use Compress::LZF::sfreeze_cr/sthaw to convert to/from binary). 518data only (use Compress::LZF::sfreeze_cr/sthaw to convert to/from binary).
476 519
520=item $db = cf::db_table "name"
521
522Create and/or open a new database table. The string must not be "db" and must be unique
523within each server.
524
477=cut 525=cut
526
527sub db_table($) {
528 my ($name) = @_;
529 my $db = BDB::db_create $DB_ENV;
530
531 eval {
532 $db->set_flags (BDB::CHKSUM);
533
534 utf8::encode $name;
535 BDB::db_open $db, undef, $name, undef, BDB::BTREE,
536 BDB::CREATE | BDB::AUTO_COMMIT, 0666;
537 cf::cleanup "db_open(db): $!" if $!;
538 };
539 cf::cleanup "db_open(db): $@" if $@;
540
541 $db
542}
478 543
479our $DB; 544our $DB;
480 545
481sub db_init { 546sub db_init {
482 unless ($DB) {
483 $DB = BDB::db_create $DB_ENV;
484
485 cf::sync_job { 547 cf::sync_job {
486 eval { 548 $DB ||= db_table "db";
487 $DB->set_flags (BDB::CHKSUM);
488
489 BDB::db_open $DB, undef, "db", undef, BDB::BTREE,
490 BDB::CREATE | BDB::AUTO_COMMIT, 0666;
491 cf::cleanup "db_open(db): $!" if $!;
492 };
493 cf::cleanup "db_open(db): $@" if $@;
494 };
495 } 549 };
496} 550}
497 551
498sub db_get($$) { 552sub db_get($$) {
499 my $key = "$_[0]/$_[1]"; 553 my $key = "$_[0]/$_[1]";
500 554
550 if (1) { 604 if (1) {
551 $md5 = 605 $md5 =
552 join "\x00", 606 join "\x00",
553 $processversion, 607 $processversion,
554 map { 608 map {
555 Coro::cede; 609 cf::cede_to_tick;
556 ($src->[$_], Digest::MD5::md5_hex $data[$_]) 610 ($src->[$_], Digest::MD5::md5_hex $data[$_])
557 } 0.. $#$src; 611 } 0.. $#$src;
558 612
559 613
560 my $dbmd5 = db_get cache => "$id/md5"; 614 my $dbmd5 = db_get cache => "$id/md5";
866 "; 920 ";
867 die if $@; 921 die if $@;
868} 922}
869 923
870our $override; 924our $override;
871our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals? 925our @INVOKE_RESULTS = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
872 926
873sub override { 927sub override {
874 $override = 1; 928 $override = 1;
875 @invoke_results = (); 929 @INVOKE_RESULTS = (@_);
876} 930}
877 931
878sub do_invoke { 932sub do_invoke {
879 my $event = shift; 933 my $event = shift;
880 my $callbacks = shift; 934 my $callbacks = shift;
881 935
882 @invoke_results = (); 936 @INVOKE_RESULTS = ();
883 937
884 local $override; 938 local $override;
885 939
886 for (@$callbacks) { 940 for (@$callbacks) {
887 eval { &{$_->[1]} }; 941 eval { &{$_->[1]} };
904 958
905Generate an object-specific event with the given arguments. 959Generate an object-specific event with the given arguments.
906 960
907This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be 961This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
908removed in future versions), and there is no public API to access override 962removed in future versions), and there is no public API to access override
909results (if you must, access C<@cf::invoke_results> directly). 963results (if you must, access C<@cf::INVOKE_RESULTS> directly).
910 964
911=back 965=back
912 966
913=cut 967=cut
914 968
915############################################################################# 969#############################################################################
916# object support 970# object support
971#
972
973sub _can_merge {
974 my ($ob1, $ob2) = @_;
975
976 local $Storable::canonical = 1;
977 my $fob1 = Storable::freeze $ob1;
978 my $fob2 = Storable::freeze $ob2;
979
980 $fob1 eq $fob2
981}
917 982
918sub reattach { 983sub reattach {
919 # basically do the same as instantiate, without calling instantiate 984 # basically do the same as instantiate, without calling instantiate
920 my ($obj) = @_; 985 my ($obj) = @_;
921 986
968sub object_freezer_save { 1033sub object_freezer_save {
969 my ($filename, $rdata, $objs) = @_; 1034 my ($filename, $rdata, $objs) = @_;
970 1035
971 sync_job { 1036 sync_job {
972 if (length $$rdata) { 1037 if (length $$rdata) {
1038 utf8::decode (my $decname = $filename);
973 warn sprintf "saving %s (%d,%d)\n", 1039 warn sprintf "saving %s (%d,%d)\n",
974 $filename, length $$rdata, scalar @$objs; 1040 $decname, length $$rdata, scalar @$objs;
975 1041
976 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) { 1042 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
977 chmod SAVE_MODE, $fh; 1043 chmod SAVE_MODE, $fh;
978 aio_write $fh, 0, (length $$rdata), $$rdata, 0; 1044 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
979 aio_fsync $fh if $cf::USE_FSYNC; 1045 aio_fsync $fh if $cf::USE_FSYNC;
998 } 1064 }
999 } else { 1065 } else {
1000 aio_unlink $filename; 1066 aio_unlink $filename;
1001 aio_unlink "$filename.pst"; 1067 aio_unlink "$filename.pst";
1002 } 1068 }
1003 } 1069 };
1004} 1070}
1005 1071
1006sub object_freezer_as_string { 1072sub object_freezer_as_string {
1007 my ($rdata, $objs) = @_; 1073 my ($rdata, $objs) = @_;
1008 1074
1020 or return; 1086 or return;
1021 1087
1022 unless (aio_stat "$filename.pst") { 1088 unless (aio_stat "$filename.pst") {
1023 (aio_load "$filename.pst", $av) >= 0 1089 (aio_load "$filename.pst", $av) >= 0
1024 or return; 1090 or return;
1091
1025 $av = eval { (Storable::thaw $av)->{objs} }; 1092 $av = eval { (Storable::thaw $av)->{objs} };
1026 } 1093 }
1027 1094
1095 utf8::decode (my $decname = $filename);
1028 warn sprintf "loading %s (%d)\n", 1096 warn sprintf "loading %s (%d,%d)\n",
1029 $filename, length $data, scalar @{$av || []}; 1097 $decname, length $data, scalar @{$av || []};
1098
1030 return ($data, $av); 1099 ($data, $av)
1031} 1100}
1032 1101
1033=head2 COMMAND CALLBACKS 1102=head2 COMMAND CALLBACKS
1034 1103
1035=over 4 1104=over 4
1108 my ($pl, $buf) = @_; 1177 my ($pl, $buf) = @_;
1109 1178
1110 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) }; 1179 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) };
1111 1180
1112 if (ref $msg) { 1181 if (ref $msg) {
1182 my ($type, $reply, @payload) =
1183 "ARRAY" eq ref $msg
1184 ? @$msg
1185 : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
1186
1187 my @reply;
1188
1113 if (my $cb = $EXTCMD{$msg->{msgtype}}) { 1189 if (my $cb = $EXTCMD{$type}) {
1114 if (my %reply = $cb->($pl, $msg)) { 1190 @reply = $cb->($pl, @payload);
1115 $pl->ext_reply ($msg->{msgid}, %reply);
1116 }
1117 } 1191 }
1192
1193 $pl->ext_reply ($reply, @reply)
1194 if $reply;
1195
1118 } else { 1196 } else {
1119 warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n"; 1197 warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1120 } 1198 }
1121 1199
1122 cf::override; 1200 cf::override;
1212 1290
1213=head3 cf::player 1291=head3 cf::player
1214 1292
1215=over 4 1293=over 4
1216 1294
1295=item cf::player::num_playing
1296
1297Returns the official number of playing players, as per the Crossfire metaserver rules.
1298
1299=cut
1300
1301sub num_playing {
1302 scalar grep
1303 $_->ob->map
1304 && !$_->hidden
1305 && !$_->ob->flag (cf::FLAG_WIZ),
1306 cf::player::list
1307}
1308
1217=item cf::player::find $login 1309=item cf::player::find $login
1218 1310
1219Returns the given player object, loading it if necessary (might block). 1311Returns the given player object, loading it if necessary (might block).
1220 1312
1221=cut 1313=cut
1256 aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst"; 1348 aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1257 aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata"; 1349 aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1258 aio_unlink +(playerdir $login) . "/$login.pl.pst"; 1350 aio_unlink +(playerdir $login) . "/$login.pl.pst";
1259 aio_unlink +(playerdir $login) . "/$login.pl"; 1351 aio_unlink +(playerdir $login) . "/$login.pl";
1260 1352
1261 my $pl = load_pl path $login 1353 my $f = new_from_file cf::object::thawer path $login
1262 or return; 1354 or return;
1355
1356 my $pl = cf::player::load_pl $f
1357 or return;
1358 local $cf::PLAYER_LOADING{$login} = $pl;
1359 $f->resolve_delayed_derefs;
1263 $cf::PLAYER{$login} = $pl 1360 $cf::PLAYER{$login} = $pl
1264 } 1361 }
1265 } 1362 }
1266} 1363}
1267 1364
1277 1374
1278 aio_mkdir playerdir $pl, 0770; 1375 aio_mkdir playerdir $pl, 0770;
1279 $pl->{last_save} = $cf::RUNTIME; 1376 $pl->{last_save} = $cf::RUNTIME;
1280 1377
1281 $pl->save_pl ($path); 1378 $pl->save_pl ($path);
1282 Coro::cede; 1379 cf::cede_to_tick;
1283} 1380}
1284 1381
1285sub new($) { 1382sub new($) {
1286 my ($login) = @_; 1383 my ($login) = @_;
1287 1384
1291 $self->{deny_save} = 1; 1388 $self->{deny_save} = 1;
1292 1389
1293 $cf::PLAYER{$login} = $self; 1390 $cf::PLAYER{$login} = $self;
1294 1391
1295 $self 1392 $self
1393}
1394
1395=item $player->send_msg ($channel, $msg, $color, [extra...])
1396
1397=cut
1398
1399sub send_msg {
1400 my $ns = shift->ns
1401 or return;
1402 $ns->send_msg (@_);
1296} 1403}
1297 1404
1298=item $pl->quit_character 1405=item $pl->quit_character
1299 1406
1300Nukes the player without looking back. If logged in, the connection will 1407Nukes the player without looking back. If logged in, the connection will
1355 or return []; 1462 or return [];
1356 1463
1357 my @logins; 1464 my @logins;
1358 1465
1359 for my $login (@$dirs) { 1466 for my $login (@$dirs) {
1467 my $path = path $login;
1468
1469 # a .pst is a dead give-away for a valid player
1470 unless (-e "$path.pst") {
1360 my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next; 1471 my $fh = aio_open $path, Fcntl::O_RDONLY, 0 or next;
1361 aio_read $fh, 0, 512, my $buf, 0 or next; 1472 aio_read $fh, 0, 512, my $buf, 0 or next;
1362 $buf !~ /^password -------------$/m or next; # official not-valid tag 1473 $buf !~ /^password -------------$/m or next; # official not-valid tag
1474 }
1363 1475
1364 utf8::decode $login; 1476 utf8::decode $login;
1365 push @logins, $login; 1477 push @logins, $login;
1366 } 1478 }
1367 1479
1405sub expand_cfpod { 1517sub expand_cfpod {
1406 ((my $self), (local $_)) = @_; 1518 ((my $self), (local $_)) = @_;
1407 1519
1408 # escape & and < 1520 # escape & and <
1409 s/&/&amp;/g; 1521 s/&/&amp;/g;
1410 s/(?<![BIUGH])</&lt;/g; 1522 s/(?<![BIUGHT])</&lt;/g;
1411 1523
1412 # this is buggy, it needs to properly take care of nested <'s 1524 # this is buggy, it needs to properly take care of nested <'s
1413 1525
1414 1 while 1526 1 while
1415 # replace B<>, I<>, U<> etc. 1527 # replace B<>, I<>, U<> etc.
1416 s/B<([^\>]*)>/<b>$1<\/b>/ 1528 s/B<([^\>]*)>/<b>$1<\/b>/
1417 || s/I<([^\>]*)>/<i>$1<\/i>/ 1529 || s/I<([^\>]*)>/<i>$1<\/i>/
1418 || s/U<([^\>]*)>/<u>$1<\/u>/ 1530 || s/U<([^\>]*)>/<u>$1<\/u>/
1531 || s/T<([^\>]*)>/<big><b>$1<\/b><\/big>/
1419 # replace G<male|female> tags 1532 # replace G<male|female> tags
1420 || s{G<([^>|]*)\|([^>]*)>}{ 1533 || s{G<([^>|]*)\|([^>]*)>}{
1421 $self->gender ? $2 : $1 1534 $self->gender ? $2 : $1
1422 }ge 1535 }ge
1423 # replace H<hint text> 1536 # replace H<hint text>
1444sub hintmode { 1557sub hintmode {
1445 $_[0]{hintmode} = $_[1] if @_ > 1; 1558 $_[0]{hintmode} = $_[1] if @_ > 1;
1446 $_[0]{hintmode} 1559 $_[0]{hintmode}
1447} 1560}
1448 1561
1449=item $player->ext_reply ($msgid, %msg) 1562=item $player->ext_reply ($msgid, @msg)
1450 1563
1451Sends an ext reply to the player. 1564Sends an ext reply to the player.
1452 1565
1453=cut 1566=cut
1454 1567
1455sub ext_reply($$%) { 1568sub ext_reply($$@) {
1456 my ($self, $id, %msg) = @_; 1569 my ($self, $id, @msg) = @_;
1457 1570
1458 $msg{msgid} = $id; 1571 $self->ns->ext_reply ($id, @msg)
1459 $self->send ("ext " . $self->ns->{json_coder}->encode (\%msg));
1460} 1572}
1461 1573
1462=item $player->ext_event ($type, %msg) 1574=item $player->ext_msg ($type, @msg)
1463 1575
1464Sends an ext event to the client. 1576Sends an ext event to the client.
1465 1577
1466=cut 1578=cut
1467 1579
1468sub ext_event($$%) { 1580sub ext_msg($$@) {
1469 my ($self, $type, %msg) = @_; 1581 my ($self, $type, @msg) = @_;
1470 1582
1471 $self->ns->ext_event ($type, %msg); 1583 $self->ns->ext_msg ($type, @msg);
1472} 1584}
1473 1585
1474=head3 cf::region 1586=head3 cf::region
1475 1587
1476=over 4 1588=over 4
1619 $self->init; # pass $1 etc. 1731 $self->init; # pass $1 etc.
1620 return $self; 1732 return $self;
1621 } 1733 }
1622 } 1734 }
1623 1735
1624 Carp::carp "unable to resolve path '$path' (base '$base')."; 1736 Carp::cluck "unable to resolve path '$path' (base '$base').";
1625 () 1737 ()
1626} 1738}
1627 1739
1628sub init { 1740sub init {
1629 my ($self) = @_; 1741 my ($self) = @_;
1692 1804
1693sub load_header_from($) { 1805sub load_header_from($) {
1694 my ($self, $path) = @_; 1806 my ($self, $path) = @_;
1695 1807
1696 utf8::encode $path; 1808 utf8::encode $path;
1697 #aio_open $path, O_RDONLY, 0 1809 my $f = new_from_file cf::object::thawer $path
1698 # or return;
1699
1700 $self->_load_header ($path)
1701 or return; 1810 or return;
1811
1812 $self->_load_header ($f)
1813 or return;
1814
1815 local $MAP_LOADING{$self->{path}} = $self;
1816 $f->resolve_delayed_derefs;
1702 1817
1703 $self->{load_path} = $path; 1818 $self->{load_path} = $path;
1704 1819
1705 1 1820 1
1706} 1821}
1760sub find { 1875sub find {
1761 my ($path, $origin) = @_; 1876 my ($path, $origin) = @_;
1762 1877
1763 $path = normalise $path, $origin && $origin->path; 1878 $path = normalise $path, $origin && $origin->path;
1764 1879
1880 cf::lock_wait "map_data:$path";#d#remove
1765 cf::lock_wait "map_find:$path"; 1881 cf::lock_wait "map_find:$path";
1766 1882
1767 $cf::MAP{$path} || do { 1883 $cf::MAP{$path} || do {
1768 my $guard = cf::lock_acquire "map_find:$path"; 1884 my $guard1 = cf::lock_acquire "map_find:$path";
1885 my $guard2 = cf::lock_acquire "map_data:$path"; # just for the fun of it
1886
1769 my $map = new_from_path cf::map $path 1887 my $map = new_from_path cf::map $path
1770 or return; 1888 or return;
1771 1889
1772 $map->{last_save} = $cf::RUNTIME; 1890 $map->{last_save} = $cf::RUNTIME;
1773 1891
1775 or return; 1893 or return;
1776 1894
1777 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?) 1895 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
1778 # doing this can freeze the server in a sync job, obviously 1896 # doing this can freeze the server in a sync job, obviously
1779 #$cf::WAIT_FOR_TICK->wait; 1897 #$cf::WAIT_FOR_TICK->wait;
1898 undef $guard1;
1899 undef $guard2;
1780 $map->reset; 1900 $map->reset;
1781 undef $guard;
1782 return find $path; 1901 return find $path;
1783 } 1902 }
1784 1903
1785 $cf::MAP{$path} = $map 1904 $cf::MAP{$path} = $map
1786 } 1905 }
1795 local $self->{deny_reset} = 1; # loading can take a long time 1914 local $self->{deny_reset} = 1; # loading can take a long time
1796 1915
1797 my $path = $self->{path}; 1916 my $path = $self->{path};
1798 1917
1799 { 1918 {
1800 my $guard = cf::lock_acquire "map_load:$path"; 1919 my $guard = cf::lock_acquire "map_data:$path";
1801 1920
1921 return unless $self->valid;
1802 return if $self->in_memory != cf::MAP_SWAPPED; 1922 return unless $self->in_memory == cf::MAP_SWAPPED;
1803 1923
1804 $self->in_memory (cf::MAP_LOADING); 1924 $self->in_memory (cf::MAP_LOADING);
1805 1925
1806 $self->alloc; 1926 $self->alloc;
1807 1927
1808 $self->pre_load; 1928 $self->pre_load;
1809 Coro::cede; 1929 cf::cede_to_tick;
1810 1930
1931 my $f = new_from_file cf::object::thawer $self->{load_path};
1932 $f->skip_block;
1811 $self->_load_objects ($self->{load_path}, 1) 1933 $self->_load_objects ($f)
1812 or return; 1934 or return;
1813 1935
1814 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1) 1936 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1815 if delete $self->{load_original}; 1937 if delete $self->{load_original};
1816 1938
1817 if (my $uniq = $self->uniq_path) { 1939 if (my $uniq = $self->uniq_path) {
1818 utf8::encode $uniq; 1940 utf8::encode $uniq;
1819 if (aio_open $uniq, O_RDONLY, 0) { 1941 unless (aio_stat $uniq) {
1942 if (my $f = new_from_file cf::object::thawer $uniq) {
1820 $self->clear_unique_items; 1943 $self->clear_unique_items;
1821 $self->_load_objects ($uniq, 0); 1944 $self->_load_objects ($f);
1945 $f->resolve_delayed_derefs;
1946 }
1822 } 1947 }
1823 } 1948 }
1824 1949
1825 Coro::cede; 1950 $f->resolve_delayed_derefs;
1951
1952 cf::cede_to_tick;
1826 # now do the right thing for maps 1953 # now do the right thing for maps
1827 $self->link_multipart_objects; 1954 $self->link_multipart_objects;
1828 $self->difficulty ($self->estimate_difficulty) 1955 $self->difficulty ($self->estimate_difficulty)
1829 unless $self->difficulty; 1956 unless $self->difficulty;
1830 Coro::cede; 1957 cf::cede_to_tick;
1831 1958
1832 unless ($self->{deny_activate}) { 1959 unless ($self->{deny_activate}) {
1833 $self->decay_objects; 1960 $self->decay_objects;
1834 $self->fix_auto_apply; 1961 $self->fix_auto_apply;
1835 $self->update_buttons; 1962 $self->update_buttons;
1836 Coro::cede; 1963 cf::cede_to_tick;
1837 $self->set_darkness_map; 1964 $self->set_darkness_map;
1838 Coro::cede; 1965 cf::cede_to_tick;
1839 $self->activate; 1966 $self->activate;
1840 } 1967 }
1968
1969 $self->{last_save} = $cf::RUNTIME;
1970 $self->last_access ($cf::RUNTIME);
1841 1971
1842 $self->in_memory (cf::MAP_IN_MEMORY); 1972 $self->in_memory (cf::MAP_IN_MEMORY);
1843 } 1973 }
1844 1974
1845 $self->post_load; 1975 $self->post_load;
1856 1986
1857 $self 1987 $self
1858} 1988}
1859 1989
1860# find and load all maps in the 3x3 area around a map 1990# find and load all maps in the 3x3 area around a map
1861sub load_diag { 1991sub load_neighbours {
1862 my ($map) = @_; 1992 my ($map) = @_;
1863 1993
1864 my @diag; # diagonal neighbours 1994 my @neigh; # diagonal neighbours
1865 1995
1866 for (0 .. 3) { 1996 for (0 .. 3) {
1867 my $neigh = $map->tile_path ($_) 1997 my $neigh = $map->tile_path ($_)
1868 or next; 1998 or next;
1869 $neigh = find $neigh, $map 1999 $neigh = find $neigh, $map
1870 or next; 2000 or next;
1871 $neigh->load; 2001 $neigh->load;
1872 2002
2003 push @neigh,
1873 push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh], 2004 [$neigh->tile_path (($_ + 3) % 4), $neigh],
1874 [$neigh->tile_path (($_ + 1) % 4), $neigh]; 2005 [$neigh->tile_path (($_ + 1) % 4), $neigh];
1875 } 2006 }
1876 2007
1877 for (@diag) { 2008 for (grep defined $_->[0], @neigh) {
2009 my ($path, $origin) = @$_;
1878 my $neigh = find @$_ 2010 my $neigh = find $path, $origin
1879 or next; 2011 or next;
1880 $neigh->load; 2012 $neigh->load;
1881 } 2013 }
1882} 2014}
1883 2015
1888} 2020}
1889 2021
1890sub do_load_sync { 2022sub do_load_sync {
1891 my ($map) = @_; 2023 my ($map) = @_;
1892 2024
2025 cf::LOG cf::llevDebug | cf::logBacktrace, "do_load_sync"
2026 if $Coro::current == $Coro::main;
2027
1893 cf::sync_job { $map->load }; 2028 cf::sync_job { $map->load };
1894} 2029}
1895 2030
1896our %MAP_PREFETCH; 2031our %MAP_PREFETCH;
1897our $MAP_PREFETCHER = undef; 2032our $MAP_PREFETCHER = undef;
1898 2033
1899sub find_async { 2034sub find_async {
1900 my ($path, $origin) = @_; 2035 my ($path, $origin, $load) = @_;
1901 2036
1902 $path = normalise $path, $origin && $origin->{path}; 2037 $path = normalise $path, $origin && $origin->{path};
1903 2038
1904 if (my $map = $cf::MAP{$path}) { 2039 if (my $map = $cf::MAP{$path}) {
1905 return $map if $map->in_memory == cf::MAP_IN_MEMORY; 2040 return $map if !$load || $map->in_memory == cf::MAP_IN_MEMORY;
1906 } 2041 }
1907 2042
1908 undef $MAP_PREFETCH{$path}; 2043 $MAP_PREFETCH{$path} |= $load;
2044
1909 $MAP_PREFETCHER ||= cf::async { 2045 $MAP_PREFETCHER ||= cf::async {
2046 $Coro::current->{desc} = "map prefetcher";
2047
1910 while (%MAP_PREFETCH) { 2048 while (%MAP_PREFETCH) {
1911 for my $path (keys %MAP_PREFETCH) { 2049 while (my ($k, $v) = each %MAP_PREFETCH) {
1912 my $map = find $path 2050 if (my $map = find $k) {
1913 or next;
1914 $map->load; 2051 $map->load if $v;
2052 }
1915 2053
1916 delete $MAP_PREFETCH{$path}; 2054 delete $MAP_PREFETCH{$k};
1917 } 2055 }
1918 } 2056 }
1919 undef $MAP_PREFETCHER; 2057 undef $MAP_PREFETCHER;
1920 }; 2058 };
1921 $MAP_PREFETCHER->prio (6); 2059 $MAP_PREFETCHER->prio (6);
1924} 2062}
1925 2063
1926sub save { 2064sub save {
1927 my ($self) = @_; 2065 my ($self) = @_;
1928 2066
1929 my $lock = cf::lock_acquire "map_data:" . $self->path; 2067 my $lock = cf::lock_acquire "map_data:$self->{path}";
1930 2068
1931 $self->{last_save} = $cf::RUNTIME; 2069 $self->{last_save} = $cf::RUNTIME;
1932 2070
1933 return unless $self->dirty; 2071 return unless $self->dirty;
1934 2072
1940 return if $self->{deny_save}; 2078 return if $self->{deny_save};
1941 2079
1942 local $self->{last_access} = $self->last_access;#d# 2080 local $self->{last_access} = $self->last_access;#d#
1943 2081
1944 cf::async { 2082 cf::async {
2083 $Coro::current->{desc} = "map player save";
1945 $_->contr->save for $self->players; 2084 $_->contr->save for $self->players;
1946 }; 2085 };
1947 2086
1948 if ($uniq) { 2087 if ($uniq) {
1949 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS); 2088 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1957 my ($self) = @_; 2096 my ($self) = @_;
1958 2097
1959 # save first because save cedes 2098 # save first because save cedes
1960 $self->save; 2099 $self->save;
1961 2100
1962 my $lock = cf::lock_acquire "map_data:" . $self->path; 2101 my $lock = cf::lock_acquire "map_data:$self->{path}";
1963 2102
1964 return if $self->players; 2103 return if $self->players;
1965 return if $self->in_memory != cf::MAP_IN_MEMORY; 2104 return if $self->in_memory != cf::MAP_IN_MEMORY;
1966 return if $self->{deny_save}; 2105 return if $self->{deny_save};
1967 2106
2107 $self->in_memory (cf::MAP_SWAPPED);
2108
2109 $self->deactivate;
2110 $_->clear_links_to ($self) for values %cf::MAP;
1968 $self->clear; 2111 $self->clear;
1969 $self->in_memory (cf::MAP_SWAPPED);
1970} 2112}
1971 2113
1972sub reset_at { 2114sub reset_at {
1973 my ($self) = @_; 2115 my ($self) = @_;
1974 2116
2006 if $uniq; 2148 if $uniq;
2007 } 2149 }
2008 2150
2009 delete $cf::MAP{$self->path}; 2151 delete $cf::MAP{$self->path};
2010 2152
2153 $self->deactivate;
2154 $_->clear_links_to ($self) for values %cf::MAP;
2011 $self->clear; 2155 $self->clear;
2012
2013 $_->clear_links_to ($self) for values %cf::MAP;
2014 2156
2015 $self->unlink_save; 2157 $self->unlink_save;
2016 $self->destroy; 2158 $self->destroy;
2017} 2159}
2018 2160
2019my $nuke_counter = "aaaa"; 2161my $nuke_counter = "aaaa";
2020 2162
2021sub nuke { 2163sub nuke {
2022 my ($self) = @_; 2164 my ($self) = @_;
2023 2165
2166 {
2167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2168
2024 delete $cf::MAP{$self->path}; 2169 delete $cf::MAP{$self->path};
2025 2170
2026 $self->unlink_save; 2171 $self->unlink_save;
2027 2172
2028 bless $self, "cf::map"; 2173 bless $self, "cf::map";
2029 delete $self->{deny_reset}; 2174 delete $self->{deny_reset};
2030 $self->{deny_save} = 1; 2175 $self->{deny_save} = 1;
2031 $self->reset_timeout (1); 2176 $self->reset_timeout (1);
2032 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++)); 2177 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2033 2178
2034 $cf::MAP{$self->path} = $self; 2179 $cf::MAP{$self->path} = $self;
2180 }
2035 2181
2036 $self->reset; # polite request, might not happen 2182 $self->reset; # polite request, might not happen
2037} 2183}
2038 2184
2039=item $maps = cf::map::tmp_maps 2185=item $maps = cf::map::tmp_maps
2115 2261
2116sub inv_recursive { 2262sub inv_recursive {
2117 inv_recursive_ inv $_[0] 2263 inv_recursive_ inv $_[0]
2118} 2264}
2119 2265
2266=item $ref = $ob->ref
2267
2268creates and returns a persistent reference to an objetc that can be stored as a string.
2269
2270=item $ob = cf::object::deref ($refstring)
2271
2272returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2273even if the object actually exists. May block.
2274
2275=cut
2276
2277sub deref {
2278 my ($ref) = @_;
2279
2280 if ($ref =~ m{^player\/(<1\.[0-9a-f]+>)/(.*)$}) {
2281 my ($uuid, $name) = ($1, $2);
2282 my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2283 or return;
2284 $pl->ob->uuid eq $uuid
2285 or return;
2286
2287 $pl->ob
2288 } else {
2289 warn "$ref: cannot resolve object reference\n";
2290 undef
2291 }
2292}
2293
2120package cf; 2294package cf;
2121 2295
2122=back 2296=back
2123 2297
2124=head3 cf::object::player 2298=head3 cf::object::player
2146 2320
2147 } else { 2321 } else {
2148 my $pl = $self->contr; 2322 my $pl = $self->contr;
2149 2323
2150 if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) { 2324 if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2151 my $diag = $pl->{npc_dialog}; 2325 my $dialog = $pl->{npc_dialog};
2152 $diag->{pl}->ext_reply ( 2326 $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2153 $diag->{id},
2154 msgtype => "reply",
2155 msg => $diag->{pl}->expand_cfpod ($msg),
2156 add_topics => []
2157 );
2158 2327
2159 } else { 2328 } else {
2160 $msg = $npc->name . " says: $msg" if $npc; 2329 $msg = $npc->name . " says: $msg" if $npc;
2161 $self->message ($msg, $flags); 2330 $self->message ($msg, $flags);
2162 } 2331 }
2163 } 2332 }
2333}
2334
2335=item $object->send_msg ($channel, $msg, $color, [extra...])
2336
2337=cut
2338
2339sub cf::object::send_msg {
2340 my $pl = shift->contr
2341 or return;
2342 $pl->send_msg (@_);
2164} 2343}
2165 2344
2166=item $player_object->may ("access") 2345=item $player_object->may ("access")
2167 2346
2168Returns wether the given player is authorized to access resource "access" 2347Returns wether the given player is authorized to access resource "access"
2247 # use -1 or undef as default coordinates, not 0, 0 2426 # use -1 or undef as default coordinates, not 0, 0
2248 ($x, $y) = ($map->enter_x, $map->enter_y) 2427 ($x, $y) = ($map->enter_x, $map->enter_y)
2249 if $x <=0 && $y <= 0; 2428 if $x <=0 && $y <= 0;
2250 2429
2251 $map->load; 2430 $map->load;
2252 $map->load_diag; 2431 $map->load_neighbours;
2253 2432
2254 return unless $self->contr->active; 2433 return unless $self->contr->active;
2255 $self->activate_recursive; 2434 $self->activate_recursive;
2256 2435
2257 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext 2436 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2277 my $gen = $self->{_goto_generation} = ++$GOTOGEN; 2456 my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2278 2457
2279 $self->enter_link; 2458 $self->enter_link;
2280 2459
2281 (async { 2460 (async {
2461 $Coro::current->{desc} = "player::goto $path $x $y";
2462
2463 # *tag paths override both path and x|y
2464 if ($path =~ /^\*(.*)$/) {
2465 if (my @obs = grep $_->map, ext::map_tags::find $1) {
2466 my $ob = $obs[rand @obs];
2467
2468 # see if we actually can go there
2469 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2470 $ob = $obs[rand @obs];
2471 } else {
2472 $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2473 }
2474 # else put us there anyways for now #d#
2475
2476 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2477 } else {
2478 ($path, $x, $y) = (undef, undef, undef);
2479 }
2480 }
2481
2282 my $map = eval { 2482 my $map = eval {
2283 my $map = cf::map::find $path; 2483 my $map = defined $path ? cf::map::find $path : undef;
2284 2484
2285 if ($map) { 2485 if ($map) {
2286 $map = $map->customise_for ($self); 2486 $map = $map->customise_for ($self);
2287 $map = $check->($map) if $check && $map; 2487 $map = $check->($map) if $check && $map;
2288 } else { 2488 } else {
2289 $self->message ("The exit to '$path' is closed", cf::NDI_UNIQUE | cf::NDI_RED); 2489 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2290 } 2490 }
2291 2491
2292 $map 2492 $map
2293 }; 2493 };
2294 2494
2384 # if exit is damned, update players death & WoR home-position 2584 # if exit is damned, update players death & WoR home-position
2385 $self->contr->savebed ($slaying, $hp, $sp) 2585 $self->contr->savebed ($slaying, $hp, $sp)
2386 if $exit->flag (FLAG_DAMNED); 2586 if $exit->flag (FLAG_DAMNED);
2387 2587
2388 (async { 2588 (async {
2589 $Coro::current->{desc} = "enter_exit $slaying $hp $sp";
2590
2389 $self->deactivate_recursive; # just to be sure 2591 $self->deactivate_recursive; # just to be sure
2390 unless (eval { 2592 unless (eval {
2391 $self->goto ($slaying, $hp, $sp); 2593 $self->goto ($slaying, $hp, $sp);
2392 2594
2393 1; 2595 1;
2419 2621
2420 utf8::encode $text; 2622 utf8::encode $text;
2421 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text); 2623 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2422} 2624}
2423 2625
2424=item $client->send_msg ($color, $type, $msg, [extra...]) 2626=item $client->send_msg ($channel, $msg, $color, [extra...])
2425 2627
2426Send a drawinfo or msg packet to the client, formatting the msg for the 2628Send a drawinfo or msg packet to the client, formatting the msg for the
2427client if neccessary. C<$type> should be a string identifying the type of 2629client if neccessary. C<$type> should be a string identifying the type of
2428the message, with C<log> being the default. If C<$color> is negative, suppress 2630the message, with C<log> being the default. If C<$color> is negative, suppress
2429the message unless the client supports the msg packet. 2631the message unless the client supports the msg packet.
2430 2632
2431=cut 2633=cut
2432 2634
2635our %CHANNEL = (
2636 "c/identify" => {
2637 id => "infobox",
2638 title => "Identify",
2639 reply => undef,
2640 tooltip => "Items recently identified",
2641 },
2642 "c/examine" => {
2643 id => "infobox",
2644 title => "Examine",
2645 reply => undef,
2646 tooltip => "Signs and other items you examined",
2647 },
2648 "c/lookat" => {
2649 id => "infobox",
2650 title => "Look",
2651 reply => undef,
2652 tooltip => "What you saw there",
2653 },
2654);
2655
2433sub cf::client::send_msg { 2656sub cf::client::send_msg {
2434 my ($self, $color, $type, $msg, @extra) = @_; 2657 my ($self, $channel, $msg, $color, @extra) = @_;
2435 2658
2436 $msg = $self->pl->expand_cfpod ($msg); 2659 $msg = $self->pl->expand_cfpod ($msg);
2437 2660
2661 $color &= cf::NDI_CLIENT_MASK; # just in case...
2662
2663 # check predefined channels, for the benefit of C
2664 if ($CHANNEL{$channel}) {
2665 $channel = $CHANNEL{$channel};
2666
2667 $self->ext_msg (channel_info => $channel)
2668 if $self->can_msg;
2669
2670 $channel = $channel->{id};
2671
2672 } elsif (ref $channel) {
2673 # send meta info to client, if not yet sent
2674 unless (exists $self->{channel}{$channel->{id}}) {
2675 $self->{channel}{$channel->{id}} = $channel;
2676 $self->ext_msg (channel_info => $channel)
2677 if $self->can_msg;
2678 }
2679
2680 $channel = $channel->{id};
2681 }
2682
2438 return unless @extra || length $msg; 2683 return unless @extra || length $msg;
2439 2684
2440 if ($self->can_msg) { 2685 if ($self->can_msg) {
2686 # default colour, mask it out
2687 $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2688 if $color & cf::NDI_DEF;
2689
2441 $self->send_packet ("msg " . $self->{json_coder}->encode ([$color, $type, $msg, @extra])); 2690 $self->send_packet ("msg " . $self->{json_coder}->encode (
2691 [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]));
2442 } else { 2692 } else {
2443 # replace some tags by gcfclient-compatible ones
2444 for ($msg) {
2445 1 while
2446 s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2447 || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2448 || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2449 || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2450 || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2451 }
2452
2453 if ($color >= 0) { 2693 if ($color >= 0) {
2694 # replace some tags by gcfclient-compatible ones
2695 for ($msg) {
2696 1 while
2697 s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2698 || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2699 || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2700 || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2701 || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2702 }
2703
2704 $color &= cf::NDI_COLOR_MASK;
2705
2706 utf8::encode $msg;
2707
2454 if (0 && $msg =~ /\[/) { 2708 if (0 && $msg =~ /\[/) {
2709 # COMMAND/INFO
2455 $self->send_packet ("drawextinfo $color 4 0 $msg") 2710 $self->send_packet ("drawextinfo $color 10 8 $msg")
2456 } else { 2711 } else {
2457 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g; 2712 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2458 $self->send_packet ("drawinfo $color $msg") 2713 $self->send_packet ("drawinfo $color $msg")
2459 } 2714 }
2460 } 2715 }
2461 } 2716 }
2462} 2717}
2463 2718
2464=item $client->ext_event ($type, %msg) 2719=item $client->ext_msg ($type, @msg)
2465 2720
2466Sends an ext event to the client. 2721Sends an ext event to the client.
2467 2722
2468=cut 2723=cut
2469 2724
2470sub cf::client::ext_event($$%) { 2725sub cf::client::ext_msg($$@) {
2471 my ($self, $type, %msg) = @_; 2726 my ($self, $type, @msg) = @_;
2472 2727
2473 return unless $self->extcmd; 2728 if ($self->extcmd == 2) {
2474 2729 $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2730 } elsif ($self->extcmd == 1) { # TODO: remove
2475 $msg{msgtype} = "event_$type"; 2731 push @msg, msgtype => "event_$type";
2476 $self->send_packet ("ext " . $self->{json_coder}->encode (\%msg)); 2732 $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2733 }
2734}
2735
2736=item $client->ext_reply ($msgid, @msg)
2737
2738Sends an ext reply to the client.
2739
2740=cut
2741
2742sub cf::client::ext_reply($$@) {
2743 my ($self, $id, @msg) = @_;
2744
2745 if ($self->extcmd == 2) {
2746 $self->send_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
2747 } elsif ($self->extcmd == 1) {
2748 #TODO: version 1, remove
2749 unshift @msg, msgtype => "reply", msgid => $id;
2750 $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2751 }
2477} 2752}
2478 2753
2479=item $success = $client->query ($flags, "text", \&cb) 2754=item $success = $client->query ($flags, "text", \&cb)
2480 2755
2481Queues a query to the client, calling the given callback with 2756Queues a query to the client, calling the given callback with
2536 my ($ns, $buf) = @_; 2811 my ($ns, $buf) = @_;
2537 2812
2538 my $msg = eval { $ns->{json_coder}->decode ($buf) }; 2813 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2539 2814
2540 if (ref $msg) { 2815 if (ref $msg) {
2816 my ($type, $reply, @payload) =
2817 "ARRAY" eq ref $msg
2818 ? @$msg
2819 : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2820
2821 my @reply;
2822
2541 if (my $cb = $EXTICMD{$msg->{msgtype}}) { 2823 if (my $cb = $EXTICMD{$type}) {
2542 if (my %reply = $cb->($ns, $msg)) { 2824 @reply = $cb->($ns, @payload);
2543 $reply{msgid} = $msg->{msgid};
2544 $ns->send ("ext " . $ns->{json_coder}->encode (\%reply));
2545 }
2546 } 2825 }
2826
2827 $ns->ext_reply ($reply, @reply)
2828 if $reply;
2829
2547 } else { 2830 } else {
2548 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n"; 2831 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2549 } 2832 }
2550 2833
2551 cf::override; 2834 cf::override;
2598our $safe = new Safe "safe"; 2881our $safe = new Safe "safe";
2599our $safe_hole = new Safe::Hole; 2882our $safe_hole = new Safe::Hole;
2600 2883
2601$SIG{FPE} = 'IGNORE'; 2884$SIG{FPE} = 'IGNORE';
2602 2885
2603$safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time)); 2886$safe->permit_only (Opcode::opset qw(
2887 :base_core :base_mem :base_orig :base_math
2888 grepstart grepwhile mapstart mapwhile
2889 sort time
2890));
2604 2891
2605# here we export the classes and methods available to script code 2892# here we export the classes and methods available to script code
2606 2893
2607=pod 2894=pod
2608 2895
2609The following functions and methods are available within a safe environment: 2896The following functions and methods are available within a safe environment:
2610 2897
2611 cf::object 2898 cf::object
2612 contr pay_amount pay_player map x y force_find force_add 2899 contr pay_amount pay_player map x y force_find force_add
2613 insert remove 2900 insert remove name archname title slaying race decrease_ob_nr
2614 2901
2615 cf::object::player 2902 cf::object::player
2616 player 2903 player
2617 2904
2618 cf::player 2905 cf::player
2623 2910
2624=cut 2911=cut
2625 2912
2626for ( 2913for (
2627 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y 2914 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2915 insert remove inv name archname title slaying race
2628 insert remove)], 2916 decrease_ob_nr)],
2629 ["cf::object::player" => qw(player)], 2917 ["cf::object::player" => qw(player)],
2630 ["cf::player" => qw(peaceful)], 2918 ["cf::player" => qw(peaceful)],
2631 ["cf::map" => qw(trigger)], 2919 ["cf::map" => qw(trigger)],
2632) { 2920) {
2633 no strict 'refs'; 2921 no strict 'refs';
2709# the server's init and main functions 2997# the server's init and main functions
2710 2998
2711sub load_facedata($) { 2999sub load_facedata($) {
2712 my ($path) = @_; 3000 my ($path) = @_;
2713 3001
3002 # HACK to clear player env face cache, we need some signal framework
3003 # for this (global event?)
3004 %ext::player_env::MUSIC_FACE_CACHE = ();
3005
3006 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3007
2714 warn "loading facedata from $path\n"; 3008 warn "loading facedata from $path\n";
2715 3009
2716 my $facedata; 3010 my $facedata;
2717 0 < aio_load $path, $facedata 3011 0 < aio_load $path, $facedata
2718 or die "$path: $!"; 3012 or die "$path: $!";
2719 3013
2720 $facedata = Coro::Storable::thaw $facedata; 3014 $facedata = Coro::Storable::thaw $facedata;
2721 3015
2722 $facedata->{version} == 2 3016 $facedata->{version} == 2
2723 or cf::cleanup "$path: version mismatch, cannot proceed."; 3017 or cf::cleanup "$path: version mismatch, cannot proceed.";
3018
3019 # patch in the exptable
3020 $facedata->{resource}{"res/exp_table"} = {
3021 type => FT_RSRC,
3022 data => $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]),
3023 };
3024 cf::cede_to_tick;
2724 3025
2725 { 3026 {
2726 my $faces = $facedata->{faceinfo}; 3027 my $faces = $facedata->{faceinfo};
2727 3028
2728 while (my ($face, $info) = each %$faces) { 3029 while (my ($face, $info) = each %$faces) {
2729 my $idx = (cf::face::find $face) || cf::face::alloc $face; 3030 my $idx = (cf::face::find $face) || cf::face::alloc $face;
2730 cf::face::set_visibility $idx, $info->{visibility}; 3031 cf::face::set_visibility $idx, $info->{visibility};
2731 cf::face::set_magicmap $idx, $info->{magicmap}; 3032 cf::face::set_magicmap $idx, $info->{magicmap};
2732 cf::face::set_data $idx, 0, $info->{data32}, $info->{chksum32}; 3033 cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32};
2733 cf::face::set_data $idx, 1, $info->{data64}, $info->{chksum64}; 3034 cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64};
2734 3035
2735 cf::cede_to_tick; 3036 cf::cede_to_tick;
2736 } 3037 }
2737 3038
2738 while (my ($face, $info) = each %$faces) { 3039 while (my ($face, $info) = each %$faces) {
2763 3064
2764 { 3065 {
2765 # TODO: for gcfclient pleasure, we should give resources 3066 # TODO: for gcfclient pleasure, we should give resources
2766 # that gcfclient doesn't grok a >10000 face index. 3067 # that gcfclient doesn't grok a >10000 face index.
2767 my $res = $facedata->{resource}; 3068 my $res = $facedata->{resource};
2768 my $enc = JSON::XS->new->utf8->canonical; 3069
3070 my $soundconf = delete $res->{"res/sound.conf"};
2769 3071
2770 while (my ($name, $info) = each %$res) { 3072 while (my ($name, $info) = each %$res) {
2771 my $meta = $enc->encode ({
2772 name => $name,
2773 type => $info->{type},
2774 copyright => $info->{copyright}, #TODO#
2775 });
2776 my $data = pack "(w/a*)*", $meta, $info->{data};
2777 my $chk = Digest::MD5::md5 "$info->{chksum},$meta"; # mangle data checksum and metadata
2778
2779 my $idx = (cf::face::find $name) || cf::face::alloc $name; 3073 my $idx = (cf::face::find $name) || cf::face::alloc $name;
3074 my $data;
3075
3076 if ($info->{type} & 1) {
3077 # prepend meta info
3078
3079 my $meta = $enc->encode ({
3080 name => $name,
3081 %{ $info->{meta} || {} },
3082 });
3083
3084 $data = pack "(w/a*)*", $meta, $info->{data};
3085 } else {
3086 $data = $info->{data};
3087 }
3088
3089 cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data;
2780 cf::face::set_type $idx, 1; 3090 cf::face::set_type $idx, $info->{type};
2781 cf::face::set_data $idx, 0, $data, $chk;
2782 3091
2783 cf::cede_to_tick; 3092 cf::cede_to_tick;
2784 } 3093 }
3094
3095 if ($soundconf) {
3096 $soundconf = $enc->decode (delete $soundconf->{data});
3097
3098 for (0 .. SOUND_CAST_SPELL_0 - 1) {
3099 my $sound = $soundconf->{compat}[$_]
3100 or next;
3101
3102 my $face = cf::face::find "sound/$sound->[1]";
3103 cf::sound::set $sound->[0] => $face;
3104 cf::sound::old_sound_index $_, $face; # gcfclient-compat
3105 }
3106
3107 while (my ($k, $v) = each %{$soundconf->{event}}) {
3108 my $face = cf::face::find "sound/$v";
3109 cf::sound::set $k => $face;
3110 }
3111 }
2785 } 3112 }
2786 3113
2787 1 3114 1
2788} 3115}
2789 3116
3117register_exticmd fx_want => sub {
3118 my ($ns, $want) = @_;
3119
3120 while (my ($k, $v) = each %$want) {
3121 $ns->fx_want ($k, $v);
3122 }
3123};
3124
2790sub reload_regions { 3125sub reload_regions {
3126 # HACK to clear player env face cache, we need some signal framework
3127 # for this (global event?)
3128 %ext::player_env::MUSIC_FACE_CACHE = ();
3129
2791 load_resource_file "$MAPDIR/regions" 3130 load_resource_file "$MAPDIR/regions"
2792 or die "unable to load regions file\n"; 3131 or die "unable to load regions file\n";
2793 3132
2794 for (cf::region::list) { 3133 for (cf::region::list) {
2795 $_->{match} = qr/$_->{match}/ 3134 $_->{match} = qr/$_->{match}/
2831 3170
2832sub init { 3171sub init {
2833 reload_resources; 3172 reload_resources;
2834} 3173}
2835 3174
2836sub cfg_load { 3175sub reload_config {
2837 open my $fh, "<:utf8", "$CONFDIR/config" 3176 open my $fh, "<:utf8", "$CONFDIR/config"
2838 or return; 3177 or return;
2839 3178
2840 local $/; 3179 local $/;
2841 *CFG = YAML::Syck::Load <$fh>; 3180 *CFG = YAML::Syck::Load <$fh>;
2857sub main { 3196sub main {
2858 # we must not ever block the main coroutine 3197 # we must not ever block the main coroutine
2859 local $Coro::idle = sub { 3198 local $Coro::idle = sub {
2860 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d# 3199 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
2861 (async { 3200 (async {
3201 $Coro::current->{desc} = "IDLE BUG HANDLER";
2862 Event::one_event; 3202 Event::one_event;
2863 })->prio (Coro::PRIO_MAX); 3203 })->prio (Coro::PRIO_MAX);
2864 }; 3204 };
2865 3205
2866 cfg_load; 3206 reload_config;
2867 db_init; 3207 db_init;
2868 load_extensions; 3208 load_extensions;
2869 3209
2870 $TICK_WATCHER->start; 3210 $TICK_WATCHER->start;
2871 Event::loop; 3211 Event::loop;
3064 warn "reloading cf.pm"; 3404 warn "reloading cf.pm";
3065 require cf; 3405 require cf;
3066 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt 3406 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3067 3407
3068 warn "loading config and database again"; 3408 warn "loading config and database again";
3069 cf::cfg_load; 3409 cf::reload_config;
3070 3410
3071 warn "loading extensions"; 3411 warn "loading extensions";
3072 cf::load_extensions; 3412 cf::load_extensions;
3073 3413
3074 warn "reattaching attachments to objects/players"; 3414 warn "reattaching attachments to objects/players";
3110register_command "reload" => sub { 3450register_command "reload" => sub {
3111 my ($who, $arg) = @_; 3451 my ($who, $arg) = @_;
3112 3452
3113 if ($who->flag (FLAG_WIZ)) { 3453 if ($who->flag (FLAG_WIZ)) {
3114 $who->message ("reloading server."); 3454 $who->message ("reloading server.");
3455 async {
3456 $Coro::current->{desc} = "perl_reload";
3115 async { reload_perl }; 3457 reload_perl;
3458 };
3116 } 3459 }
3117}; 3460};
3118 3461
3119unshift @INC, $LIBDIR; 3462unshift @INC, $LIBDIR;
3120 3463
3139 my $signal = new Coro::Signal; 3482 my $signal = new Coro::Signal;
3140 push @WAIT_FOR_TICK_BEGIN, $signal; 3483 push @WAIT_FOR_TICK_BEGIN, $signal;
3141 $signal->wait; 3484 $signal->wait;
3142} 3485}
3143 3486
3144 my $min = 1e6;#d#
3145 my $avg = 10;
3146$TICK_WATCHER = Event->timer ( 3487$TICK_WATCHER = Event->timer (
3147 reentrant => 0, 3488 reentrant => 0,
3148 parked => 1, 3489 parked => 1,
3149 prio => 0, 3490 prio => 0,
3150 at => $NEXT_TICK || $TICK, 3491 at => $NEXT_TICK || $TICK,
3158 3499
3159 $NOW = $tick_start = Event::time; 3500 $NOW = $tick_start = Event::time;
3160 3501
3161 cf::server_tick; # one server iteration 3502 cf::server_tick; # one server iteration
3162 3503
3163 0 && sync_job {#d#
3164 for(1..10) {
3165 my $t = Event::time;
3166 my $map = my $map = new_from_path cf::map "/tmp/x.map"
3167 or die;
3168
3169 $map->width (50);
3170 $map->height (50);
3171 $map->alloc;
3172 $map->_load_objects ("/tmp/x.map", 1);
3173 my $t = Event::time - $t;
3174
3175 #next unless $t < 0.0013;#d#
3176 if ($t < $min) {
3177 $min = $t;
3178 }
3179 $avg = $avg * 0.99 + $t * 0.01;
3180 }
3181 warn "XXXXXXXXXXXXXXXXXX min $min avg $avg\n";#d#
3182 exit 0;
3183 # 2007-05-22 02:33:04.569 min 0.00112509727478027 avg 0.0012259249572477
3184 };
3185
3186 $RUNTIME += $TICK; 3504 $RUNTIME += $TICK;
3187 $NEXT_TICK += $TICK; 3505 $NEXT_TICK += $TICK;
3188 3506
3189 if ($NOW >= $NEXT_RUNTIME_WRITE) { 3507 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3190 $NEXT_RUNTIME_WRITE = $NOW + 10; 3508 $NEXT_RUNTIME_WRITE = $NOW + 10;
3191 Coro::async_pool { 3509 Coro::async_pool {
3510 $Coro::current->{desc} = "runtime saver";
3192 write_runtime 3511 write_runtime
3193 or warn "ERROR: unable to write runtime file: $!"; 3512 or warn "ERROR: unable to write runtime file: $!";
3194 }; 3513 };
3195 } 3514 }
3196 3515
3197# my $AFTER = Event::time;
3198# warn $AFTER - $NOW;#d#
3199
3200 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) { 3516 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3201 $sig->send; 3517 $sig->send;
3202 } 3518 }
3203 while (my $sig = shift @WAIT_FOR_TICK) { 3519 while (my $sig = shift @WAIT_FOR_TICK) {
3204 $sig->send; 3520 $sig->send;
3214 3530
3215 $LOAD = ($NOW - $tick_start) / $TICK; 3531 $LOAD = ($NOW - $tick_start) / $TICK;
3216 $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25; 3532 $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3217 3533
3218 _post_tick; 3534 _post_tick;
3219
3220
3221 }, 3535 },
3222); 3536);
3223 3537
3224{ 3538{
3539 BDB::min_parallel 8;
3225 BDB::max_poll_time $TICK * 0.1; 3540 BDB::max_poll_time $TICK * 0.1;
3226 $BDB_POLL_WATCHER = Event->io ( 3541 $BDB_POLL_WATCHER = Event->io (
3227 reentrant => 0, 3542 reentrant => 0,
3228 fd => BDB::poll_fileno, 3543 fd => BDB::poll_fileno,
3229 poll => 'r', 3544 poll => 'r',
3230 prio => 0, 3545 prio => 0,
3231 data => WF_AUTOCANCEL, 3546 data => WF_AUTOCANCEL,
3232 cb => \&BDB::poll_cb, 3547 cb => \&BDB::poll_cb,
3233 ); 3548 );
3234 BDB::min_parallel 8;
3235 3549
3236 BDB::set_sync_prepare { 3550 BDB::set_sync_prepare {
3237 my $status; 3551 my $status;
3238 my $current = $Coro::current; 3552 my $current = $Coro::current;
3239 ( 3553 (
3248 ) 3562 )
3249 }; 3563 };
3250 3564
3251 unless ($DB_ENV) { 3565 unless ($DB_ENV) {
3252 $DB_ENV = BDB::db_env_create; 3566 $DB_ENV = BDB::db_env_create;
3567 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC
3568 | BDB::LOG_AUTOREMOVE, 1);
3569 $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
3570 $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
3253 3571
3254 cf::sync_job { 3572 cf::sync_job {
3255 eval { 3573 eval {
3256 BDB::db_env_open 3574 BDB::db_env_open
3257 $DB_ENV, 3575 $DB_ENV,
3259 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN 3577 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3260 | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE, 3578 | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3261 0666; 3579 0666;
3262 3580
3263 cf::cleanup "db_env_open($BDBDIR): $!" if $!; 3581 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3264
3265 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC, 1);
3266 $DB_ENV->set_lk_detect;
3267 }; 3582 };
3268 3583
3269 cf::cleanup "db_env_open(db): $@" if $@; 3584 cf::cleanup "db_env_open(db): $@" if $@;
3270 }; 3585 };
3271 } 3586 }
3587
3588 $BDB_DEADLOCK_WATCHER = Event->timer (
3589 after => 3,
3590 interval => 1,
3591 hard => 1,
3592 prio => 0,
3593 data => WF_AUTOCANCEL,
3594 cb => sub {
3595 BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
3596 },
3597 );
3598 $BDB_CHECKPOINT_WATCHER = Event->timer (
3599 after => 11,
3600 interval => 60,
3601 hard => 1,
3602 prio => 0,
3603 data => WF_AUTOCANCEL,
3604 cb => sub {
3605 BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
3606 },
3607 );
3608 $BDB_TRICKLE_WATCHER = Event->timer (
3609 after => 5,
3610 interval => 10,
3611 hard => 1,
3612 prio => 0,
3613 data => WF_AUTOCANCEL,
3614 cb => sub {
3615 BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
3616 },
3617 );
3272} 3618}
3273 3619
3274{ 3620{
3275 IO::AIO::min_parallel 8; 3621 IO::AIO::min_parallel 8;
3276 3622
3279 $AIO_POLL_WATCHER = Event->io ( 3625 $AIO_POLL_WATCHER = Event->io (
3280 reentrant => 0, 3626 reentrant => 0,
3281 data => WF_AUTOCANCEL, 3627 data => WF_AUTOCANCEL,
3282 fd => IO::AIO::poll_fileno, 3628 fd => IO::AIO::poll_fileno,
3283 poll => 'r', 3629 poll => 'r',
3284 prio => 6, 3630 prio => 0,
3285 cb => \&IO::AIO::poll_cb, 3631 cb => \&IO::AIO::poll_cb,
3286 ); 3632 );
3287} 3633}
3288 3634
3289my $_log_backtrace; 3635my $_log_backtrace;
3295 3641
3296 # limit the # of concurrent backtraces 3642 # limit the # of concurrent backtraces
3297 if ($_log_backtrace < 2) { 3643 if ($_log_backtrace < 2) {
3298 ++$_log_backtrace; 3644 ++$_log_backtrace;
3299 async { 3645 async {
3646 $Coro::current->{desc} = "abt $msg";
3647
3300 my @bt = fork_call { 3648 my @bt = fork_call {
3301 @addr = map { sprintf "%x", $_ } @addr; 3649 @addr = map { sprintf "%x", $_ } @addr;
3302 my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X; 3650 my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3303 open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |" 3651 open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3304 or die "addr2line: $!"; 3652 or die "addr2line: $!";

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines