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.493 by root, Mon Oct 26 02:34:37 2009 UTC vs.
Revision 1.526 by root, Wed Apr 21 05:48:35 2010 UTC

1# 1#
2# This file is part of Deliantra, the Roguelike Realtime MMORPG. 2# This file is part of Deliantra, the Roguelike Realtime MMORPG.
3# 3#
4# Copyright (©) 2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team 4# Copyright (©) 2006,2007,2008,2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5# 5#
6# Deliantra is free software: you can redistribute it and/or modify it under 6# Deliantra is free software: you can redistribute it and/or modify it under
7# the terms of the Affero GNU General Public License as published by the 7# the terms of the Affero GNU General Public License as published by the
8# Free Software Foundation, either version 3 of the License, or (at your 8# Free Software Foundation, either version 3 of the License, or (at your
9# option) any later version. 9# option) any later version.
53use Coro::Util (); 53use Coro::Util ();
54 54
55use JSON::XS 2.01 (); 55use JSON::XS 2.01 ();
56use BDB (); 56use BDB ();
57use Data::Dumper; 57use Data::Dumper;
58use Digest::MD5;
59use Fcntl; 58use Fcntl;
60use YAML::XS (); 59use YAML::XS ();
61use IO::AIO (); 60use IO::AIO ();
62use Time::HiRes; 61use Time::HiRes;
63use Compress::LZF; 62use Compress::LZF;
109our $PIDFILE = "$LOCALDIR/pid"; 108our $PIDFILE = "$LOCALDIR/pid";
110our $RUNTIMEFILE = "$LOCALDIR/runtime"; 109our $RUNTIMEFILE = "$LOCALDIR/runtime";
111 110
112our %RESOURCE; 111our %RESOURCE;
113 112
113our $OUTPUT_RATE_MIN = 4000;
114our $OUTPUT_RATE_MAX = 100000;
115
114our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!) 116our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!)
115our $NEXT_RUNTIME_WRITE; # when should the runtime file be written 117our $NEXT_RUNTIME_WRITE; # when should the runtime file be written
116our $NEXT_TICK; 118our $NEXT_TICK;
117our $USE_FSYNC = 1; # use fsync to write maps - default on 119our $USE_FSYNC = 1; # use fsync to write maps - default on
118 120
163 165
164our $EMERGENCY_POSITION; 166our $EMERGENCY_POSITION;
165 167
166sub cf::map::normalise; 168sub cf::map::normalise;
167 169
170sub in_main() {
171 $Coro::current == $Coro::main
172}
173
168############################################################################# 174#############################################################################
169 175
170%REFLECT = (); 176%REFLECT = ();
171for (@REFLECT) { 177for (@REFLECT) {
172 my $reflect = JSON::XS::decode_json $_; 178 my $reflect = JSON::XS::decode_json $_;
259$Coro::State::DIEHOOK = sub { 265$Coro::State::DIEHOOK = sub {
260 return unless $^S eq 0; # "eq", not "==" 266 return unless $^S eq 0; # "eq", not "=="
261 267
262 warn Carp::longmess $_[0]; 268 warn Carp::longmess $_[0];
263 269
264 if ($Coro::current == $Coro::main) {#d# 270 if (in_main) {#d#
265 warn "DIEHOOK called in main context, Coro bug?\n";#d# 271 warn "DIEHOOK called in main context, Coro bug?\n";#d#
266 return;#d# 272 return;#d#
267 }#d# 273 }#d#
268 274
269 # kill coroutine otherwise 275 # kill coroutine otherwise
397} 403}
398 404
399=item cf::periodic $interval, $cb 405=item cf::periodic $interval, $cb
400 406
401Like EV::periodic, but randomly selects a starting point so that the actions 407Like EV::periodic, but randomly selects a starting point so that the actions
402get spread over timer. 408get spread over time.
403 409
404=cut 410=cut
405 411
406sub periodic($$) { 412sub periodic($$) {
407 my ($interval, $cb) = @_; 413 my ($interval, $cb) = @_;
424 430
425=cut 431=cut
426 432
427our @SLOT_QUEUE; 433our @SLOT_QUEUE;
428our $SLOT_QUEUE; 434our $SLOT_QUEUE;
435our $SLOT_DECAY = 0.9;
429 436
430$SLOT_QUEUE->cancel if $SLOT_QUEUE; 437$SLOT_QUEUE->cancel if $SLOT_QUEUE;
431$SLOT_QUEUE = Coro::async { 438$SLOT_QUEUE = Coro::async {
432 $Coro::current->desc ("timeslot manager"); 439 $Coro::current->desc ("timeslot manager");
433 440
434 my $signal = new Coro::Signal; 441 my $signal = new Coro::Signal;
442 my $busy;
435 443
436 while () { 444 while () {
437 next_job: 445 next_job:
446
438 my $avail = cf::till_tick; 447 my $avail = cf::till_tick;
439 if ($avail > 0.01) { 448
440 for (0 .. $#SLOT_QUEUE) { 449 for (0 .. $#SLOT_QUEUE) {
441 if ($SLOT_QUEUE[$_][0] < $avail) { 450 if ($SLOT_QUEUE[$_][0] <= $avail) {
451 $busy = 0;
442 my $job = splice @SLOT_QUEUE, $_, 1, (); 452 my $job = splice @SLOT_QUEUE, $_, 1, ();
443 $job->[2]->send; 453 $job->[2]->send;
444 Coro::cede; 454 Coro::cede;
445 goto next_job; 455 goto next_job;
446 } 456 } else {
457 $SLOT_QUEUE[$_][0] *= $SLOT_DECAY;
447 } 458 }
448 } 459 }
449 460
450 if (@SLOT_QUEUE) { 461 if (@SLOT_QUEUE) {
451 # we do not use wait_for_tick() as it returns immediately when tick is inactive 462 # we do not use wait_for_tick() as it returns immediately when tick is inactive
452 push @cf::WAIT_FOR_TICK, $signal; 463 push @cf::WAIT_FOR_TICK, $signal;
453 $signal->wait; 464 $signal->wait;
454 } else { 465 } else {
466 $busy = 0;
455 Coro::schedule; 467 Coro::schedule;
456 } 468 }
457 } 469 }
458}; 470};
459 471
460sub get_slot($;$$) { 472sub get_slot($;$$) {
461 return if tick_inhibit || $Coro::current == $Coro::main; 473 return if tick_inhibit || $Coro::current == $Coro::main;
462 474
463 my ($time, $pri, $name) = @_; 475 my ($time, $pri, $name) = @_;
464 476
465 $time = $TICK * .6 if $time > $TICK * .6; 477 $time = clamp $time, 0.01, $TICK * .6;
478
466 my $sig = new Coro::Signal; 479 my $sig = new Coro::Signal;
467 480
468 push @SLOT_QUEUE, [$time, $pri, $sig, $name]; 481 push @SLOT_QUEUE, [$time, $pri, $sig, $name];
469 @SLOT_QUEUE = sort { $b->[1] <=> $a->[1] } @SLOT_QUEUE; 482 @SLOT_QUEUE = sort { $b->[1] <=> $a->[1] } @SLOT_QUEUE;
470 $SLOT_QUEUE->ready; 483 $SLOT_QUEUE->ready;
497 510
498sub sync_job(&) { 511sub sync_job(&) {
499 my ($job) = @_; 512 my ($job) = @_;
500 513
501 if ($Coro::current == $Coro::main) { 514 if ($Coro::current == $Coro::main) {
502 my $time = EV::time; 515 my $time = AE::time;
503 516
504 # this is the main coro, too bad, we have to block 517 # this is the main coro, too bad, we have to block
505 # till the operation succeeds, freezing the server :/ 518 # till the operation succeeds, freezing the server :/
506 519
507 LOG llevError, Carp::longmess "sync job";#d# 520 LOG llevError, Carp::longmess "sync job";#d#
524 } else { 537 } else {
525 EV::loop EV::LOOP_ONESHOT; 538 EV::loop EV::LOOP_ONESHOT;
526 } 539 }
527 } 540 }
528 541
529 my $time = EV::time - $time; 542 my $time = AE::time - $time;
530 543
531 $TICK_START += $time; # do not account sync jobs to server load 544 $TICK_START += $time; # do not account sync jobs to server load
532 545
533 wantarray ? @res : $res[0] 546 wantarray ? @res : $res[0]
534 } else { 547 } else {
578 reset_signals; 591 reset_signals;
579 &$cb 592 &$cb
580 }, @args; 593 }, @args;
581 594
582 wantarray ? @res : $res[-1] 595 wantarray ? @res : $res[-1]
596}
597
598sub objinfo {
599 (
600 "counter value" => cf::object::object_count,
601 "objects created" => cf::object::create_count,
602 "objects destroyed" => cf::object::destroy_count,
603 "freelist size" => cf::object::free_count,
604 "allocated objects" => cf::object::objects_size,
605 "active objects" => cf::object::actives_size,
606 )
583} 607}
584 608
585=item $coin = coin_from_name $name 609=item $coin = coin_from_name $name
586 610
587=cut 611=cut
1304} 1328}
1305 1329
1306use File::Glob (); 1330use File::Glob ();
1307 1331
1308cf::player->attach ( 1332cf::player->attach (
1309 on_command => sub { 1333 on_unknown_command => sub {
1310 my ($pl, $name, $params) = @_; 1334 my ($pl, $name, $params) = @_;
1311 1335
1312 my $cb = $COMMAND{$name} 1336 my $cb = $COMMAND{$name}
1313 or return; 1337 or return;
1314 1338
1393 . "\n};\n1"; 1417 . "\n};\n1";
1394 1418
1395 $todo{$base} = \%ext; 1419 $todo{$base} = \%ext;
1396 } 1420 }
1397 1421
1422 my $pass = 0;
1398 my %done; 1423 my %done;
1399 while (%todo) { 1424 while (%todo) {
1400 my $progress; 1425 my $progress;
1401 1426
1427 ++$pass;
1428
1429 ext:
1402 while (my ($k, $v) = each %todo) { 1430 while (my ($k, $v) = each %todo) {
1403 for (split /,\s*/, $v->{meta}{depends}) { 1431 for (split /,\s*/, $v->{meta}{depends}) {
1404 goto skip 1432 next ext
1405 unless exists $done{$_}; 1433 unless exists $done{$_};
1406 } 1434 }
1407 1435
1408 warn "... loading '$k' into '$v->{pkg}'\n"; 1436 warn "... pass $pass, loading '$k' into '$v->{pkg}'\n";
1409 1437
1410 unless (eval $v->{source}) { 1438 my $active = eval $v->{source};
1439
1440 if (length $@) {
1411 my $msg = $@ ? "$v->{path}: $@\n" 1441 warn "$v->{path}: $@\n";
1412 : "$v->{base}: extension inactive.\n";
1413 1442
1414 if (exists $v->{meta}{mandatory}) {
1415 warn $msg;
1416 cf::cleanup "mandatory extension failed to load, exiting."; 1443 cf::cleanup "mandatory extension '$k' failed to load, exiting."
1417 } 1444 if exists $v->{meta}{mandatory};
1418 1445
1419 warn $msg; 1446 warn "$v->{base}: optional extension cannot be loaded, skipping.\n";
1447 delete $todo{$k};
1448 } else {
1449 $done{$k} = delete $todo{$k};
1450 push @EXTS, $v->{pkg};
1451 $progress = 1;
1452
1453 warn "$v->{base}: extension inactive.\n"
1454 unless $active;
1420 } 1455 }
1421
1422 $done{$k} = delete $todo{$k};
1423 push @EXTS, $v->{pkg};
1424 $progress = 1;
1425 } 1456 }
1426 1457
1427 skip: 1458 unless ($progress) {
1428 die "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n" 1459 warn "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n";
1429 unless $progress; 1460
1461 while (my ($k, $v) = each %todo) {
1462 cf::cleanup "mandatory extension '$k' has unresolved dependencies, exiting."
1463 if exists $v->{meta}{mandatory};
1464 }
1465 }
1430 } 1466 }
1431 }; 1467 };
1432} 1468}
1433 1469
1434############################################################################# 1470#############################################################################
1518 $cf::PLAYER{$login} = $pl 1554 $cf::PLAYER{$login} = $pl
1519 } 1555 }
1520 } 1556 }
1521} 1557}
1522 1558
1559cf::player->attach (
1560 on_load => sub {
1561 my ($pl, $path) = @_;
1562
1563 # restore slots saved in save, below
1564 my $slots = delete $pl->{_slots};
1565
1566 $pl->ob->current_weapon ($slots->[0]);
1567 $pl->combat_ob ($slots->[1]);
1568 $pl->ranged_ob ($slots->[2]);
1569 },
1570);
1571
1523sub save($) { 1572sub save($) {
1524 my ($pl) = @_; 1573 my ($pl) = @_;
1525 1574
1526 return if $pl->{deny_save}; 1575 return if $pl->{deny_save};
1527 1576
1532 1581
1533 aio_mkdir playerdir $pl, 0770; 1582 aio_mkdir playerdir $pl, 0770;
1534 $pl->{last_save} = $cf::RUNTIME; 1583 $pl->{last_save} = $cf::RUNTIME;
1535 1584
1536 cf::get_slot 0.01; 1585 cf::get_slot 0.01;
1586
1587 # save slots, to be restored later
1588 local $pl->{_slots} = [$pl->ob->current_weapon, $pl->combat_ob, $pl->ranged_ob];
1537 1589
1538 $pl->save_pl ($path); 1590 $pl->save_pl ($path);
1539 cf::cede_to_tick; 1591 cf::cede_to_tick;
1540} 1592}
1541 1593
1643 \@logins 1695 \@logins
1644} 1696}
1645 1697
1646=item $player->maps 1698=item $player->maps
1647 1699
1700=item cf::player::maps $login
1701
1648Returns an arrayref of map paths that are private for this 1702Returns an arrayref of map paths that are private for this
1649player. May block. 1703player. May block.
1650 1704
1651=cut 1705=cut
1652 1706
1714=cut 1768=cut
1715 1769
1716sub find_by_path($) { 1770sub find_by_path($) {
1717 my ($path) = @_; 1771 my ($path) = @_;
1718 1772
1773 $path =~ s/^~[^\/]*//; # skip ~login
1774
1719 my ($match, $specificity); 1775 my ($match, $specificity);
1720 1776
1721 for my $region (list) { 1777 for my $region (list) {
1722 if ($region->{match} && $path =~ $region->{match}) { 1778 if ($region->{match} && $path =~ $region->{match}) {
1723 ($match, $specificity) = ($region, $region->specificity) 1779 ($match, $specificity) = ($region, $region->specificity)
1754 my $lock = cf::lock_acquire "generate_random_map"; # the random map generator is NOT reentrant ATM 1810 my $lock = cf::lock_acquire "generate_random_map"; # the random map generator is NOT reentrant ATM
1755 1811
1756 # mit "rum" bekleckern, nicht 1812 # mit "rum" bekleckern, nicht
1757 $self->_create_random_map ( 1813 $self->_create_random_map (
1758 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle}, 1814 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1759 $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle}, 1815 $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle}, $rmp->{miningstyle},
1760 $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map}, 1816 $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1761 $rmp->{exit_on_final_map}, 1817 $rmp->{exit_on_final_map},
1762 $rmp->{xsize}, $rmp->{ysize}, 1818 $rmp->{xsize}, $rmp->{ysize},
1763 $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3}, 1819 $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1764 $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase}, 1820 $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1786 1842
1787 $EXT_MAP{$pkg} = [$prio, qr<$regex>]; 1843 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1788} 1844}
1789 1845
1790# also paths starting with '/' 1846# also paths starting with '/'
1791$EXT_MAP{"cf::map"} = [0, qr{^(?=/)}]; 1847$EXT_MAP{"cf::map::wrap"} = [0, qr{^(?=/)}];
1792 1848
1793sub thawer_merge { 1849sub thawer_merge {
1794 my ($self, $merge) = @_; 1850 my ($self, $merge) = @_;
1795 1851
1796 # we have to keep some variables in memory intact 1852 # we have to keep some variables in memory intact
2010 2066
2011 $cf::MAP{$path} = $map 2067 $cf::MAP{$path} = $map
2012 } 2068 }
2013} 2069}
2014 2070
2015sub pre_load { } 2071sub pre_load { }
2016sub post_load { } 2072#sub post_load { } # XS
2017 2073
2018sub load { 2074sub load {
2019 my ($self) = @_; 2075 my ($self) = @_;
2020 2076
2021 local $self->{deny_reset} = 1; # loading can take a long time 2077 local $self->{deny_reset} = 1; # loading can take a long time
2078 } 2134 }
2079 2135
2080 $self->post_load; 2136 $self->post_load;
2081} 2137}
2082 2138
2139# customize the map for a given player, i.e.
2140# return the _real_ map. used by e.g. per-player
2141# maps to change the path to ~playername/mappath
2083sub customise_for { 2142sub customise_for {
2084 my ($self, $ob) = @_; 2143 my ($self, $ob) = @_;
2085 2144
2086 return find "~" . $ob->name . "/" . $self->{path} 2145 return find "~" . $ob->name . "/" . $self->{path}
2087 if $self->per_player; 2146 if $self->per_player;
2164 $MAP_PREFETCHER->prio (6); 2223 $MAP_PREFETCHER->prio (6);
2165 2224
2166 () 2225 ()
2167} 2226}
2168 2227
2228# common code, used by both ->save and ->swapout
2169sub save { 2229sub _save {
2170 my ($self) = @_; 2230 my ($self) = @_;
2171
2172 my $lock = cf::lock_acquire "map_data:$self->{path}";
2173 2231
2174 $self->{last_save} = $cf::RUNTIME; 2232 $self->{last_save} = $cf::RUNTIME;
2175 2233
2176 return unless $self->dirty; 2234 return unless $self->dirty;
2177 2235
2197 } else { 2255 } else {
2198 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES); 2256 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2199 } 2257 }
2200} 2258}
2201 2259
2260sub save {
2261 my ($self) = @_;
2262
2263 my $lock = cf::lock_acquire "map_data:$self->{path}";
2264
2265 $self->_save;
2266}
2267
2202sub swap_out { 2268sub swap_out {
2203 my ($self) = @_; 2269 my ($self) = @_;
2204 2270
2205 # save first because save cedes
2206 $self->save;
2207
2208 my $lock = cf::lock_acquire "map_data:$self->{path}"; 2271 my $lock = cf::lock_acquire "map_data:$self->{path}";
2209 2272
2210 return if $self->players;
2211 return if $self->in_memory != cf::MAP_ACTIVE; 2273 return if $self->in_memory != cf::MAP_ACTIVE;
2212 return if $self->{deny_save}; 2274 return if $self->{deny_save};
2275 return if $self->players;
2213 2276
2277 # first deactivate the map and "unlink" it from the core
2278 $self->deactivate;
2279 $_->clear_links_to ($self) for values %cf::MAP;
2214 $self->in_memory (cf::MAP_SWAPPED); 2280 $self->in_memory (cf::MAP_SWAPPED);
2281
2282 # then atomically save
2283 $self->_save;
2284
2285 # then free the map
2286 $self->clear;
2287}
2288
2289sub reset_at {
2290 my ($self) = @_;
2291
2292 # TODO: safety, remove and allow resettable per-player maps
2293 return 1e99 if $self->{deny_reset};
2294
2295 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2296 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2297
2298 $time + $to
2299}
2300
2301sub should_reset {
2302 my ($self) = @_;
2303
2304 $self->reset_at <= $cf::RUNTIME
2305}
2306
2307sub reset {
2308 my ($self) = @_;
2309
2310 my $lock = cf::lock_acquire "map_data:$self->{path}";
2311
2312 return if $self->players;
2313
2314 warn "resetting map ", $self->path, "\n";
2315
2316 $self->in_memory (cf::MAP_SWAPPED);
2317
2318 # need to save uniques path
2319 unless ($self->{deny_save}) {
2320 my $uniq = $self->uniq_path; utf8::encode $uniq;
2321
2322 $self->_save_objects ($uniq, cf::IO_UNIQUES)
2323 if $uniq;
2324 }
2325
2326 delete $cf::MAP{$self->path};
2215 2327
2216 $self->deactivate; 2328 $self->deactivate;
2217 $_->clear_links_to ($self) for values %cf::MAP; 2329 $_->clear_links_to ($self) for values %cf::MAP;
2218 $self->clear; 2330 $self->clear;
2219}
2220
2221sub reset_at {
2222 my ($self) = @_;
2223
2224 # TODO: safety, remove and allow resettable per-player maps
2225 return 1e99 if $self->{deny_reset};
2226
2227 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2228 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2229
2230 $time + $to
2231}
2232
2233sub should_reset {
2234 my ($self) = @_;
2235
2236 $self->reset_at <= $cf::RUNTIME
2237}
2238
2239sub reset {
2240 my ($self) = @_;
2241
2242 my $lock = cf::lock_acquire "map_data:$self->{path}";
2243
2244 return if $self->players;
2245
2246 warn "resetting map ", $self->path, "\n";
2247
2248 $self->in_memory (cf::MAP_SWAPPED);
2249
2250 # need to save uniques path
2251 unless ($self->{deny_save}) {
2252 my $uniq = $self->uniq_path; utf8::encode $uniq;
2253
2254 $self->_save_objects ($uniq, cf::IO_UNIQUES)
2255 if $uniq;
2256 }
2257
2258 delete $cf::MAP{$self->path};
2259
2260 $self->deactivate;
2261 $_->clear_links_to ($self) for values %cf::MAP;
2262 $self->clear;
2263 2331
2264 $self->unlink_save; 2332 $self->unlink_save;
2265 $self->destroy; 2333 $self->destroy;
2266} 2334}
2267 2335
2275 2343
2276 delete $cf::MAP{$self->path}; 2344 delete $cf::MAP{$self->path};
2277 2345
2278 $self->unlink_save; 2346 $self->unlink_save;
2279 2347
2280 bless $self, "cf::map"; 2348 bless $self, "cf::map::wrap";
2281 delete $self->{deny_reset}; 2349 delete $self->{deny_reset};
2282 $self->{deny_save} = 1; 2350 $self->{deny_save} = 1;
2283 $self->reset_timeout (1); 2351 $self->reset_timeout (1);
2284 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++)); 2352 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2285 2353
2551 return if UNIVERSAL::isa $self->map, "ext::map_link"; 2619 return if UNIVERSAL::isa $self->map, "ext::map_link";
2552 2620
2553 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y] 2621 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2554 if $self->map && $self->map->{path} ne "{link}"; 2622 if $self->map && $self->map->{path} ne "{link}";
2555 2623
2556 $self->enter_map ($LINK_MAP || link_map, 10, 10); 2624 $self->enter_map ($LINK_MAP || link_map, 3, 3);
2557} 2625}
2558 2626
2559sub cf::object::player::leave_link { 2627sub cf::object::player::leave_link {
2560 my ($self, $map, $x, $y) = @_; 2628 my ($self, $map, $x, $y) = @_;
2561 2629
2590 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext 2658 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2591 $self->enter_map ($map, $x, $y); 2659 $self->enter_map ($map, $x, $y);
2592 2660
2593 # only activate afterwards, to support waiting in hooks 2661 # only activate afterwards, to support waiting in hooks
2594 $self->activate_recursive; 2662 $self->activate_recursive;
2663# unless exists $self->{_link_pos}; # maybe we re in-flight again
2595} 2664}
2596 2665
2597=item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]]) 2666=item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2598 2667
2599Moves the player to the given map-path and coordinates by first freezing 2668Moves the player to the given map-path and coordinates by first freezing
2785sub cf::client::send_drawinfo { 2854sub cf::client::send_drawinfo {
2786 my ($self, $text, $flags) = @_; 2855 my ($self, $text, $flags) = @_;
2787 2856
2788 utf8::encode $text; 2857 utf8::encode $text;
2789 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text); 2858 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2859}
2860
2861=item $client->send_big_packet ($pkt)
2862
2863Like C<send_packet>, but tries to compress large packets, and fragments
2864them as required.
2865
2866=cut
2867
2868our $MAXFRAGSIZE = cf::MAXSOCKBUF - 64;
2869
2870sub cf::client::send_big_packet {
2871 my ($self, $pkt) = @_;
2872
2873 # try lzf for large packets
2874 $pkt = "lzf " . Compress::LZF::compress $pkt
2875 if 1024 <= length $pkt and $self->{can_lzf};
2876
2877 # split very large packets
2878 if ($MAXFRAGSIZE < length $pkt and $self->{can_lzf}) {
2879 $self->send_packet ("frag $_") for unpack "(a$MAXFRAGSIZE)*", $pkt;
2880 $pkt = "frag";
2881 }
2882
2883 $self->send_packet ($pkt);
2790} 2884}
2791 2885
2792=item $client->send_msg ($channel, $msg, $color, [extra...]) 2886=item $client->send_msg ($channel, $msg, $color, [extra...])
2793 2887
2794Send a drawinfo or msg packet to the client, formatting the msg for the 2888Send a drawinfo or msg packet to the client, formatting the msg for the
2946 my $pkt = "msg " 3040 my $pkt = "msg "
2947 . $self->{json_coder}->encode ( 3041 . $self->{json_coder}->encode (
2948 [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra] 3042 [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]
2949 ); 3043 );
2950 3044
2951 # try lzf for large packets
2952 $pkt = "lzf " . Compress::LZF::compress $pkt
2953 if 1024 <= length $pkt and $self->{can_lzf};
2954
2955 # split very large packets
2956 if (8192 < length $pkt and $self->{can_lzf}) {
2957 $self->send_packet ("frag $_") for unpack "(a8192)*", $pkt;
2958 $pkt = "frag";
2959 }
2960
2961 $self->send_packet ($pkt); 3045 $self->send_big_packet ($pkt);
2962} 3046}
2963 3047
2964=item $client->ext_msg ($type, @msg) 3048=item $client->ext_msg ($type, @msg)
2965 3049
2966Sends an ext event to the client. 3050Sends an ext event to the client.
2969 3053
2970sub cf::client::ext_msg($$@) { 3054sub cf::client::ext_msg($$@) {
2971 my ($self, $type, @msg) = @_; 3055 my ($self, $type, @msg) = @_;
2972 3056
2973 if ($self->extcmd == 2) { 3057 if ($self->extcmd == 2) {
2974 $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg])); 3058 $self->send_big_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2975 } elsif ($self->extcmd == 1) { # TODO: remove 3059 } elsif ($self->extcmd == 1) { # TODO: remove
2976 push @msg, msgtype => "event_$type"; 3060 push @msg, msgtype => "event_$type";
2977 $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg})); 3061 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2978 } 3062 }
2979} 3063}
2980 3064
2981=item $client->ext_reply ($msgid, @msg) 3065=item $client->ext_reply ($msgid, @msg)
2982 3066
2986 3070
2987sub cf::client::ext_reply($$@) { 3071sub cf::client::ext_reply($$@) {
2988 my ($self, $id, @msg) = @_; 3072 my ($self, $id, @msg) = @_;
2989 3073
2990 if ($self->extcmd == 2) { 3074 if ($self->extcmd == 2) {
2991 $self->send_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg])); 3075 $self->send_big_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
2992 } elsif ($self->extcmd == 1) { 3076 } elsif ($self->extcmd == 1) {
2993 #TODO: version 1, remove 3077 #TODO: version 1, remove
2994 unshift @msg, msgtype => "reply", msgid => $id; 3078 unshift @msg, msgtype => "reply", msgid => $id;
2995 $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg})); 3079 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2996 } 3080 }
2997} 3081}
2998 3082
2999=item $success = $client->query ($flags, "text", \&cb) 3083=item $success = $client->query ($flags, "text", \&cb)
3000 3084
3101 3185
3102 $coro 3186 $coro
3103} 3187}
3104 3188
3105cf::client->attach ( 3189cf::client->attach (
3106 on_destroy => sub { 3190 on_client_destroy => sub {
3107 my ($ns) = @_; 3191 my ($ns) = @_;
3108 3192
3109 $_->cancel for values %{ (delete $ns->{_coro}) || {} }; 3193 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
3110 }, 3194 },
3111); 3195);
3127our $safe_hole = new Safe::Hole; 3211our $safe_hole = new Safe::Hole;
3128 3212
3129$SIG{FPE} = 'IGNORE'; 3213$SIG{FPE} = 'IGNORE';
3130 3214
3131$safe->permit_only (Opcode::opset qw( 3215$safe->permit_only (Opcode::opset qw(
3132 :base_core :base_mem :base_orig :base_math 3216 :base_core :base_mem :base_orig :base_math :base_loop
3133 grepstart grepwhile mapstart mapwhile 3217 grepstart grepwhile mapstart mapwhile
3134 sort time 3218 sort time
3135)); 3219));
3136 3220
3137# here we export the classes and methods available to script code 3221# here we export the classes and methods available to script code
3189 $qcode =~ s/"/‟/g; # not allowed in #line filenames 3273 $qcode =~ s/"/‟/g; # not allowed in #line filenames
3190 $qcode =~ s/\n/\\n/g; 3274 $qcode =~ s/\n/\\n/g;
3191 3275
3192 %vars = (_dummy => 0) unless %vars; 3276 %vars = (_dummy => 0) unless %vars;
3193 3277
3278 my @res;
3194 local $_; 3279 local $_;
3195 local @safe::cf::_safe_eval_args = values %vars;
3196 3280
3197 my $eval = 3281 my $eval =
3198 "do {\n" 3282 "do {\n"
3199 . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n" 3283 . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3200 . "#line 0 \"{$qcode}\"\n" 3284 . "#line 0 \"{$qcode}\"\n"
3201 . $code 3285 . $code
3202 . "\n}" 3286 . "\n}"
3203 ; 3287 ;
3204 3288
3289 if ($CFG{safe_eval}) {
3205 sub_generation_inc; 3290 sub_generation_inc;
3291 local @safe::cf::_safe_eval_args = values %vars;
3206 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval); 3292 @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3207 sub_generation_inc; 3293 sub_generation_inc;
3294 } else {
3295 local @cf::_safe_eval_args = values %vars;
3296 @res = wantarray ? eval eval : scalar eval $eval;
3297 }
3208 3298
3209 if ($@) { 3299 if ($@) {
3210 warn "$@"; 3300 warn "$@";
3211 warn "while executing safe code '$code'\n"; 3301 warn "while executing safe code '$code'\n";
3212 warn "with arguments " . (join " ", %vars) . "\n"; 3302 warn "with arguments " . (join " ", %vars) . "\n";
3231=cut 3321=cut
3232 3322
3233sub register_script_function { 3323sub register_script_function {
3234 my ($fun, $cb) = @_; 3324 my ($fun, $cb) = @_;
3235 3325
3236 no strict 'refs'; 3326 $fun = "safe::$fun" if $CFG{safe_eval};
3237 *{"safe::$fun"} = $safe_hole->wrap ($cb); 3327 *$fun = $safe_hole->wrap ($cb);
3238} 3328}
3239 3329
3240=back 3330=back
3241 3331
3242=cut 3332=cut
3263 3353
3264 $facedata->{version} == 2 3354 $facedata->{version} == 2
3265 or cf::cleanup "$path: version mismatch, cannot proceed."; 3355 or cf::cleanup "$path: version mismatch, cannot proceed.";
3266 3356
3267 # patch in the exptable 3357 # patch in the exptable
3358 my $exp_table = $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]);
3268 $facedata->{resource}{"res/exp_table"} = { 3359 $facedata->{resource}{"res/exp_table"} = {
3269 type => FT_RSRC, 3360 type => FT_RSRC,
3270 data => $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]), 3361 data => $exp_table,
3362 hash => (Digest::MD5::md5 $exp_table),
3271 }; 3363 };
3272 cf::cede_to_tick; 3364 cf::cede_to_tick;
3273 3365
3274 { 3366 {
3275 my $faces = $facedata->{faceinfo}; 3367 my $faces = $facedata->{faceinfo};
3277 while (my ($face, $info) = each %$faces) { 3369 while (my ($face, $info) = each %$faces) {
3278 my $idx = (cf::face::find $face) || cf::face::alloc $face; 3370 my $idx = (cf::face::find $face) || cf::face::alloc $face;
3279 3371
3280 cf::face::set_visibility $idx, $info->{visibility}; 3372 cf::face::set_visibility $idx, $info->{visibility};
3281 cf::face::set_magicmap $idx, $info->{magicmap}; 3373 cf::face::set_magicmap $idx, $info->{magicmap};
3282 cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32}; 3374 cf::face::set_data $idx, 0, $info->{data32}, $info->{hash32};
3283 cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64}; 3375 cf::face::set_data $idx, 1, $info->{data64}, $info->{hash64};
3284 3376
3285 cf::cede_to_tick; 3377 cf::cede_to_tick;
3286 } 3378 }
3287 3379
3288 while (my ($face, $info) = each %$faces) { 3380 while (my ($face, $info) = each %$faces) {
3312 3404
3313 cf::anim::invalidate_all; # d'oh 3405 cf::anim::invalidate_all; # d'oh
3314 } 3406 }
3315 3407
3316 { 3408 {
3317 # TODO: for gcfclient pleasure, we should give resources
3318 # that gcfclient doesn't grok a >10000 face index.
3319 my $res = $facedata->{resource}; 3409 my $res = $facedata->{resource};
3320 3410
3321 while (my ($name, $info) = each %$res) { 3411 while (my ($name, $info) = each %$res) {
3322 if (defined $info->{type}) { 3412 if (defined $info->{type}) {
3323 my $idx = (cf::face::find $name) || cf::face::alloc $name; 3413 my $idx = (cf::face::find $name) || cf::face::alloc $name;
3324 my $data;
3325 3414
3326 if ($info->{type} & 1) { 3415 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3327 # prepend meta info
3328
3329 my $meta = $enc->encode ({
3330 name => $name,
3331 %{ $info->{meta} || {} },
3332 });
3333
3334 $data = pack "(w/a*)*", $meta, $info->{data};
3335 } else {
3336 $data = $info->{data};
3337 }
3338
3339 cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data;
3340 cf::face::set_type $idx, $info->{type}; 3416 cf::face::set_type $idx, $info->{type};
3341 } else { 3417 } else {
3342 $RESOURCE{$name} = $info; 3418 $RESOURCE{$name} = $info;
3343 } 3419 }
3344 3420
3487 3563
3488 LOG llevInfo, "Welcome to Deliantra, v" . VERSION; 3564 LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3489 LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team."; 3565 LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3490 LOG llevInfo, "Copyright (C) 1994 Mark Wedel."; 3566 LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3491 LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen."; 3567 LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen.";
3492
3493 cf::init_experience;
3494 cf::init_anim;
3495 cf::init_attackmess;
3496 cf::init_dynamic;
3497 3568
3498 $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority 3569 $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3499 3570
3500 # we must not ever block the main coroutine 3571 # we must not ever block the main coroutine
3501 local $Coro::idle = sub { 3572 local $Coro::idle = sub {
3507 }; 3578 };
3508 3579
3509 evthread_start IO::AIO::poll_fileno; 3580 evthread_start IO::AIO::poll_fileno;
3510 3581
3511 cf::sync_job { 3582 cf::sync_job {
3583 cf::init_experience;
3584 cf::init_anim;
3585 cf::init_attackmess;
3586 cf::init_dynamic;
3587
3588 cf::load_settings;
3589 cf::load_materials;
3590
3512 reload_resources; 3591 reload_resources;
3513 reload_config; 3592 reload_config;
3514 db_init; 3593 db_init;
3515 3594
3516 cf::load_settings;
3517 cf::load_materials;
3518 cf::init_uuid; 3595 cf::init_uuid;
3519 cf::init_signals; 3596 cf::init_signals;
3520 cf::init_commands;
3521 cf::init_skills; 3597 cf::init_skills;
3522 3598
3523 cf::init_beforeplay; 3599 cf::init_beforeplay;
3524 3600
3525 atomic; 3601 atomic;
3532 use POSIX (); 3608 use POSIX ();
3533 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD}; 3609 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3534 3610
3535 (pop @POST_INIT)->(0) while @POST_INIT; 3611 (pop @POST_INIT)->(0) while @POST_INIT;
3536 }; 3612 };
3613
3614 cf::object::thawer::errors_are_fatal 0;
3615 warn "parse errors in files are no longer fatal from this point on.\n";
3537 3616
3538 main_loop; 3617 main_loop;
3539} 3618}
3540 3619
3541############################################################################# 3620#############################################################################
3543 3622
3544# install some emergency cleanup handlers 3623# install some emergency cleanup handlers
3545BEGIN { 3624BEGIN {
3546 our %SIGWATCHER = (); 3625 our %SIGWATCHER = ();
3547 for my $signal (qw(INT HUP TERM)) { 3626 for my $signal (qw(INT HUP TERM)) {
3548 $SIGWATCHER{$signal} = EV::signal $signal, sub { 3627 $SIGWATCHER{$signal} = AE::signal $signal, sub {
3549 cf::cleanup "SIG$signal"; 3628 cf::cleanup "SIG$signal";
3550 }; 3629 };
3551 } 3630 }
3552} 3631}
3553 3632
3554sub write_runtime_sync { 3633sub write_runtime_sync {
3634 my $t0 = AE::time;
3635
3555 # first touch the runtime file to show we are still running: 3636 # first touch the runtime file to show we are still running:
3556 # the fsync below can take a very very long time. 3637 # the fsync below can take a very very long time.
3557 3638
3558 IO::AIO::aio_utime $RUNTIMEFILE, undef, undef; 3639 IO::AIO::aio_utime $RUNTIMEFILE, undef, undef;
3559 3640
3560 my $guard = cf::lock_acquire "write_runtime"; 3641 my $guard = cf::lock_acquire "write_runtime";
3561 3642
3562 my $fh = aio_open "$RUNTIMEFILE~", O_WRONLY | O_CREAT, 0644 3643 my $fh = aio_open "$RUNTIMEFILE~", O_WRONLY | O_CREAT | O_TRUNC, 0644
3563 or return; 3644 or return;
3564 3645
3565 my $value = $cf::RUNTIME + 90 + 10; 3646 my $value = $cf::RUNTIME + 90 + 10;
3566 # 10 is the runtime save interval, for a monotonic clock 3647 # 10 is the runtime save interval, for a monotonic clock
3567 # 60 allows for the watchdog to kill the server. 3648 # 60 allows for the watchdog to kill the server.
3580 or return; 3661 or return;
3581 3662
3582 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE 3663 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3583 and return; 3664 and return;
3584 3665
3585 warn "runtime file written.\n"; 3666 warn sprintf "runtime file written (%gs).\n", AE::time - $t0;
3586 3667
3587 1 3668 1
3588} 3669}
3589 3670
3590our $uuid_lock; 3671our $uuid_lock;
3716 my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH}; 3797 my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3717 for my $name (keys %$leaf_symtab) { 3798 for my $name (keys %$leaf_symtab) {
3718 _gv_clear *{"$pkg$name"}; 3799 _gv_clear *{"$pkg$name"};
3719# use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"}; 3800# use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3720 } 3801 }
3721 warn "cleared package $pkg\n";#d#
3722} 3802}
3723 3803
3724sub do_reload_perl() { 3804sub do_reload_perl() {
3725 # can/must only be called in main 3805 # can/must only be called in main
3726 if ($Coro::current != $Coro::main) { 3806 if (in_main) {
3727 warn "can only reload from main coroutine"; 3807 warn "can only reload from main coroutine";
3728 return; 3808 return;
3729 } 3809 }
3730 3810
3731 return if $RELOAD++; 3811 return if $RELOAD++;
3732 3812
3733 my $t1 = EV::time; 3813 my $t1 = AE::time;
3734 3814
3735 while ($RELOAD) { 3815 while ($RELOAD) {
3736 warn "reloading..."; 3816 warn "reloading...";
3737 3817
3738 warn "entering sync_job"; 3818 warn "entering sync_job";
3841 3921
3842 warn "reloaded"; 3922 warn "reloaded";
3843 --$RELOAD; 3923 --$RELOAD;
3844 } 3924 }
3845 3925
3846 $t1 = EV::time - $t1; 3926 $t1 = AE::time - $t1;
3847 warn "reload completed in ${t1}s\n"; 3927 warn "reload completed in ${t1}s\n";
3848}; 3928};
3849 3929
3850our $RELOAD_WATCHER; # used only during reload 3930our $RELOAD_WATCHER; # used only during reload
3851 3931
3854 # coro crashes during coro_state_free->destroy here. 3934 # coro crashes during coro_state_free->destroy here.
3855 3935
3856 $RELOAD_WATCHER ||= cf::async { 3936 $RELOAD_WATCHER ||= cf::async {
3857 Coro::AIO::aio_wait cache_extensions; 3937 Coro::AIO::aio_wait cache_extensions;
3858 3938
3859 $RELOAD_WATCHER = EV::timer $TICK * 1.5, 0, sub { 3939 $RELOAD_WATCHER = AE::timer $TICK * 1.5, 0, sub {
3860 do_reload_perl; 3940 do_reload_perl;
3861 undef $RELOAD_WATCHER; 3941 undef $RELOAD_WATCHER;
3862 }; 3942 };
3863 }; 3943 };
3864} 3944}
3881 3961
3882our @WAIT_FOR_TICK; 3962our @WAIT_FOR_TICK;
3883our @WAIT_FOR_TICK_BEGIN; 3963our @WAIT_FOR_TICK_BEGIN;
3884 3964
3885sub wait_for_tick { 3965sub wait_for_tick {
3886 return if tick_inhibit || $Coro::current == $Coro::main; 3966 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
3887 3967
3888 my $signal = new Coro::Signal; 3968 my $signal = new Coro::Signal;
3889 push @WAIT_FOR_TICK, $signal; 3969 push @WAIT_FOR_TICK, $signal;
3890 $signal->wait; 3970 $signal->wait;
3891} 3971}
3892 3972
3893sub wait_for_tick_begin { 3973sub wait_for_tick_begin {
3894 return if tick_inhibit || $Coro::current == $Coro::main; 3974 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
3895 3975
3896 my $signal = new Coro::Signal; 3976 my $signal = new Coro::Signal;
3897 push @WAIT_FOR_TICK_BEGIN, $signal; 3977 push @WAIT_FOR_TICK_BEGIN, $signal;
3898 $signal->wait; 3978 $signal->wait;
3899} 3979}
3904 unless ++$bug_warning > 10; 3984 unless ++$bug_warning > 10;
3905 return; 3985 return;
3906 } 3986 }
3907 3987
3908 cf::server_tick; # one server iteration 3988 cf::server_tick; # one server iteration
3989
3990 #for(1..3e6){} AE::now_update; $NOW=AE::now; # generate load #d#
3909 3991
3910 if ($NOW >= $NEXT_RUNTIME_WRITE) { 3992 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3911 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.; 3993 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
3912 Coro::async_pool { 3994 Coro::async_pool {
3913 $Coro::current->{desc} = "runtime saver"; 3995 $Coro::current->{desc} = "runtime saver";
3936} 4018}
3937 4019
3938{ 4020{
3939 # configure BDB 4021 # configure BDB
3940 4022
3941 BDB::min_parallel 8; 4023 BDB::min_parallel 16;
3942 BDB::max_poll_reqs $TICK * 0.1; 4024 BDB::max_poll_reqs $TICK * 0.1;
3943 $AnyEvent::BDB::WATCHER->priority (1); 4025 $AnyEvent::BDB::WATCHER->priority (1);
3944 4026
3945 unless ($DB_ENV) { 4027 unless ($DB_ENV) {
3946 $DB_ENV = BDB::db_env_create; 4028 $DB_ENV = BDB::db_env_create;

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines