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.513 by root, Mon Apr 12 05:22:38 2010 UTC vs.
Revision 1.542 by root, Wed May 5 09:05:03 2010 UTC

20# The authors can be reached via e-mail to <support@deliantra.net> 20# The authors can be reached via e-mail to <support@deliantra.net>
21# 21#
22 22
23package cf; 23package cf;
24 24
25use 5.10.0; 25use common::sense;
26use utf8;
27use strict qw(vars subs);
28 26
29use Symbol; 27use Symbol;
30use List::Util; 28use List::Util;
31use Socket; 29use Socket;
32use EV; 30use EV;
78# strictly for debugging 76# strictly for debugging
79$SIG{QUIT} = sub { Carp::cluck "SIGQUIT" }; 77$SIG{QUIT} = sub { Carp::cluck "SIGQUIT" };
80 78
81sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload 79sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
82 80
81our @ORIG_INC;
82
83our %COMMAND = (); 83our %COMMAND = ();
84our %COMMAND_TIME = (); 84our %COMMAND_TIME = ();
85 85
86our @EXTS = (); # list of extension package names 86our @EXTS = (); # list of extension package names
87our %EXTCMD = (); 87our %EXTCMD = ();
106our $RANDOMDIR = "$LOCALDIR/random"; 106our $RANDOMDIR = "$LOCALDIR/random";
107our $BDBDIR = "$LOCALDIR/db"; 107our $BDBDIR = "$LOCALDIR/db";
108our $PIDFILE = "$LOCALDIR/pid"; 108our $PIDFILE = "$LOCALDIR/pid";
109our $RUNTIMEFILE = "$LOCALDIR/runtime"; 109our $RUNTIMEFILE = "$LOCALDIR/runtime";
110 110
111our %RESOURCE; 111our %RESOURCE; # unused
112 112
113our $OUTPUT_RATE_MIN = 4000; 113our $OUTPUT_RATE_MIN = 3000;
114our $OUTPUT_RATE_MAX = 100000; 114our $OUTPUT_RATE_MAX = 1000000;
115
116our $MAX_LINKS = 32; # how many chained exits to follow
117our $VERBOSE_IO = 1;
115 118
116our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!) 119our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!)
117our $NEXT_RUNTIME_WRITE; # when should the runtime file be written 120our $NEXT_RUNTIME_WRITE; # when should the runtime file be written
118our $NEXT_TICK; 121our $NEXT_TICK;
119our $USE_FSYNC = 1; # use fsync to write maps - default on 122our $USE_FSYNC = 1; # use fsync to write maps - default on
165 168
166our $EMERGENCY_POSITION; 169our $EMERGENCY_POSITION;
167 170
168sub cf::map::normalise; 171sub cf::map::normalise;
169 172
173sub in_main() {
174 $Coro::current == $Coro::main
175}
176
170############################################################################# 177#############################################################################
171 178
172%REFLECT = (); 179%REFLECT = ();
173for (@REFLECT) { 180for (@REFLECT) {
174 my $reflect = JSON::XS::decode_json $_; 181 my $reflect = JSON::XS::decode_json $_;
245 252
246=back 253=back
247 254
248=cut 255=cut
249 256
257sub error(@) { LOG llevError, join "", @_ }
258sub warn (@) { LOG llevWarn , join "", @_ }
259sub info (@) { LOG llevInfo , join "", @_ }
260sub debug(@) { LOG llevDebug, join "", @_ }
261sub trace(@) { LOG llevTrace, join "", @_ }
262
250$Coro::State::WARNHOOK = sub { 263$Coro::State::WARNHOOK = sub {
251 my $msg = join "", @_; 264 my $msg = join "", @_;
252 265
253 $msg .= "\n" 266 $msg .= "\n"
254 unless $msg =~ /\n$/; 267 unless $msg =~ /\n$/;
255 268
256 $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge; 269 $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge;
257 270
258 LOG llevError, $msg; 271 LOG llevWarn, $msg;
259}; 272};
260 273
261$Coro::State::DIEHOOK = sub { 274$Coro::State::DIEHOOK = sub {
262 return unless $^S eq 0; # "eq", not "==" 275 return unless $^S eq 0; # "eq", not "=="
263 276
264 warn Carp::longmess $_[0]; 277 error Carp::longmess $_[0];
265 278
266 if ($Coro::current == $Coro::main) {#d# 279 if (in_main) {#d#
267 warn "DIEHOOK called in main context, Coro bug?\n";#d# 280 error "DIEHOOK called in main context, Coro bug?\n";#d#
268 return;#d# 281 return;#d#
269 }#d# 282 }#d#
270 283
271 # kill coroutine otherwise 284 # kill coroutine otherwise
272 Coro::terminate 285 Coro::terminate
399} 412}
400 413
401=item cf::periodic $interval, $cb 414=item cf::periodic $interval, $cb
402 415
403Like EV::periodic, but randomly selects a starting point so that the actions 416Like EV::periodic, but randomly selects a starting point so that the actions
404get spread over timer. 417get spread over time.
405 418
406=cut 419=cut
407 420
408sub periodic($$) { 421sub periodic($$) {
409 my ($interval, $cb) = @_; 422 my ($interval, $cb) = @_;
505=cut 518=cut
506 519
507sub sync_job(&) { 520sub sync_job(&) {
508 my ($job) = @_; 521 my ($job) = @_;
509 522
510 if ($Coro::current == $Coro::main) { 523 if (in_main) {
511 my $time = AE::time; 524 my $time = AE::time;
512 525
513 # this is the main coro, too bad, we have to block 526 # this is the main coro, too bad, we have to block
514 # till the operation succeeds, freezing the server :/ 527 # till the operation succeeds, freezing the server :/
515 528
516 LOG llevError, Carp::longmess "sync job";#d# 529 #LOG llevError, Carp::longmess "sync job";#d#
517 530
518 my $freeze_guard = freeze_mainloop; 531 my $freeze_guard = freeze_mainloop;
519 532
520 my $busy = 1; 533 my $busy = 1;
521 my @res; 534 my @res;
522 535
523 (async { 536 (async {
524 $Coro::current->desc ("sync job coro"); 537 $Coro::current->desc ("sync job coro");
525 @res = eval { $job->() }; 538 @res = eval { $job->() };
526 warn $@ if $@; 539 error $@ if $@;
527 undef $busy; 540 undef $busy;
528 })->prio (Coro::PRIO_MAX); 541 })->prio (Coro::PRIO_MAX);
529 542
530 while ($busy) { 543 while ($busy) {
531 if (Coro::nready) { 544 if (Coro::nready) {
587 reset_signals; 600 reset_signals;
588 &$cb 601 &$cb
589 }, @args; 602 }, @args;
590 603
591 wantarray ? @res : $res[-1] 604 wantarray ? @res : $res[-1]
605}
606
607sub objinfo {
608 (
609 "counter value" => cf::object::object_count,
610 "objects created" => cf::object::create_count,
611 "objects destroyed" => cf::object::destroy_count,
612 "freelist size" => cf::object::free_count,
613 "allocated objects" => cf::object::objects_size,
614 "active objects" => cf::object::actives_size,
615 )
592} 616}
593 617
594=item $coin = coin_from_name $name 618=item $coin = coin_from_name $name
595 619
596=cut 620=cut
633within each server. 657within each server.
634 658
635=cut 659=cut
636 660
637sub db_table($) { 661sub db_table($) {
662 cf::error "db_get called from main context"
663 if $Coro::current == $Coro::main;
664
638 my ($name) = @_; 665 my ($name) = @_;
639 my $db = BDB::db_create $DB_ENV; 666 my $db = BDB::db_create $DB_ENV;
640 667
641 eval { 668 eval {
642 $db->set_flags (BDB::CHKSUM); 669 $db->set_flags (BDB::CHKSUM);
652} 679}
653 680
654our $DB; 681our $DB;
655 682
656sub db_init { 683sub db_init {
657 cf::sync_job {
658 $DB ||= db_table "db"; 684 $DB ||= db_table "db";
659 };
660} 685}
661 686
662sub db_get($$) { 687sub db_get($$) {
663 my $key = "$_[0]/$_[1]"; 688 my $key = "$_[0]/$_[1]";
664 689
665 cf::sync_job { 690 cf::error "db_get called from main context"
691 if $Coro::current == $Coro::main;
692
666 BDB::db_get $DB, undef, $key, my $data; 693 BDB::db_get $DB, undef, $key, my $data;
667 694
668 $! ? () 695 $! ? ()
669 : $data 696 : $data
670 }
671} 697}
672 698
673sub db_put($$$) { 699sub db_put($$$) {
674 BDB::dbreq_pri 4; 700 BDB::dbreq_pri 4;
675 BDB::db_put $DB, undef, "$_[0]/$_[1]", $_[2], 0, sub { }; 701 BDB::db_put $DB, undef, "$_[0]/$_[1]", $_[2], 0, sub { };
731 757
732 my $t1 = Time::HiRes::time; 758 my $t1 = Time::HiRes::time;
733 my $data = $process->(\@data); 759 my $data = $process->(\@data);
734 my $t2 = Time::HiRes::time; 760 my $t2 = Time::HiRes::time;
735 761
736 warn "cache: '$id' processed in ", $t2 - $t1, "s\n"; 762 info "cache: '$id' processed in ", $t2 - $t1, "s\n";
737 763
738 db_put cache => "$id/data", $data; 764 db_put cache => "$id/data", $data;
739 db_put cache => "$id/md5" , $md5; 765 db_put cache => "$id/md5" , $md5;
740 db_put cache => "$id/meta", $meta; 766 db_put cache => "$id/meta", $meta;
741 767
751 777
752=cut 778=cut
753 779
754sub datalog($@) { 780sub datalog($@) {
755 my ($type, %kv) = @_; 781 my ($type, %kv) = @_;
756 warn "DATALOG ", JSON::XS->new->ascii->encode ({ %kv, type => $type }); 782 info "DATALOG ", JSON::XS->new->ascii->encode ({ %kv, type => $type });
757} 783}
758 784
759=back 785=back
760 786
761=cut 787=cut
956 982
957 } elsif (exists $cb_id{$type}) { 983 } elsif (exists $cb_id{$type}) {
958 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg; 984 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
959 985
960 } elsif (ref $type) { 986 } elsif (ref $type) {
961 warn "attaching objects not supported, ignoring.\n"; 987 error "attaching objects not supported, ignoring.\n";
962 988
963 } else { 989 } else {
964 shift @arg; 990 shift @arg;
965 warn "attach argument '$type' not supported, ignoring.\n"; 991 error "attach argument '$type' not supported, ignoring.\n";
966 } 992 }
967 } 993 }
968} 994}
969 995
970sub _object_attach { 996sub _object_attach {
980 _attach $registry, $klass, @attach; 1006 _attach $registry, $klass, @attach;
981 } 1007 }
982 1008
983 $obj->{$name} = \%arg; 1009 $obj->{$name} = \%arg;
984 } else { 1010 } else {
985 warn "object uses attachment '$name' which is not available, postponing.\n"; 1011 info "object uses attachment '$name' which is not available, postponing.\n";
986 } 1012 }
987 1013
988 $obj->{_attachment}{$name} = undef; 1014 $obj->{_attachment}{$name} = undef;
989} 1015}
990 1016
1049 1075
1050 for (@$callbacks) { 1076 for (@$callbacks) {
1051 eval { &{$_->[1]} }; 1077 eval { &{$_->[1]} };
1052 1078
1053 if ($@) { 1079 if ($@) {
1054 warn "$@";
1055 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n"; 1080 error "$@", "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
1056 override; 1081 override;
1057 } 1082 }
1058 1083
1059 return 1 if $override; 1084 return 1 if $override;
1060 } 1085 }
1139 for (@$attach) { 1164 for (@$attach) {
1140 my ($klass, @attach) = @$_; 1165 my ($klass, @attach) = @$_;
1141 _attach $registry, $klass, @attach; 1166 _attach $registry, $klass, @attach;
1142 } 1167 }
1143 } else { 1168 } else {
1144 warn "object uses attachment '$name' that is not available, postponing.\n"; 1169 info "object uses attachment '$name' that is not available, postponing.\n";
1145 } 1170 }
1146 } 1171 }
1147} 1172}
1148 1173
1149cf::attachable->attach ( 1174cf::attachable->attach (
1176 my ($filename, $rdata, $objs) = @_; 1201 my ($filename, $rdata, $objs) = @_;
1177 1202
1178 sync_job { 1203 sync_job {
1179 if (length $$rdata) { 1204 if (length $$rdata) {
1180 utf8::decode (my $decname = $filename); 1205 utf8::decode (my $decname = $filename);
1181 warn sprintf "saving %s (%d,%d)\n", 1206 trace sprintf "saving %s (%d,%d)\n",
1182 $decname, length $$rdata, scalar @$objs; 1207 $decname, length $$rdata, scalar @$objs
1208 if $VERBOSE_IO;
1183 1209
1184 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) { 1210 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
1185 aio_chmod $fh, SAVE_MODE; 1211 aio_chmod $fh, SAVE_MODE;
1186 aio_write $fh, 0, (length $$rdata), $$rdata, 0; 1212 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
1187 if ($cf::USE_FSYNC) { 1213 if ($cf::USE_FSYNC) {
1209 aio_rename "$filename~", $filename; 1235 aio_rename "$filename~", $filename;
1210 1236
1211 $filename =~ s%/[^/]+$%%; 1237 $filename =~ s%/[^/]+$%%;
1212 aio_pathsync $filename if $cf::USE_FSYNC; 1238 aio_pathsync $filename if $cf::USE_FSYNC;
1213 } else { 1239 } else {
1214 warn "unable to save objects: $filename~: $!\n"; 1240 error "unable to save objects: $filename~: $!\n";
1215 } 1241 }
1216 } else { 1242 } else {
1217 aio_unlink $filename; 1243 aio_unlink $filename;
1218 aio_unlink "$filename.pst"; 1244 aio_unlink "$filename.pst";
1219 } 1245 }
1243 my $st = eval { Coro::Storable::thaw $av }; 1269 my $st = eval { Coro::Storable::thaw $av };
1244 $av = $st->{objs}; 1270 $av = $st->{objs};
1245 } 1271 }
1246 1272
1247 utf8::decode (my $decname = $filename); 1273 utf8::decode (my $decname = $filename);
1248 warn sprintf "loading %s (%d,%d)\n", 1274 trace sprintf "loading %s (%d,%d)\n",
1249 $decname, length $data, scalar @{$av || []}; 1275 $decname, length $data, scalar @{$av || []}
1276 if $VERBOSE_IO;
1250 1277
1251 ($data, $av) 1278 ($data, $av)
1252} 1279}
1253 1280
1254=head2 COMMAND CALLBACKS 1281=head2 COMMAND CALLBACKS
1346 1373
1347 $pl->ext_reply ($reply, @reply) 1374 $pl->ext_reply ($reply, @reply)
1348 if $reply; 1375 if $reply;
1349 1376
1350 } else { 1377 } else {
1351 warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n"; 1378 error "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1352 } 1379 }
1353 1380
1354 cf::override; 1381 cf::override;
1355 }, 1382 },
1356); 1383);
1367 1394
1368 $grp 1395 $grp
1369} 1396}
1370 1397
1371sub load_extensions { 1398sub load_extensions {
1399 info "loading extensions...";
1400
1372 cf::sync_job { 1401 cf::sync_job {
1373 my %todo; 1402 my %todo;
1374 1403
1375 for my $path (<$LIBDIR/*.ext>) { 1404 for my $path (<$LIBDIR/*.ext>) {
1376 next unless -r $path; 1405 next unless -r $path;
1394 1423
1395 $ext{meta} = { map { (split /=/, $_, 2)[0, 1] } split /\s+/, $1 } 1424 $ext{meta} = { map { (split /=/, $_, 2)[0, 1] } split /\s+/, $1 }
1396 if $source =~ /\A#!.*?perl.*?#\s*(.*)$/m; 1425 if $source =~ /\A#!.*?perl.*?#\s*(.*)$/m;
1397 1426
1398 $ext{source} = 1427 $ext{source} =
1399 "package $pkg; use 5.10.0; use strict 'vars', 'subs'; use utf8;\n" 1428 "package $pkg; use common::sense;\n"
1400 . "#line 1 \"$path\"\n{\n" 1429 . "#line 1 \"$path\"\n{\n"
1401 . $source 1430 . $source
1402 . "\n};\n1"; 1431 . "\n};\n1";
1403 1432
1404 $todo{$base} = \%ext; 1433 $todo{$base} = \%ext;
1416 for (split /,\s*/, $v->{meta}{depends}) { 1445 for (split /,\s*/, $v->{meta}{depends}) {
1417 next ext 1446 next ext
1418 unless exists $done{$_}; 1447 unless exists $done{$_};
1419 } 1448 }
1420 1449
1421 warn "... pass $pass, loading '$k' into '$v->{pkg}'\n"; 1450 trace "... pass $pass, loading '$k' into '$v->{pkg}'\n";
1422 1451
1423 my $active = eval $v->{source}; 1452 my $active = eval $v->{source};
1424 1453
1425 if (length $@) { 1454 if (length $@) {
1426 warn "$v->{path}: $@\n"; 1455 error "$v->{path}: $@\n";
1427 1456
1428 cf::cleanup "mandatory extension '$k' failed to load, exiting." 1457 cf::cleanup "mandatory extension '$k' failed to load, exiting."
1429 if exists $v->{meta}{mandatory}; 1458 if exists $v->{meta}{mandatory};
1459
1460 warn "$v->{base}: optional extension cannot be loaded, skipping.\n";
1461 delete $todo{$k};
1430 } else { 1462 } else {
1431 $done{$k} = delete $todo{$k}; 1463 $done{$k} = delete $todo{$k};
1432 push @EXTS, $v->{pkg}; 1464 push @EXTS, $v->{pkg};
1433 $progress = 1; 1465 $progress = 1;
1434 1466
1435 warn "$v->{base}: extension inactive.\n" 1467 info "$v->{base}: extension inactive.\n"
1436 unless $active; 1468 unless $active;
1437 } 1469 }
1438 } 1470 }
1439 1471
1440 unless ($progress) { 1472 unless ($progress) {
1612 $pl->password ("*"); # this should lock out the player until we have nuked the dir 1644 $pl->password ("*"); # this should lock out the player until we have nuked the dir
1613 1645
1614 $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active; 1646 $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1615 $pl->deactivate; 1647 $pl->deactivate;
1616 my $killer = cf::arch::get "killer_quit"; $pl->killer ($killer); $killer->destroy; 1648 my $killer = cf::arch::get "killer_quit"; $pl->killer ($killer); $killer->destroy;
1617 $pl->ob->check_score;
1618 $pl->invoke (cf::EVENT_PLAYER_QUIT); 1649 $pl->invoke (cf::EVENT_PLAYER_QUIT);
1619 $pl->ns->destroy if $pl->ns; 1650 $pl->ns->destroy if $pl->ns;
1620 1651
1621 my $path = playerdir $pl; 1652 my $path = playerdir $pl;
1622 my $temp = "$path~$cf::RUNTIME~deleting~"; 1653 my $temp = "$path~$cf::RUNTIME~deleting~";
1677 \@logins 1708 \@logins
1678} 1709}
1679 1710
1680=item $player->maps 1711=item $player->maps
1681 1712
1713=item cf::player::maps $login
1714
1682Returns an arrayref of map paths that are private for this 1715Returns an arrayref of map paths that are private for this
1683player. May block. 1716player. May block.
1684 1717
1685=cut 1718=cut
1686 1719
1747 1780
1748=cut 1781=cut
1749 1782
1750sub find_by_path($) { 1783sub find_by_path($) {
1751 my ($path) = @_; 1784 my ($path) = @_;
1785
1786 $path =~ s/^~[^\/]*//; # skip ~login
1752 1787
1753 my ($match, $specificity); 1788 my ($match, $specificity);
1754 1789
1755 for my $region (list) { 1790 for my $region (list) {
1756 if ($region->{match} && $path =~ $region->{match}) { 1791 if ($region->{match} && $path =~ $region->{match}) {
1813 1848
1814sub register { 1849sub register {
1815 my (undef, $regex, $prio) = @_; 1850 my (undef, $regex, $prio) = @_;
1816 my $pkg = caller; 1851 my $pkg = caller;
1817 1852
1818 no strict;
1819 push @{"$pkg\::ISA"}, __PACKAGE__; 1853 push @{"$pkg\::ISA"}, __PACKAGE__;
1820 1854
1821 $EXT_MAP{$pkg} = [$prio, qr<$regex>]; 1855 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1822} 1856}
1823 1857
1824# also paths starting with '/' 1858# also paths starting with '/'
1825$EXT_MAP{"cf::map"} = [0, qr{^(?=/)}]; 1859$EXT_MAP{"cf::map::wrap"} = [0, qr{^(?=/)}];
1826 1860
1827sub thawer_merge { 1861sub thawer_merge {
1828 my ($self, $merge) = @_; 1862 my ($self, $merge) = @_;
1829 1863
1830 # we have to keep some variables in memory intact 1864 # we have to keep some variables in memory intact
2140 or next; 2174 or next;
2141 $neigh = find $neigh, $map 2175 $neigh = find $neigh, $map
2142 or next; 2176 or next;
2143 $neigh->load; 2177 $neigh->load;
2144 2178
2179 # now find the diagonal neighbours
2145 push @neigh, 2180 push @neigh,
2146 [$neigh->tile_path (($_ + 3) % 4), $neigh], 2181 [$neigh->tile_path (($_ + 3) % 4), $neigh],
2147 [$neigh->tile_path (($_ + 1) % 4), $neigh]; 2182 [$neigh->tile_path (($_ + 1) % 4), $neigh];
2148 } 2183 }
2149 2184
2156} 2191}
2157 2192
2158sub find_sync { 2193sub find_sync {
2159 my ($path, $origin) = @_; 2194 my ($path, $origin) = @_;
2160 2195
2161 cf::sync_job { find $path, $origin } 2196 return cf::LOG cf::llevError | cf::logBacktrace, "do_find_sync"
2197 if $Coro::current == $Coro::main;
2198
2199 find $path, $origin
2162} 2200}
2163 2201
2164sub do_load_sync { 2202sub do_load_sync {
2165 my ($map) = @_; 2203 my ($map) = @_;
2166 2204
2167 cf::LOG cf::llevDebug | cf::logBacktrace, "do_load_sync" 2205 return cf::LOG cf::llevError | cf::logBacktrace, "do_load_sync"
2168 if $Coro::current == $Coro::main; 2206 if $Coro::current == $Coro::main;
2169 2207
2170 cf::sync_job { $map->load }; 2208 $map->load;
2171} 2209}
2172 2210
2173our %MAP_PREFETCH; 2211our %MAP_PREFETCH;
2174our $MAP_PREFETCHER = undef; 2212our $MAP_PREFETCHER = undef;
2175 2213
2201 $MAP_PREFETCHER->prio (6); 2239 $MAP_PREFETCHER->prio (6);
2202 2240
2203 () 2241 ()
2204} 2242}
2205 2243
2244# common code, used by both ->save and ->swapout
2206sub save { 2245sub _save {
2207 my ($self) = @_; 2246 my ($self) = @_;
2208
2209 my $lock = cf::lock_acquire "map_data:$self->{path}";
2210 2247
2211 $self->{last_save} = $cf::RUNTIME; 2248 $self->{last_save} = $cf::RUNTIME;
2212 2249
2213 return unless $self->dirty; 2250 return unless $self->dirty;
2214 2251
2234 } else { 2271 } else {
2235 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES); 2272 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2236 } 2273 }
2237} 2274}
2238 2275
2276sub save {
2277 my ($self) = @_;
2278
2279 my $lock = cf::lock_acquire "map_data:$self->{path}";
2280
2281 $self->_save;
2282}
2283
2239sub swap_out { 2284sub swap_out {
2240 my ($self) = @_; 2285 my ($self) = @_;
2241 2286
2242 # save first because save cedes
2243 $self->save;
2244
2245 my $lock = cf::lock_acquire "map_data:$self->{path}"; 2287 my $lock = cf::lock_acquire "map_data:$self->{path}";
2246 2288
2247 return if $self->players;
2248 return if $self->in_memory != cf::MAP_ACTIVE; 2289 return if $self->in_memory != cf::MAP_ACTIVE;
2249 return if $self->{deny_save}; 2290 return if $self->{deny_save};
2291 return if $self->players;
2250 2292
2293 # first deactivate the map and "unlink" it from the core
2294 $self->deactivate;
2295 $_->clear_links_to ($self) for values %cf::MAP;
2251 $self->in_memory (cf::MAP_SWAPPED); 2296 $self->in_memory (cf::MAP_SWAPPED);
2297
2298 # then atomically save
2299 $self->_save;
2300
2301 # then free the map
2302 $self->clear;
2303}
2304
2305sub reset_at {
2306 my ($self) = @_;
2307
2308 # TODO: safety, remove and allow resettable per-player maps
2309 return 1e99 if $self->{deny_reset};
2310
2311 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2312 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2313
2314 $time + $to
2315}
2316
2317sub should_reset {
2318 my ($self) = @_;
2319
2320 $self->reset_at <= $cf::RUNTIME
2321}
2322
2323sub reset {
2324 my ($self) = @_;
2325
2326 my $lock = cf::lock_acquire "map_data:$self->{path}";
2327
2328 return if $self->players;
2329
2330 cf::trace "resetting map ", $self->path, "\n";
2331
2332 $self->in_memory (cf::MAP_SWAPPED);
2333
2334 # need to save uniques path
2335 unless ($self->{deny_save}) {
2336 my $uniq = $self->uniq_path; utf8::encode $uniq;
2337
2338 $self->_save_objects ($uniq, cf::IO_UNIQUES)
2339 if $uniq;
2340 }
2341
2342 delete $cf::MAP{$self->path};
2252 2343
2253 $self->deactivate; 2344 $self->deactivate;
2254 $_->clear_links_to ($self) for values %cf::MAP; 2345 $_->clear_links_to ($self) for values %cf::MAP;
2255 $self->clear; 2346 $self->clear;
2256}
2257
2258sub reset_at {
2259 my ($self) = @_;
2260
2261 # TODO: safety, remove and allow resettable per-player maps
2262 return 1e99 if $self->{deny_reset};
2263
2264 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2265 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2266
2267 $time + $to
2268}
2269
2270sub should_reset {
2271 my ($self) = @_;
2272
2273 $self->reset_at <= $cf::RUNTIME
2274}
2275
2276sub reset {
2277 my ($self) = @_;
2278
2279 my $lock = cf::lock_acquire "map_data:$self->{path}";
2280
2281 return if $self->players;
2282
2283 warn "resetting map ", $self->path, "\n";
2284
2285 $self->in_memory (cf::MAP_SWAPPED);
2286
2287 # need to save uniques path
2288 unless ($self->{deny_save}) {
2289 my $uniq = $self->uniq_path; utf8::encode $uniq;
2290
2291 $self->_save_objects ($uniq, cf::IO_UNIQUES)
2292 if $uniq;
2293 }
2294
2295 delete $cf::MAP{$self->path};
2296
2297 $self->deactivate;
2298 $_->clear_links_to ($self) for values %cf::MAP;
2299 $self->clear;
2300 2347
2301 $self->unlink_save; 2348 $self->unlink_save;
2302 $self->destroy; 2349 $self->destroy;
2303} 2350}
2304 2351
2312 2359
2313 delete $cf::MAP{$self->path}; 2360 delete $cf::MAP{$self->path};
2314 2361
2315 $self->unlink_save; 2362 $self->unlink_save;
2316 2363
2317 bless $self, "cf::map"; 2364 bless $self, "cf::map::wrap";
2318 delete $self->{deny_reset}; 2365 delete $self->{deny_reset};
2319 $self->{deny_save} = 1; 2366 $self->{deny_save} = 1;
2320 $self->reset_timeout (1); 2367 $self->reset_timeout (1);
2321 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++)); 2368 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2322 2369
2552 2599
2553Freezes the player and moves him/her to a special map (C<{link}>). 2600Freezes the player and moves him/her to a special map (C<{link}>).
2554 2601
2555The player should be reasonably safe there for short amounts of time (e.g. 2602The player should be reasonably safe there for short amounts of time (e.g.
2556for loading a map). You I<MUST> call C<leave_link> as soon as possible, 2603for loading a map). You I<MUST> call C<leave_link> as soon as possible,
2557though, as the palyer cannot control the character while it is on the link 2604though, as the player cannot control the character while it is on the link
2558map. 2605map.
2559 2606
2560Will never block. 2607Will never block.
2561 2608
2562=item $player_object->leave_link ($map, $x, $y) 2609=item $player_object->leave_link ($map, $x, $y)
2583sub cf::object::player::enter_link { 2630sub cf::object::player::enter_link {
2584 my ($self) = @_; 2631 my ($self) = @_;
2585 2632
2586 $self->deactivate_recursive; 2633 $self->deactivate_recursive;
2587 2634
2635 ++$self->{_link_recursion};
2636
2588 return if UNIVERSAL::isa $self->map, "ext::map_link"; 2637 return if UNIVERSAL::isa $self->map, "ext::map_link";
2589 2638
2590 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y] 2639 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2591 if $self->map && $self->map->{path} ne "{link}"; 2640 if $self->map && $self->map->{path} ne "{link}";
2592 2641
2593 $self->enter_map ($LINK_MAP || link_map, 10, 10); 2642 $self->enter_map ($LINK_MAP || link_map, 3, 3);
2594} 2643}
2595 2644
2596sub cf::object::player::leave_link { 2645sub cf::object::player::leave_link {
2597 my ($self, $map, $x, $y) = @_; 2646 my ($self, $map, $x, $y) = @_;
2598 2647
2623 $map->load_neighbours; 2672 $map->load_neighbours;
2624 2673
2625 return unless $self->contr->active; 2674 return unless $self->contr->active;
2626 2675
2627 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext 2676 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2628 $self->enter_map ($map, $x, $y); 2677 if ($self->enter_map ($map, $x, $y)) {
2629 2678 # entering was successful
2679 delete $self->{_link_recursion};
2630 # only activate afterwards, to support waiting in hooks 2680 # only activate afterwards, to support waiting in hooks
2631 $self->activate_recursive; 2681 $self->activate_recursive;
2632} 2682 }
2633 2683
2684}
2685
2634=item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]]) 2686=item $player_object->goto ($path, $x, $y[, $check->($map, $x, $y, $player)[, $done->($player)]])
2635 2687
2636Moves the player to the given map-path and coordinates by first freezing 2688Moves the player to the given map-path and coordinates by first freezing
2637her, loading and preparing them map, calling the provided $check callback 2689her, loading and preparing them map, calling the provided $check callback
2638that has to return the map if sucecssful, and then unfreezes the player on 2690that has to return the map if sucecssful, and then unfreezes the player on
2639the new (success) or old (failed) map position. In either case, $done will 2691the new (success) or old (failed) map position. In either case, $done will
2646 2698
2647our $GOTOGEN; 2699our $GOTOGEN;
2648 2700
2649sub cf::object::player::goto { 2701sub cf::object::player::goto {
2650 my ($self, $path, $x, $y, $check, $done) = @_; 2702 my ($self, $path, $x, $y, $check, $done) = @_;
2703
2704 if ($self->{_link_recursion} >= $MAX_LINKS) {
2705 error "FATAL: link recursion exceeded, ", $self->name, " goto $path $x $y, redirecting.";
2706 $self->failmsg ("Something went wrong inside the server - please contact an administrator!");
2707 ($path, $x, $y) = @$EMERGENCY_POSITION;
2708 }
2651 2709
2652 # do generation counting so two concurrent goto's will be executed in-order 2710 # do generation counting so two concurrent goto's will be executed in-order
2653 my $gen = $self->{_goto_generation} = ++$GOTOGEN; 2711 my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2654 2712
2655 $self->enter_link; 2713 $self->enter_link;
2679 my $map = eval { 2737 my $map = eval {
2680 my $map = defined $path ? cf::map::find $path : undef; 2738 my $map = defined $path ? cf::map::find $path : undef;
2681 2739
2682 if ($map) { 2740 if ($map) {
2683 $map = $map->customise_for ($self); 2741 $map = $map->customise_for ($self);
2684 $map = $check->($map) if $check && $map; 2742 $map = $check->($map, $x, $y, $self) if $check && $map;
2685 } else { 2743 } else {
2686 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED); 2744 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2687 } 2745 }
2688 2746
2689 $map 2747 $map
2697 if ($gen == $self->{_goto_generation}) { 2755 if ($gen == $self->{_goto_generation}) {
2698 delete $self->{_goto_generation}; 2756 delete $self->{_goto_generation};
2699 $self->leave_link ($map, $x, $y); 2757 $self->leave_link ($map, $x, $y);
2700 } 2758 }
2701 2759
2702 $done->() if $done; 2760 $done->($self) if $done;
2703 })->prio (1); 2761 })->prio (1);
2704} 2762}
2705 2763
2706=item $player_object->enter_exit ($exit_object) 2764=item $player_object->enter_exit ($exit_object)
2707 2765
2800 $self->message ("Something went wrong deep within the deliantra server. " 2858 $self->message ("Something went wrong deep within the deliantra server. "
2801 . "I'll try to bring you back to the map you were before. " 2859 . "I'll try to bring you back to the map you were before. "
2802 . "Please report this to the dungeon master!", 2860 . "Please report this to the dungeon master!",
2803 cf::NDI_UNIQUE | cf::NDI_RED); 2861 cf::NDI_UNIQUE | cf::NDI_RED);
2804 2862
2805 warn "ERROR in enter_exit: $@"; 2863 error "ERROR in enter_exit: $@";
2806 $self->leave_link; 2864 $self->leave_link;
2807 } 2865 }
2808 })->prio (1); 2866 })->prio (1);
2809} 2867}
2810 2868
3123 3181
3124 $ns->ext_reply ($reply, @reply) 3182 $ns->ext_reply ($reply, @reply)
3125 if $reply; 3183 if $reply;
3126 3184
3127 } else { 3185 } else {
3128 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n"; 3186 error "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
3129 } 3187 }
3130 3188
3131 cf::override; 3189 cf::override;
3132 }, 3190 },
3133); 3191);
3214 decrease split destroy change_exp value msg lore send_msg)], 3272 decrease split destroy change_exp value msg lore send_msg)],
3215 ["cf::object::player" => qw(player)], 3273 ["cf::object::player" => qw(player)],
3216 ["cf::player" => qw(peaceful send_msg)], 3274 ["cf::player" => qw(peaceful send_msg)],
3217 ["cf::map" => qw(trigger)], 3275 ["cf::map" => qw(trigger)],
3218) { 3276) {
3219 no strict 'refs';
3220 my ($pkg, @funs) = @$_; 3277 my ($pkg, @funs) = @$_;
3221 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"}) 3278 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3222 for @funs; 3279 for @funs;
3223} 3280}
3224 3281
3263 local @cf::_safe_eval_args = values %vars; 3320 local @cf::_safe_eval_args = values %vars;
3264 @res = wantarray ? eval eval : scalar eval $eval; 3321 @res = wantarray ? eval eval : scalar eval $eval;
3265 } 3322 }
3266 3323
3267 if ($@) { 3324 if ($@) {
3268 warn "$@"; 3325 warn "$@",
3269 warn "while executing safe code '$code'\n"; 3326 "while executing safe code '$code'\n",
3270 warn "with arguments " . (join " ", %vars) . "\n"; 3327 "with arguments " . (join " ", %vars) . "\n";
3271 } 3328 }
3272 3329
3273 wantarray ? @res : $res[0] 3330 wantarray ? @res : $res[0]
3274} 3331}
3275 3332
3300=cut 3357=cut
3301 3358
3302############################################################################# 3359#############################################################################
3303# the server's init and main functions 3360# the server's init and main functions
3304 3361
3362# async inc loader. yay.
3363sub inc_loader {
3364 my $mod = $_[1];
3365
3366 if (in_main && !tick_inhibit) {
3367 Carp::cluck "ERROR: attempted synchronous perl module load ($mod)";
3368 } else {
3369 debug "loading perl module $mod\n";
3370 }
3371
3372 # 1. find real file
3373 for my $dir (@ORIG_INC) {
3374 ref $dir and next;
3375 0 <= Coro::AIO::aio_load "$dir/$mod", my $data
3376 or next;
3377
3378 $data = "#line 1 $dir/$mod\n$data";
3379
3380 open my $fh, "<", \$data or die;
3381
3382 return $fh;
3383 }
3384
3385 ()
3386}
3387
3388sub init_inc {
3389 # save original @INC
3390 @ORIG_INC = ($LIBDIR, @INC) unless @ORIG_INC;
3391
3392 # make sure we can do scalar-opens
3393 open my $dummy, "<", \my $dummy2;
3394
3395 # execute some stuff so perl load's some of the core modules
3396 /Ü/ =~ /ü/i;
3397 eval { &Storable::nstore_fd };
3398
3399 @INC = (\&inc_loader, @ORIG_INC); # @ORIG_INC is needed for DynaLoader, AutoLoad etc.
3400
3401 debug "module loading will be asynchronous from this point on.";
3402}
3403
3305sub load_facedata($) { 3404sub load_facedata($) {
3306 my ($path) = @_; 3405 my ($path) = @_;
3307 3406
3308 # HACK to clear player env face cache, we need some signal framework 3407 # HACK to clear player env face cache, we need some signal framework
3309 # for this (global event?) 3408 # for this (global event?)
3310 %ext::player_env::MUSIC_FACE_CACHE = (); 3409 %ext::player_env::MUSIC_FACE_CACHE = ();
3311 3410
3312 my $enc = JSON::XS->new->utf8->canonical->relaxed; 3411 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3313 3412
3314 warn "loading facedata from $path\n"; 3413 trace "loading facedata from $path\n";
3315 3414
3316 my $facedata; 3415 my $facedata;
3317 0 < aio_load $path, $facedata 3416 0 < aio_load $path, $facedata
3318 or die "$path: $!"; 3417 or die "$path: $!";
3319 3418
3353 3452
3354 if (my $smooth = cf::face::find $info->{smooth}) { 3453 if (my $smooth = cf::face::find $info->{smooth}) {
3355 cf::face::set_smooth $idx, $smooth; 3454 cf::face::set_smooth $idx, $smooth;
3356 cf::face::set_smoothlevel $idx, $info->{smoothlevel}; 3455 cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3357 } else { 3456 } else {
3358 warn "smooth face '$info->{smooth}' not found for face '$face'"; 3457 error "smooth face '$info->{smooth}' not found for face '$face'";
3359 } 3458 }
3360 3459
3361 cf::cede_to_tick; 3460 cf::cede_to_tick;
3362 } 3461 }
3363 } 3462 }
3381 my $idx = (cf::face::find $name) || cf::face::alloc $name; 3480 my $idx = (cf::face::find $name) || cf::face::alloc $name;
3382 3481
3383 cf::face::set_data $idx, 0, $info->{data}, $info->{hash}; 3482 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3384 cf::face::set_type $idx, $info->{type}; 3483 cf::face::set_type $idx, $info->{type};
3385 } else { 3484 } else {
3386 $RESOURCE{$name} = $info; 3485 $RESOURCE{$name} = $info; # unused
3387 } 3486 }
3388 3487
3389 cf::cede_to_tick; 3488 cf::cede_to_tick;
3390 } 3489 }
3391 } 3490 }
3392 3491
3393 cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE); 3492 cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3394 3493
3395 1 3494 1
3396} 3495}
3397
3398cf::global->attach (on_resource_update => sub {
3399 if (my $soundconf = $RESOURCE{"res/sound.conf"}) {
3400 $soundconf = JSON::XS->new->utf8->relaxed->decode ($soundconf->{data});
3401
3402 for (0 .. SOUND_CAST_SPELL_0 - 1) {
3403 my $sound = $soundconf->{compat}[$_]
3404 or next;
3405
3406 my $face = cf::face::find "sound/$sound->[1]";
3407 cf::sound::set $sound->[0] => $face;
3408 cf::sound::old_sound_index $_, $face; # gcfclient-compat
3409 }
3410
3411 while (my ($k, $v) = each %{$soundconf->{event}}) {
3412 my $face = cf::face::find "sound/$v";
3413 cf::sound::set $k => $face;
3414 }
3415 }
3416});
3417 3496
3418register_exticmd fx_want => sub { 3497register_exticmd fx_want => sub {
3419 my ($ns, $want) = @_; 3498 my ($ns, $want) = @_;
3420 3499
3421 while (my ($k, $v) = each %$want) { 3500 while (my ($k, $v) = each %$want) {
3460sub reload_treasures { 3539sub reload_treasures {
3461 load_resource_file "$DATADIR/treasures" 3540 load_resource_file "$DATADIR/treasures"
3462 or die "unable to load treasurelists\n"; 3541 or die "unable to load treasurelists\n";
3463} 3542}
3464 3543
3544sub reload_sound {
3545 trace "loading sound config from $DATADIR/sound\n";
3546
3547 0 < Coro::AIO::aio_load "$DATADIR/sound", my $data
3548 or die "$DATADIR/sound $!";
3549
3550 my $soundconf = JSON::XS->new->utf8->relaxed->decode ($data);
3551
3552 for (0 .. SOUND_CAST_SPELL_0 - 1) {
3553 my $sound = $soundconf->{compat}[$_]
3554 or next;
3555
3556 my $face = cf::face::find "sound/$sound->[1]";
3557 cf::sound::set $sound->[0] => $face;
3558 cf::sound::old_sound_index $_, $face; # gcfclient-compat
3559 }
3560
3561 while (my ($k, $v) = each %{$soundconf->{event}}) {
3562 my $face = cf::face::find "sound/$v";
3563 cf::sound::set $k => $face;
3564 }
3565}
3566
3465sub reload_resources { 3567sub reload_resources {
3466 warn "reloading resource files...\n"; 3568 trace "reloading resource files...\n";
3467 3569
3468 reload_facedata; 3570 reload_facedata;
3571 reload_sound;
3469 reload_archetypes; 3572 reload_archetypes;
3470 reload_regions; 3573 reload_regions;
3471 reload_treasures; 3574 reload_treasures;
3472 3575
3473 warn "finished reloading resource files\n"; 3576 trace "finished reloading resource files\n";
3474} 3577}
3475 3578
3476sub reload_config { 3579sub reload_config {
3477 warn "reloading config file...\n"; 3580 trace "reloading config file...\n";
3478 3581
3479 open my $fh, "<:utf8", "$CONFDIR/config" 3582 open my $fh, "<:utf8", "$CONFDIR/config"
3480 or return; 3583 or return;
3481 3584
3482 local $/; 3585 local $/;
3483 *CFG = YAML::XS::Load scalar <$fh>; 3586 *CFG = YAML::XS::Load scalar <$fh>;
3484 3587
3485 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37]; 3588 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_104_115", 49, 38];
3486 3589
3487 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset}; 3590 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3488 $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset}; 3591 $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3489 3592
3490 if (exists $CFG{mlockall}) { 3593 if (exists $CFG{mlockall}) {
3493 and die "WARNING: m(un)lockall failed: $!\n"; 3596 and die "WARNING: m(un)lockall failed: $!\n";
3494 }; 3597 };
3495 warn $@ if $@; 3598 warn $@ if $@;
3496 } 3599 }
3497 3600
3498 warn "finished reloading resource files\n"; 3601 trace "finished reloading resource files\n";
3499} 3602}
3500 3603
3501sub pidfile() { 3604sub pidfile() {
3502 sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT 3605 sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3503 or die "$PIDFILE: $!"; 3606 or die "$PIDFILE: $!";
3516 seek $fh, 0, 0; 3619 seek $fh, 0, 0;
3517 print $fh $$; 3620 print $fh $$;
3518} 3621}
3519 3622
3520sub main_loop { 3623sub main_loop {
3521 warn "EV::loop starting\n"; 3624 trace "EV::loop starting\n";
3522 if (1) { 3625 if (1) {
3523 EV::loop; 3626 EV::loop;
3524 } 3627 }
3525 warn "EV::loop returned\n"; 3628 trace "EV::loop returned\n";
3526 goto &main_loop unless $REALLY_UNLOOP; 3629 goto &main_loop unless $REALLY_UNLOOP;
3527} 3630}
3528 3631
3529sub main { 3632sub main {
3530 cf::init_globals; # initialise logging 3633 cf::init_globals; # initialise logging
3531 3634
3532 LOG llevInfo, "Welcome to Deliantra, v" . VERSION; 3635 LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3533 LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team."; 3636 LOG llevInfo, "Copyright (C) 2005-2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3534 LOG llevInfo, "Copyright (C) 1994 Mark Wedel."; 3637 LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3535 LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen."; 3638 LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen.";
3536
3537 cf::init_experience;
3538 cf::init_anim;
3539 cf::init_attackmess;
3540 cf::init_dynamic;
3541 3639
3542 $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority 3640 $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3543 3641
3544 # we must not ever block the main coroutine 3642 # we must not ever block the main coroutine
3545 local $Coro::idle = sub { 3643 local $Coro::idle = sub {
3551 }; 3649 };
3552 3650
3553 evthread_start IO::AIO::poll_fileno; 3651 evthread_start IO::AIO::poll_fileno;
3554 3652
3555 cf::sync_job { 3653 cf::sync_job {
3654 init_inc;
3655
3656 cf::init_experience;
3657 cf::init_anim;
3658 cf::init_attackmess;
3659 cf::init_dynamic;
3660
3556 cf::load_settings; 3661 cf::load_settings;
3557 cf::load_materials; 3662 cf::load_materials;
3558 3663
3559 reload_resources; 3664 reload_resources;
3560 reload_config; 3665 reload_config;
3577 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD}; 3682 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3578 3683
3579 (pop @POST_INIT)->(0) while @POST_INIT; 3684 (pop @POST_INIT)->(0) while @POST_INIT;
3580 }; 3685 };
3581 3686
3687 cf::object::thawer::errors_are_fatal 0;
3688 info "parse errors in files are no longer fatal from this point on.\n";
3689
3690 my $free_main; $free_main = EV::idle sub {
3691 undef $free_main;
3692 undef &main; # free gobs of memory :)
3693 };
3694
3582 main_loop; 3695 goto &main_loop;
3583} 3696}
3584 3697
3585############################################################################# 3698#############################################################################
3586# initialisation and cleanup 3699# initialisation and cleanup
3587 3700
3626 or return; 3739 or return;
3627 3740
3628 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE 3741 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3629 and return; 3742 and return;
3630 3743
3631 warn sprintf "runtime file written (%gs).\n", AE::time - $t0; 3744 trace sprintf "runtime file written (%gs).\n", AE::time - $t0;
3632 3745
3633 1 3746 1
3634} 3747}
3635 3748
3636our $uuid_lock; 3749our $uuid_lock;
3648 or return; 3761 or return;
3649 3762
3650 my $value = uuid_seq uuid_cur; 3763 my $value = uuid_seq uuid_cur;
3651 3764
3652 unless ($value) { 3765 unless ($value) {
3653 warn "cowardly refusing to write zero uuid value!\n"; 3766 info "cowardly refusing to write zero uuid value!\n";
3654 return; 3767 return;
3655 } 3768 }
3656 3769
3657 my $value = uuid_str $value + $uuid_skip; 3770 my $value = uuid_str $value + $uuid_skip;
3658 $uuid_skip = 0; 3771 $uuid_skip = 0;
3668 or return; 3781 or return;
3669 3782
3670 aio_rename "$uuid~", $uuid 3783 aio_rename "$uuid~", $uuid
3671 and return; 3784 and return;
3672 3785
3673 warn "uuid file written ($value).\n"; 3786 trace "uuid file written ($value).\n";
3674 3787
3675 1 3788 1
3676 3789
3677} 3790}
3678 3791
3684} 3797}
3685 3798
3686sub emergency_save() { 3799sub emergency_save() {
3687 my $freeze_guard = cf::freeze_mainloop; 3800 my $freeze_guard = cf::freeze_mainloop;
3688 3801
3689 warn "emergency_perl_save: enter\n"; 3802 info "emergency_perl_save: enter\n";
3803
3804 # this is a trade-off: we want to be very quick here, so
3805 # save all maps without fsync, and later call a global sync
3806 # (which in turn might be very very slow)
3807 local $USE_FSYNC = 0;
3690 3808
3691 cf::sync_job { 3809 cf::sync_job {
3692 # this is a trade-off: we want to be very quick here, so 3810 cf::write_runtime_sync; # external watchdog should not bark
3693 # save all maps without fsync, and later call a global sync
3694 # (which in turn might be very very slow)
3695 local $USE_FSYNC = 0;
3696 3811
3697 # use a peculiar iteration method to avoid tripping on perl 3812 # use a peculiar iteration method to avoid tripping on perl
3698 # refcount bugs in for. also avoids problems with players 3813 # refcount bugs in for. also avoids problems with players
3699 # and maps saved/destroyed asynchronously. 3814 # and maps saved/destroyed asynchronously.
3700 warn "emergency_perl_save: begin player save\n"; 3815 info "emergency_perl_save: begin player save\n";
3701 for my $login (keys %cf::PLAYER) { 3816 for my $login (keys %cf::PLAYER) {
3702 my $pl = $cf::PLAYER{$login} or next; 3817 my $pl = $cf::PLAYER{$login} or next;
3703 $pl->valid or next; 3818 $pl->valid or next;
3704 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt 3819 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3705 $pl->save; 3820 $pl->save;
3706 } 3821 }
3707 warn "emergency_perl_save: end player save\n"; 3822 info "emergency_perl_save: end player save\n";
3708 3823
3824 cf::write_runtime_sync; # external watchdog should not bark
3825
3709 warn "emergency_perl_save: begin map save\n"; 3826 info "emergency_perl_save: begin map save\n";
3710 for my $path (keys %cf::MAP) { 3827 for my $path (keys %cf::MAP) {
3711 my $map = $cf::MAP{$path} or next; 3828 my $map = $cf::MAP{$path} or next;
3712 $map->valid or next; 3829 $map->valid or next;
3713 $map->save; 3830 $map->save;
3714 } 3831 }
3715 warn "emergency_perl_save: end map save\n"; 3832 info "emergency_perl_save: end map save\n";
3716 3833
3834 cf::write_runtime_sync; # external watchdog should not bark
3835
3717 warn "emergency_perl_save: begin database checkpoint\n"; 3836 info "emergency_perl_save: begin database checkpoint\n";
3718 BDB::db_env_txn_checkpoint $DB_ENV; 3837 BDB::db_env_txn_checkpoint $DB_ENV;
3719 warn "emergency_perl_save: end database checkpoint\n"; 3838 info "emergency_perl_save: end database checkpoint\n";
3720 3839
3721 warn "emergency_perl_save: begin write uuid\n"; 3840 info "emergency_perl_save: begin write uuid\n";
3722 write_uuid_sync 1; 3841 write_uuid_sync 1;
3723 warn "emergency_perl_save: end write uuid\n"; 3842 info "emergency_perl_save: end write uuid\n";
3843
3844 cf::write_runtime_sync; # external watchdog should not bark
3845
3846 trace "emergency_perl_save: syncing database to disk";
3847 BDB::db_env_txn_checkpoint $DB_ENV;
3848
3849 info "emergency_perl_save: starting sync\n";
3850 IO::AIO::aio_sync sub {
3851 info "emergency_perl_save: finished sync\n";
3852 };
3853
3854 cf::write_runtime_sync; # external watchdog should not bark
3855
3856 trace "emergency_perl_save: flushing outstanding aio requests";
3857 while (IO::AIO::nreqs || BDB::nreqs) {
3858 Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3859 }
3860
3861 cf::write_runtime_sync; # external watchdog should not bark
3724 }; 3862 };
3725 3863
3726 warn "emergency_perl_save: starting sync()\n";
3727 IO::AIO::aio_sync sub {
3728 warn "emergency_perl_save: finished sync()\n";
3729 };
3730
3731 warn "emergency_perl_save: leave\n"; 3864 info "emergency_perl_save: leave\n";
3732} 3865}
3733 3866
3734sub post_cleanup { 3867sub post_cleanup {
3735 my ($make_core) = @_; 3868 my ($make_core) = @_;
3736 3869
3870 IO::AIO::flush;
3871
3737 warn Carp::longmess "post_cleanup backtrace" 3872 error Carp::longmess "post_cleanup backtrace"
3738 if $make_core; 3873 if $make_core;
3739 3874
3740 my $fh = pidfile; 3875 my $fh = pidfile;
3741 unlink $PIDFILE if <$fh> == $$; 3876 unlink $PIDFILE if <$fh> == $$;
3742} 3877}
3762 my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH}; 3897 my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3763 for my $name (keys %$leaf_symtab) { 3898 for my $name (keys %$leaf_symtab) {
3764 _gv_clear *{"$pkg$name"}; 3899 _gv_clear *{"$pkg$name"};
3765# use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"}; 3900# use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3766 } 3901 }
3767 warn "cleared package $pkg\n";#d#
3768} 3902}
3769 3903
3770sub do_reload_perl() { 3904sub do_reload_perl() {
3771 # can/must only be called in main 3905 # can/must only be called in main
3772 if ($Coro::current != $Coro::main) { 3906 if (in_main) {
3773 warn "can only reload from main coroutine"; 3907 error "can only reload from main coroutine";
3774 return; 3908 return;
3775 } 3909 }
3776 3910
3777 return if $RELOAD++; 3911 return if $RELOAD++;
3778 3912
3779 my $t1 = AE::time; 3913 my $t1 = AE::time;
3780 3914
3781 while ($RELOAD) { 3915 while ($RELOAD) {
3782 warn "reloading..."; 3916 info "reloading...";
3783 3917
3784 warn "entering sync_job"; 3918 trace "entering sync_job";
3785 3919
3786 cf::sync_job { 3920 cf::sync_job {
3787 cf::write_runtime_sync; # external watchdog should not bark
3788 cf::emergency_save; 3921 cf::emergency_save;
3789 cf::write_runtime_sync; # external watchdog should not bark
3790 3922
3791 warn "syncing database to disk";
3792 BDB::db_env_txn_checkpoint $DB_ENV;
3793
3794 # if anything goes wrong in here, we should simply crash as we already saved
3795
3796 warn "flushing outstanding aio requests";
3797 while (IO::AIO::nreqs || BDB::nreqs) {
3798 Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3799 }
3800
3801 warn "cancelling all extension coros"; 3923 trace "cancelling all extension coros";
3802 $_->cancel for values %EXT_CORO; 3924 $_->cancel for values %EXT_CORO;
3803 %EXT_CORO = (); 3925 %EXT_CORO = ();
3804 3926
3805 warn "removing commands"; 3927 trace "removing commands";
3806 %COMMAND = (); 3928 %COMMAND = ();
3807 3929
3808 warn "removing ext/exti commands"; 3930 trace "removing ext/exti commands";
3809 %EXTCMD = (); 3931 %EXTCMD = ();
3810 %EXTICMD = (); 3932 %EXTICMD = ();
3811 3933
3812 warn "unloading/nuking all extensions"; 3934 trace "unloading/nuking all extensions";
3813 for my $pkg (@EXTS) { 3935 for my $pkg (@EXTS) {
3814 warn "... unloading $pkg"; 3936 trace "... unloading $pkg";
3815 3937
3816 if (my $cb = $pkg->can ("unload")) { 3938 if (my $cb = $pkg->can ("unload")) {
3817 eval { 3939 eval {
3818 $cb->($pkg); 3940 $cb->($pkg);
3819 1 3941 1
3820 } or warn "$pkg unloaded, but with errors: $@"; 3942 } or error "$pkg unloaded, but with errors: $@";
3821 } 3943 }
3822 3944
3823 warn "... clearing $pkg"; 3945 trace "... clearing $pkg";
3824 clear_package $pkg; 3946 clear_package $pkg;
3825 } 3947 }
3826 3948
3827 warn "unloading all perl modules loaded from $LIBDIR"; 3949 trace "unloading all perl modules loaded from $LIBDIR";
3828 while (my ($k, $v) = each %INC) { 3950 while (my ($k, $v) = each %INC) {
3829 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/; 3951 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3830 3952
3831 warn "... unloading $k"; 3953 trace "... unloading $k";
3832 delete $INC{$k}; 3954 delete $INC{$k};
3833 3955
3834 $k =~ s/\.pm$//; 3956 $k =~ s/\.pm$//;
3835 $k =~ s/\//::/g; 3957 $k =~ s/\//::/g;
3836 3958
3839 } 3961 }
3840 3962
3841 clear_package $k; 3963 clear_package $k;
3842 } 3964 }
3843 3965
3844 warn "getting rid of safe::, as good as possible"; 3966 trace "getting rid of safe::, as good as possible";
3845 clear_package "safe::$_" 3967 clear_package "safe::$_"
3846 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region); 3968 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3847 3969
3848 warn "unloading cf.pm \"a bit\""; 3970 trace "unloading cf.pm \"a bit\"";
3849 delete $INC{"cf.pm"}; 3971 delete $INC{"cf.pm"};
3850 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES; 3972 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES;
3851 3973
3852 # don't, removes xs symbols, too, 3974 # don't, removes xs symbols, too,
3853 # and global variables created in xs 3975 # and global variables created in xs
3854 #clear_package __PACKAGE__; 3976 #clear_package __PACKAGE__;
3855 3977
3856 warn "unload completed, starting to reload now"; 3978 info "unload completed, starting to reload now";
3857 3979
3858 warn "reloading cf.pm"; 3980 trace "reloading cf.pm";
3859 require cf; 3981 require cf;
3860 cf::_connect_to_perl_1; 3982 cf::_connect_to_perl_1;
3861 3983
3862 warn "loading config and database again"; 3984 trace "loading config and database again";
3863 cf::reload_config; 3985 cf::reload_config;
3864 3986
3865 warn "loading extensions"; 3987 trace "loading extensions";
3866 cf::load_extensions; 3988 cf::load_extensions;
3867 3989
3868 if ($REATTACH_ON_RELOAD) { 3990 if ($REATTACH_ON_RELOAD) {
3869 warn "reattaching attachments to objects/players"; 3991 trace "reattaching attachments to objects/players";
3870 _global_reattach; # objects, sockets 3992 _global_reattach; # objects, sockets
3871 warn "reattaching attachments to maps"; 3993 trace "reattaching attachments to maps";
3872 reattach $_ for values %MAP; 3994 reattach $_ for values %MAP;
3873 warn "reattaching attachments to players"; 3995 trace "reattaching attachments to players";
3874 reattach $_ for values %PLAYER; 3996 reattach $_ for values %PLAYER;
3875 } 3997 }
3876 3998
3877 warn "running post_init jobs"; 3999 trace "running post_init jobs";
3878 (pop @POST_INIT)->(1) while @POST_INIT; 4000 (pop @POST_INIT)->(1) while @POST_INIT;
3879 4001
3880 warn "leaving sync_job"; 4002 trace "leaving sync_job";
3881 4003
3882 1 4004 1
3883 } or do { 4005 } or do {
3884 warn $@; 4006 error $@;
3885 cf::cleanup "error while reloading, exiting."; 4007 cf::cleanup "error while reloading, exiting.";
3886 }; 4008 };
3887 4009
3888 warn "reloaded"; 4010 info "reloaded";
3889 --$RELOAD; 4011 --$RELOAD;
3890 } 4012 }
3891 4013
3892 $t1 = AE::time - $t1; 4014 $t1 = AE::time - $t1;
3893 warn "reload completed in ${t1}s\n"; 4015 info "reload completed in ${t1}s\n";
3894}; 4016};
3895 4017
3896our $RELOAD_WATCHER; # used only during reload 4018our $RELOAD_WATCHER; # used only during reload
3897 4019
3898sub reload_perl() { 4020sub reload_perl() {
3919 reload_perl; 4041 reload_perl;
3920 }; 4042 };
3921 } 4043 }
3922}; 4044};
3923 4045
3924unshift @INC, $LIBDIR; 4046#############################################################################
3925 4047
3926my $bug_warning = 0; 4048my $bug_warning = 0;
3927 4049
3928our @WAIT_FOR_TICK; 4050our @WAIT_FOR_TICK;
3929our @WAIT_FOR_TICK_BEGIN; 4051our @WAIT_FOR_TICK_BEGIN;
3958 if ($NOW >= $NEXT_RUNTIME_WRITE) { 4080 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3959 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.; 4081 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
3960 Coro::async_pool { 4082 Coro::async_pool {
3961 $Coro::current->{desc} = "runtime saver"; 4083 $Coro::current->{desc} = "runtime saver";
3962 write_runtime_sync 4084 write_runtime_sync
3963 or warn "ERROR: unable to write runtime file: $!"; 4085 or error "ERROR: unable to write runtime file: $!";
3964 }; 4086 };
3965 } 4087 }
3966 4088
3967 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) { 4089 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3968 $sig->send; 4090 $sig->send;
3976 4098
3977 if (0) { 4099 if (0) {
3978 if ($NEXT_TICK) { 4100 if ($NEXT_TICK) {
3979 my $jitter = $TICK_START - $NEXT_TICK; 4101 my $jitter = $TICK_START - $NEXT_TICK;
3980 $JITTER = $JITTER * 0.75 + $jitter * 0.25; 4102 $JITTER = $JITTER * 0.75 + $jitter * 0.25;
3981 warn "jitter $JITTER\n";#d# 4103 debug "jitter $JITTER\n";#d#
3982 } 4104 }
3983 } 4105 }
3984} 4106}
3985 4107
3986{ 4108{

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines