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.534 by root, Thu Apr 29 08:13:51 2010 UTC

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;
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}) {
1820 1855
1821 $EXT_MAP{$pkg} = [$prio, qr<$regex>]; 1856 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1822} 1857}
1823 1858
1824# also paths starting with '/' 1859# also paths starting with '/'
1825$EXT_MAP{"cf::map"} = [0, qr{^(?=/)}]; 1860$EXT_MAP{"cf::map::wrap"} = [0, qr{^(?=/)}];
1826 1861
1827sub thawer_merge { 1862sub thawer_merge {
1828 my ($self, $merge) = @_; 1863 my ($self, $merge) = @_;
1829 1864
1830 # we have to keep some variables in memory intact 1865 # we have to keep some variables in memory intact
2140 or next; 2175 or next;
2141 $neigh = find $neigh, $map 2176 $neigh = find $neigh, $map
2142 or next; 2177 or next;
2143 $neigh->load; 2178 $neigh->load;
2144 2179
2180 # now find the diagonal neighbours
2145 push @neigh, 2181 push @neigh,
2146 [$neigh->tile_path (($_ + 3) % 4), $neigh], 2182 [$neigh->tile_path (($_ + 3) % 4), $neigh],
2147 [$neigh->tile_path (($_ + 1) % 4), $neigh]; 2183 [$neigh->tile_path (($_ + 1) % 4), $neigh];
2148 } 2184 }
2149 2185
2156} 2192}
2157 2193
2158sub find_sync { 2194sub find_sync {
2159 my ($path, $origin) = @_; 2195 my ($path, $origin) = @_;
2160 2196
2161 cf::sync_job { find $path, $origin } 2197 return cf::LOG cf::llevError | cf::logBacktrace, "do_find_sync"
2198 if $Coro::current == $Coro::main;
2199
2200 find $path, $origin
2162} 2201}
2163 2202
2164sub do_load_sync { 2203sub do_load_sync {
2165 my ($map) = @_; 2204 my ($map) = @_;
2166 2205
2167 cf::LOG cf::llevDebug | cf::logBacktrace, "do_load_sync" 2206 return cf::LOG cf::llevError | cf::logBacktrace, "do_load_sync"
2168 if $Coro::current == $Coro::main; 2207 if $Coro::current == $Coro::main;
2169 2208
2170 cf::sync_job { $map->load }; 2209 $map->load;
2171} 2210}
2172 2211
2173our %MAP_PREFETCH; 2212our %MAP_PREFETCH;
2174our $MAP_PREFETCHER = undef; 2213our $MAP_PREFETCHER = undef;
2175 2214
2201 $MAP_PREFETCHER->prio (6); 2240 $MAP_PREFETCHER->prio (6);
2202 2241
2203 () 2242 ()
2204} 2243}
2205 2244
2245# common code, used by both ->save and ->swapout
2206sub save { 2246sub _save {
2207 my ($self) = @_; 2247 my ($self) = @_;
2208
2209 my $lock = cf::lock_acquire "map_data:$self->{path}";
2210 2248
2211 $self->{last_save} = $cf::RUNTIME; 2249 $self->{last_save} = $cf::RUNTIME;
2212 2250
2213 return unless $self->dirty; 2251 return unless $self->dirty;
2214 2252
2234 } else { 2272 } else {
2235 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES); 2273 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2236 } 2274 }
2237} 2275}
2238 2276
2277sub save {
2278 my ($self) = @_;
2279
2280 my $lock = cf::lock_acquire "map_data:$self->{path}";
2281
2282 $self->_save;
2283}
2284
2239sub swap_out { 2285sub swap_out {
2240 my ($self) = @_; 2286 my ($self) = @_;
2241 2287
2242 # save first because save cedes
2243 $self->save;
2244
2245 my $lock = cf::lock_acquire "map_data:$self->{path}"; 2288 my $lock = cf::lock_acquire "map_data:$self->{path}";
2246 2289
2247 return if $self->players;
2248 return if $self->in_memory != cf::MAP_ACTIVE; 2290 return if $self->in_memory != cf::MAP_ACTIVE;
2249 return if $self->{deny_save}; 2291 return if $self->{deny_save};
2292 return if $self->players;
2250 2293
2294 # first deactivate the map and "unlink" it from the core
2295 $self->deactivate;
2296 $_->clear_links_to ($self) for values %cf::MAP;
2251 $self->in_memory (cf::MAP_SWAPPED); 2297 $self->in_memory (cf::MAP_SWAPPED);
2298
2299 # then atomically save
2300 $self->_save;
2301
2302 # then free the map
2303 $self->clear;
2304}
2305
2306sub reset_at {
2307 my ($self) = @_;
2308
2309 # TODO: safety, remove and allow resettable per-player maps
2310 return 1e99 if $self->{deny_reset};
2311
2312 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2313 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2314
2315 $time + $to
2316}
2317
2318sub should_reset {
2319 my ($self) = @_;
2320
2321 $self->reset_at <= $cf::RUNTIME
2322}
2323
2324sub reset {
2325 my ($self) = @_;
2326
2327 my $lock = cf::lock_acquire "map_data:$self->{path}";
2328
2329 return if $self->players;
2330
2331 cf::trace "resetting map ", $self->path, "\n";
2332
2333 $self->in_memory (cf::MAP_SWAPPED);
2334
2335 # need to save uniques path
2336 unless ($self->{deny_save}) {
2337 my $uniq = $self->uniq_path; utf8::encode $uniq;
2338
2339 $self->_save_objects ($uniq, cf::IO_UNIQUES)
2340 if $uniq;
2341 }
2342
2343 delete $cf::MAP{$self->path};
2252 2344
2253 $self->deactivate; 2345 $self->deactivate;
2254 $_->clear_links_to ($self) for values %cf::MAP; 2346 $_->clear_links_to ($self) for values %cf::MAP;
2255 $self->clear; 2347 $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 2348
2301 $self->unlink_save; 2349 $self->unlink_save;
2302 $self->destroy; 2350 $self->destroy;
2303} 2351}
2304 2352
2312 2360
2313 delete $cf::MAP{$self->path}; 2361 delete $cf::MAP{$self->path};
2314 2362
2315 $self->unlink_save; 2363 $self->unlink_save;
2316 2364
2317 bless $self, "cf::map"; 2365 bless $self, "cf::map::wrap";
2318 delete $self->{deny_reset}; 2366 delete $self->{deny_reset};
2319 $self->{deny_save} = 1; 2367 $self->{deny_save} = 1;
2320 $self->reset_timeout (1); 2368 $self->reset_timeout (1);
2321 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++)); 2369 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2322 2370
2552 2600
2553Freezes the player and moves him/her to a special map (C<{link}>). 2601Freezes the player and moves him/her to a special map (C<{link}>).
2554 2602
2555The player should be reasonably safe there for short amounts of time (e.g. 2603The 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, 2604for 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 2605though, as the player cannot control the character while it is on the link
2558map. 2606map.
2559 2607
2560Will never block. 2608Will never block.
2561 2609
2562=item $player_object->leave_link ($map, $x, $y) 2610=item $player_object->leave_link ($map, $x, $y)
2583sub cf::object::player::enter_link { 2631sub cf::object::player::enter_link {
2584 my ($self) = @_; 2632 my ($self) = @_;
2585 2633
2586 $self->deactivate_recursive; 2634 $self->deactivate_recursive;
2587 2635
2636 ++$self->{_link_recursion};
2637
2588 return if UNIVERSAL::isa $self->map, "ext::map_link"; 2638 return if UNIVERSAL::isa $self->map, "ext::map_link";
2589 2639
2590 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y] 2640 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2591 if $self->map && $self->map->{path} ne "{link}"; 2641 if $self->map && $self->map->{path} ne "{link}";
2592 2642
2593 $self->enter_map ($LINK_MAP || link_map, 10, 10); 2643 $self->enter_map ($LINK_MAP || link_map, 3, 3);
2594} 2644}
2595 2645
2596sub cf::object::player::leave_link { 2646sub cf::object::player::leave_link {
2597 my ($self, $map, $x, $y) = @_; 2647 my ($self, $map, $x, $y) = @_;
2598 2648
2623 $map->load_neighbours; 2673 $map->load_neighbours;
2624 2674
2625 return unless $self->contr->active; 2675 return unless $self->contr->active;
2626 2676
2627 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext 2677 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2628 $self->enter_map ($map, $x, $y); 2678 if ($self->enter_map ($map, $x, $y)) {
2629 2679 # entering was successful
2680 delete $self->{_link_recursion};
2630 # only activate afterwards, to support waiting in hooks 2681 # only activate afterwards, to support waiting in hooks
2631 $self->activate_recursive; 2682 $self->activate_recursive;
2632} 2683 }
2633 2684
2685}
2686
2634=item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]]) 2687=item $player_object->goto ($path, $x, $y[, $check->($map, $x, $y, $player)[, $done->($player)]])
2635 2688
2636Moves the player to the given map-path and coordinates by first freezing 2689Moves the player to the given map-path and coordinates by first freezing
2637her, loading and preparing them map, calling the provided $check callback 2690her, loading and preparing them map, calling the provided $check callback
2638that has to return the map if sucecssful, and then unfreezes the player on 2691that 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 2692the new (success) or old (failed) map position. In either case, $done will
2646 2699
2647our $GOTOGEN; 2700our $GOTOGEN;
2648 2701
2649sub cf::object::player::goto { 2702sub cf::object::player::goto {
2650 my ($self, $path, $x, $y, $check, $done) = @_; 2703 my ($self, $path, $x, $y, $check, $done) = @_;
2704
2705 if ($self->{_link_recursion} >= $MAX_LINKS) {
2706 error "FATAL: link recursion exceeded, ", $self->name, " goto $path $x $y, redirecting.";
2707 $self->failmsg ("Something went wrong inside the server - please contact an administrator!");
2708 ($path, $x, $y) = @$EMERGENCY_POSITION;
2709 }
2651 2710
2652 # do generation counting so two concurrent goto's will be executed in-order 2711 # do generation counting so two concurrent goto's will be executed in-order
2653 my $gen = $self->{_goto_generation} = ++$GOTOGEN; 2712 my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2654 2713
2655 $self->enter_link; 2714 $self->enter_link;
2679 my $map = eval { 2738 my $map = eval {
2680 my $map = defined $path ? cf::map::find $path : undef; 2739 my $map = defined $path ? cf::map::find $path : undef;
2681 2740
2682 if ($map) { 2741 if ($map) {
2683 $map = $map->customise_for ($self); 2742 $map = $map->customise_for ($self);
2684 $map = $check->($map) if $check && $map; 2743 $map = $check->($map, $x, $y, $self) if $check && $map;
2685 } else { 2744 } else {
2686 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED); 2745 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2687 } 2746 }
2688 2747
2689 $map 2748 $map
2697 if ($gen == $self->{_goto_generation}) { 2756 if ($gen == $self->{_goto_generation}) {
2698 delete $self->{_goto_generation}; 2757 delete $self->{_goto_generation};
2699 $self->leave_link ($map, $x, $y); 2758 $self->leave_link ($map, $x, $y);
2700 } 2759 }
2701 2760
2702 $done->() if $done; 2761 $done->($self) if $done;
2703 })->prio (1); 2762 })->prio (1);
2704} 2763}
2705 2764
2706=item $player_object->enter_exit ($exit_object) 2765=item $player_object->enter_exit ($exit_object)
2707 2766
2800 $self->message ("Something went wrong deep within the deliantra server. " 2859 $self->message ("Something went wrong deep within the deliantra server. "
2801 . "I'll try to bring you back to the map you were before. " 2860 . "I'll try to bring you back to the map you were before. "
2802 . "Please report this to the dungeon master!", 2861 . "Please report this to the dungeon master!",
2803 cf::NDI_UNIQUE | cf::NDI_RED); 2862 cf::NDI_UNIQUE | cf::NDI_RED);
2804 2863
2805 warn "ERROR in enter_exit: $@"; 2864 error "ERROR in enter_exit: $@";
2806 $self->leave_link; 2865 $self->leave_link;
2807 } 2866 }
2808 })->prio (1); 2867 })->prio (1);
2809} 2868}
2810 2869
3123 3182
3124 $ns->ext_reply ($reply, @reply) 3183 $ns->ext_reply ($reply, @reply)
3125 if $reply; 3184 if $reply;
3126 3185
3127 } else { 3186 } else {
3128 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n"; 3187 error "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
3129 } 3188 }
3130 3189
3131 cf::override; 3190 cf::override;
3132 }, 3191 },
3133); 3192);
3263 local @cf::_safe_eval_args = values %vars; 3322 local @cf::_safe_eval_args = values %vars;
3264 @res = wantarray ? eval eval : scalar eval $eval; 3323 @res = wantarray ? eval eval : scalar eval $eval;
3265 } 3324 }
3266 3325
3267 if ($@) { 3326 if ($@) {
3268 warn "$@"; 3327 warn "$@",
3269 warn "while executing safe code '$code'\n"; 3328 "while executing safe code '$code'\n",
3270 warn "with arguments " . (join " ", %vars) . "\n"; 3329 "with arguments " . (join " ", %vars) . "\n";
3271 } 3330 }
3272 3331
3273 wantarray ? @res : $res[0] 3332 wantarray ? @res : $res[0]
3274} 3333}
3275 3334
3309 # for this (global event?) 3368 # for this (global event?)
3310 %ext::player_env::MUSIC_FACE_CACHE = (); 3369 %ext::player_env::MUSIC_FACE_CACHE = ();
3311 3370
3312 my $enc = JSON::XS->new->utf8->canonical->relaxed; 3371 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3313 3372
3314 warn "loading facedata from $path\n"; 3373 trace "loading facedata from $path\n";
3315 3374
3316 my $facedata; 3375 my $facedata;
3317 0 < aio_load $path, $facedata 3376 0 < aio_load $path, $facedata
3318 or die "$path: $!"; 3377 or die "$path: $!";
3319 3378
3353 3412
3354 if (my $smooth = cf::face::find $info->{smooth}) { 3413 if (my $smooth = cf::face::find $info->{smooth}) {
3355 cf::face::set_smooth $idx, $smooth; 3414 cf::face::set_smooth $idx, $smooth;
3356 cf::face::set_smoothlevel $idx, $info->{smoothlevel}; 3415 cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3357 } else { 3416 } else {
3358 warn "smooth face '$info->{smooth}' not found for face '$face'"; 3417 error "smooth face '$info->{smooth}' not found for face '$face'";
3359 } 3418 }
3360 3419
3361 cf::cede_to_tick; 3420 cf::cede_to_tick;
3362 } 3421 }
3363 } 3422 }
3381 my $idx = (cf::face::find $name) || cf::face::alloc $name; 3440 my $idx = (cf::face::find $name) || cf::face::alloc $name;
3382 3441
3383 cf::face::set_data $idx, 0, $info->{data}, $info->{hash}; 3442 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3384 cf::face::set_type $idx, $info->{type}; 3443 cf::face::set_type $idx, $info->{type};
3385 } else { 3444 } else {
3386 $RESOURCE{$name} = $info; 3445 $RESOURCE{$name} = $info; # unused
3387 } 3446 }
3388 3447
3389 cf::cede_to_tick; 3448 cf::cede_to_tick;
3390 } 3449 }
3391 } 3450 }
3392 3451
3393 cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE); 3452 cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3394 3453
3395 1 3454 1
3396} 3455}
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 3456
3418register_exticmd fx_want => sub { 3457register_exticmd fx_want => sub {
3419 my ($ns, $want) = @_; 3458 my ($ns, $want) = @_;
3420 3459
3421 while (my ($k, $v) = each %$want) { 3460 while (my ($k, $v) = each %$want) {
3460sub reload_treasures { 3499sub reload_treasures {
3461 load_resource_file "$DATADIR/treasures" 3500 load_resource_file "$DATADIR/treasures"
3462 or die "unable to load treasurelists\n"; 3501 or die "unable to load treasurelists\n";
3463} 3502}
3464 3503
3504sub reload_sound {
3505 trace "loading sound config from $DATADIR/sound\n";
3506
3507 0 < Coro::AIO::aio_load "$DATADIR/sound", my $data
3508 or die "$DATADIR/sound $!";
3509
3510 my $soundconf = JSON::XS->new->utf8->relaxed->decode ($data);
3511
3512 for (0 .. SOUND_CAST_SPELL_0 - 1) {
3513 my $sound = $soundconf->{compat}[$_]
3514 or next;
3515
3516 my $face = cf::face::find "sound/$sound->[1]";
3517 cf::sound::set $sound->[0] => $face;
3518 cf::sound::old_sound_index $_, $face; # gcfclient-compat
3519 }
3520
3521 while (my ($k, $v) = each %{$soundconf->{event}}) {
3522 my $face = cf::face::find "sound/$v";
3523 cf::sound::set $k => $face;
3524 }
3525}
3526
3465sub reload_resources { 3527sub reload_resources {
3466 warn "reloading resource files...\n"; 3528 trace "reloading resource files...\n";
3467 3529
3468 reload_facedata; 3530 reload_facedata;
3531 reload_sound;
3469 reload_archetypes; 3532 reload_archetypes;
3470 reload_regions; 3533 reload_regions;
3471 reload_treasures; 3534 reload_treasures;
3472 3535
3473 warn "finished reloading resource files\n"; 3536 trace "finished reloading resource files\n";
3474} 3537}
3475 3538
3476sub reload_config { 3539sub reload_config {
3477 warn "reloading config file...\n"; 3540 trace "reloading config file...\n";
3478 3541
3479 open my $fh, "<:utf8", "$CONFDIR/config" 3542 open my $fh, "<:utf8", "$CONFDIR/config"
3480 or return; 3543 or return;
3481 3544
3482 local $/; 3545 local $/;
3483 *CFG = YAML::XS::Load scalar <$fh>; 3546 *CFG = YAML::XS::Load scalar <$fh>;
3484 3547
3485 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37]; 3548 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_104_115", 49, 38];
3486 3549
3487 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset}; 3550 $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}; 3551 $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3489 3552
3490 if (exists $CFG{mlockall}) { 3553 if (exists $CFG{mlockall}) {
3493 and die "WARNING: m(un)lockall failed: $!\n"; 3556 and die "WARNING: m(un)lockall failed: $!\n";
3494 }; 3557 };
3495 warn $@ if $@; 3558 warn $@ if $@;
3496 } 3559 }
3497 3560
3498 warn "finished reloading resource files\n"; 3561 trace "finished reloading resource files\n";
3499} 3562}
3500 3563
3501sub pidfile() { 3564sub pidfile() {
3502 sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT 3565 sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3503 or die "$PIDFILE: $!"; 3566 or die "$PIDFILE: $!";
3516 seek $fh, 0, 0; 3579 seek $fh, 0, 0;
3517 print $fh $$; 3580 print $fh $$;
3518} 3581}
3519 3582
3520sub main_loop { 3583sub main_loop {
3521 warn "EV::loop starting\n"; 3584 trace "EV::loop starting\n";
3522 if (1) { 3585 if (1) {
3523 EV::loop; 3586 EV::loop;
3524 } 3587 }
3525 warn "EV::loop returned\n"; 3588 trace "EV::loop returned\n";
3526 goto &main_loop unless $REALLY_UNLOOP; 3589 goto &main_loop unless $REALLY_UNLOOP;
3527} 3590}
3528 3591
3529sub main { 3592sub main {
3530 cf::init_globals; # initialise logging 3593 cf::init_globals; # initialise logging
3531 3594
3532 LOG llevInfo, "Welcome to Deliantra, v" . VERSION; 3595 LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3533 LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team."; 3596 LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3534 LOG llevInfo, "Copyright (C) 1994 Mark Wedel."; 3597 LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3535 LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen."; 3598 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 3599
3542 $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority 3600 $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3543 3601
3544 # we must not ever block the main coroutine 3602 # we must not ever block the main coroutine
3545 local $Coro::idle = sub { 3603 local $Coro::idle = sub {
3551 }; 3609 };
3552 3610
3553 evthread_start IO::AIO::poll_fileno; 3611 evthread_start IO::AIO::poll_fileno;
3554 3612
3555 cf::sync_job { 3613 cf::sync_job {
3614 cf::init_experience;
3615 cf::init_anim;
3616 cf::init_attackmess;
3617 cf::init_dynamic;
3618
3556 cf::load_settings; 3619 cf::load_settings;
3557 cf::load_materials; 3620 cf::load_materials;
3558 3621
3559 reload_resources; 3622 reload_resources;
3560 reload_config; 3623 reload_config;
3576 use POSIX (); 3639 use POSIX ();
3577 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD}; 3640 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3578 3641
3579 (pop @POST_INIT)->(0) while @POST_INIT; 3642 (pop @POST_INIT)->(0) while @POST_INIT;
3580 }; 3643 };
3644
3645 cf::object::thawer::errors_are_fatal 0;
3646 info "parse errors in files are no longer fatal from this point on.\n";
3581 3647
3582 main_loop; 3648 main_loop;
3583} 3649}
3584 3650
3585############################################################################# 3651#############################################################################
3626 or return; 3692 or return;
3627 3693
3628 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE 3694 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3629 and return; 3695 and return;
3630 3696
3631 warn sprintf "runtime file written (%gs).\n", AE::time - $t0; 3697 trace sprintf "runtime file written (%gs).\n", AE::time - $t0;
3632 3698
3633 1 3699 1
3634} 3700}
3635 3701
3636our $uuid_lock; 3702our $uuid_lock;
3648 or return; 3714 or return;
3649 3715
3650 my $value = uuid_seq uuid_cur; 3716 my $value = uuid_seq uuid_cur;
3651 3717
3652 unless ($value) { 3718 unless ($value) {
3653 warn "cowardly refusing to write zero uuid value!\n"; 3719 info "cowardly refusing to write zero uuid value!\n";
3654 return; 3720 return;
3655 } 3721 }
3656 3722
3657 my $value = uuid_str $value + $uuid_skip; 3723 my $value = uuid_str $value + $uuid_skip;
3658 $uuid_skip = 0; 3724 $uuid_skip = 0;
3668 or return; 3734 or return;
3669 3735
3670 aio_rename "$uuid~", $uuid 3736 aio_rename "$uuid~", $uuid
3671 and return; 3737 and return;
3672 3738
3673 warn "uuid file written ($value).\n"; 3739 trace "uuid file written ($value).\n";
3674 3740
3675 1 3741 1
3676 3742
3677} 3743}
3678 3744
3684} 3750}
3685 3751
3686sub emergency_save() { 3752sub emergency_save() {
3687 my $freeze_guard = cf::freeze_mainloop; 3753 my $freeze_guard = cf::freeze_mainloop;
3688 3754
3689 warn "emergency_perl_save: enter\n"; 3755 info "emergency_perl_save: enter\n";
3756
3757 # this is a trade-off: we want to be very quick here, so
3758 # save all maps without fsync, and later call a global sync
3759 # (which in turn might be very very slow)
3760 local $USE_FSYNC = 0;
3690 3761
3691 cf::sync_job { 3762 cf::sync_job {
3692 # this is a trade-off: we want to be very quick here, so
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
3697 # use a peculiar iteration method to avoid tripping on perl 3763 # use a peculiar iteration method to avoid tripping on perl
3698 # refcount bugs in for. also avoids problems with players 3764 # refcount bugs in for. also avoids problems with players
3699 # and maps saved/destroyed asynchronously. 3765 # and maps saved/destroyed asynchronously.
3700 warn "emergency_perl_save: begin player save\n"; 3766 info "emergency_perl_save: begin player save\n";
3701 for my $login (keys %cf::PLAYER) { 3767 for my $login (keys %cf::PLAYER) {
3702 my $pl = $cf::PLAYER{$login} or next; 3768 my $pl = $cf::PLAYER{$login} or next;
3703 $pl->valid or next; 3769 $pl->valid or next;
3704 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt 3770 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3705 $pl->save; 3771 $pl->save;
3706 } 3772 }
3707 warn "emergency_perl_save: end player save\n"; 3773 info "emergency_perl_save: end player save\n";
3708 3774
3709 warn "emergency_perl_save: begin map save\n"; 3775 info "emergency_perl_save: begin map save\n";
3710 for my $path (keys %cf::MAP) { 3776 for my $path (keys %cf::MAP) {
3711 my $map = $cf::MAP{$path} or next; 3777 my $map = $cf::MAP{$path} or next;
3712 $map->valid or next; 3778 $map->valid or next;
3713 $map->save; 3779 $map->save;
3714 } 3780 }
3715 warn "emergency_perl_save: end map save\n"; 3781 info "emergency_perl_save: end map save\n";
3716 3782
3717 warn "emergency_perl_save: begin database checkpoint\n"; 3783 info "emergency_perl_save: begin database checkpoint\n";
3718 BDB::db_env_txn_checkpoint $DB_ENV; 3784 BDB::db_env_txn_checkpoint $DB_ENV;
3719 warn "emergency_perl_save: end database checkpoint\n"; 3785 info "emergency_perl_save: end database checkpoint\n";
3720 3786
3721 warn "emergency_perl_save: begin write uuid\n"; 3787 info "emergency_perl_save: begin write uuid\n";
3722 write_uuid_sync 1; 3788 write_uuid_sync 1;
3723 warn "emergency_perl_save: end write uuid\n"; 3789 info "emergency_perl_save: end write uuid\n";
3724 }; 3790 };
3725 3791
3726 warn "emergency_perl_save: starting sync()\n"; 3792 info "emergency_perl_save: starting sync()\n";
3727 IO::AIO::aio_sync sub { 3793 IO::AIO::aio_sync sub {
3728 warn "emergency_perl_save: finished sync()\n"; 3794 info "emergency_perl_save: finished sync()\n";
3729 }; 3795 };
3730 3796
3731 warn "emergency_perl_save: leave\n"; 3797 info "emergency_perl_save: leave\n";
3732} 3798}
3733 3799
3734sub post_cleanup { 3800sub post_cleanup {
3735 my ($make_core) = @_; 3801 my ($make_core) = @_;
3736 3802
3737 warn Carp::longmess "post_cleanup backtrace" 3803 error Carp::longmess "post_cleanup backtrace"
3738 if $make_core; 3804 if $make_core;
3739 3805
3740 my $fh = pidfile; 3806 my $fh = pidfile;
3741 unlink $PIDFILE if <$fh> == $$; 3807 unlink $PIDFILE if <$fh> == $$;
3742} 3808}
3762 my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH}; 3828 my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3763 for my $name (keys %$leaf_symtab) { 3829 for my $name (keys %$leaf_symtab) {
3764 _gv_clear *{"$pkg$name"}; 3830 _gv_clear *{"$pkg$name"};
3765# use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"}; 3831# use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3766 } 3832 }
3767 warn "cleared package $pkg\n";#d#
3768} 3833}
3769 3834
3770sub do_reload_perl() { 3835sub do_reload_perl() {
3771 # can/must only be called in main 3836 # can/must only be called in main
3772 if ($Coro::current != $Coro::main) { 3837 if (in_main) {
3773 warn "can only reload from main coroutine"; 3838 error "can only reload from main coroutine";
3774 return; 3839 return;
3775 } 3840 }
3776 3841
3777 return if $RELOAD++; 3842 return if $RELOAD++;
3778 3843
3779 my $t1 = AE::time; 3844 my $t1 = AE::time;
3780 3845
3781 while ($RELOAD) { 3846 while ($RELOAD) {
3782 warn "reloading..."; 3847 info "reloading...";
3783 3848
3784 warn "entering sync_job"; 3849 trace "entering sync_job";
3785 3850
3786 cf::sync_job { 3851 cf::sync_job {
3787 cf::write_runtime_sync; # external watchdog should not bark 3852 cf::write_runtime_sync; # external watchdog should not bark
3788 cf::emergency_save; 3853 cf::emergency_save;
3789 cf::write_runtime_sync; # external watchdog should not bark 3854 cf::write_runtime_sync; # external watchdog should not bark
3790 3855
3791 warn "syncing database to disk"; 3856 trace "syncing database to disk";
3792 BDB::db_env_txn_checkpoint $DB_ENV; 3857 BDB::db_env_txn_checkpoint $DB_ENV;
3793 3858
3794 # if anything goes wrong in here, we should simply crash as we already saved 3859 # if anything goes wrong in here, we should simply crash as we already saved
3795 3860
3796 warn "flushing outstanding aio requests"; 3861 trace "flushing outstanding aio requests";
3797 while (IO::AIO::nreqs || BDB::nreqs) { 3862 while (IO::AIO::nreqs || BDB::nreqs) {
3798 Coro::EV::timer_once 0.01; # let the sync_job do it's thing 3863 Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3799 } 3864 }
3800 3865
3801 warn "cancelling all extension coros"; 3866 trace "cancelling all extension coros";
3802 $_->cancel for values %EXT_CORO; 3867 $_->cancel for values %EXT_CORO;
3803 %EXT_CORO = (); 3868 %EXT_CORO = ();
3804 3869
3805 warn "removing commands"; 3870 trace "removing commands";
3806 %COMMAND = (); 3871 %COMMAND = ();
3807 3872
3808 warn "removing ext/exti commands"; 3873 trace "removing ext/exti commands";
3809 %EXTCMD = (); 3874 %EXTCMD = ();
3810 %EXTICMD = (); 3875 %EXTICMD = ();
3811 3876
3812 warn "unloading/nuking all extensions"; 3877 trace "unloading/nuking all extensions";
3813 for my $pkg (@EXTS) { 3878 for my $pkg (@EXTS) {
3814 warn "... unloading $pkg"; 3879 trace "... unloading $pkg";
3815 3880
3816 if (my $cb = $pkg->can ("unload")) { 3881 if (my $cb = $pkg->can ("unload")) {
3817 eval { 3882 eval {
3818 $cb->($pkg); 3883 $cb->($pkg);
3819 1 3884 1
3820 } or warn "$pkg unloaded, but with errors: $@"; 3885 } or error "$pkg unloaded, but with errors: $@";
3821 } 3886 }
3822 3887
3823 warn "... clearing $pkg"; 3888 trace "... clearing $pkg";
3824 clear_package $pkg; 3889 clear_package $pkg;
3825 } 3890 }
3826 3891
3827 warn "unloading all perl modules loaded from $LIBDIR"; 3892 trace "unloading all perl modules loaded from $LIBDIR";
3828 while (my ($k, $v) = each %INC) { 3893 while (my ($k, $v) = each %INC) {
3829 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/; 3894 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3830 3895
3831 warn "... unloading $k"; 3896 trace "... unloading $k";
3832 delete $INC{$k}; 3897 delete $INC{$k};
3833 3898
3834 $k =~ s/\.pm$//; 3899 $k =~ s/\.pm$//;
3835 $k =~ s/\//::/g; 3900 $k =~ s/\//::/g;
3836 3901
3839 } 3904 }
3840 3905
3841 clear_package $k; 3906 clear_package $k;
3842 } 3907 }
3843 3908
3844 warn "getting rid of safe::, as good as possible"; 3909 trace "getting rid of safe::, as good as possible";
3845 clear_package "safe::$_" 3910 clear_package "safe::$_"
3846 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region); 3911 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3847 3912
3848 warn "unloading cf.pm \"a bit\""; 3913 trace "unloading cf.pm \"a bit\"";
3849 delete $INC{"cf.pm"}; 3914 delete $INC{"cf.pm"};
3850 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES; 3915 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES;
3851 3916
3852 # don't, removes xs symbols, too, 3917 # don't, removes xs symbols, too,
3853 # and global variables created in xs 3918 # and global variables created in xs
3854 #clear_package __PACKAGE__; 3919 #clear_package __PACKAGE__;
3855 3920
3856 warn "unload completed, starting to reload now"; 3921 info "unload completed, starting to reload now";
3857 3922
3858 warn "reloading cf.pm"; 3923 trace "reloading cf.pm";
3859 require cf; 3924 require cf;
3860 cf::_connect_to_perl_1; 3925 cf::_connect_to_perl_1;
3861 3926
3862 warn "loading config and database again"; 3927 trace "loading config and database again";
3863 cf::reload_config; 3928 cf::reload_config;
3864 3929
3865 warn "loading extensions"; 3930 trace "loading extensions";
3866 cf::load_extensions; 3931 cf::load_extensions;
3867 3932
3868 if ($REATTACH_ON_RELOAD) { 3933 if ($REATTACH_ON_RELOAD) {
3869 warn "reattaching attachments to objects/players"; 3934 trace "reattaching attachments to objects/players";
3870 _global_reattach; # objects, sockets 3935 _global_reattach; # objects, sockets
3871 warn "reattaching attachments to maps"; 3936 trace "reattaching attachments to maps";
3872 reattach $_ for values %MAP; 3937 reattach $_ for values %MAP;
3873 warn "reattaching attachments to players"; 3938 trace "reattaching attachments to players";
3874 reattach $_ for values %PLAYER; 3939 reattach $_ for values %PLAYER;
3875 } 3940 }
3876 3941
3877 warn "running post_init jobs"; 3942 trace "running post_init jobs";
3878 (pop @POST_INIT)->(1) while @POST_INIT; 3943 (pop @POST_INIT)->(1) while @POST_INIT;
3879 3944
3880 warn "leaving sync_job"; 3945 trace "leaving sync_job";
3881 3946
3882 1 3947 1
3883 } or do { 3948 } or do {
3884 warn $@; 3949 error $@;
3885 cf::cleanup "error while reloading, exiting."; 3950 cf::cleanup "error while reloading, exiting.";
3886 }; 3951 };
3887 3952
3888 warn "reloaded"; 3953 info "reloaded";
3889 --$RELOAD; 3954 --$RELOAD;
3890 } 3955 }
3891 3956
3892 $t1 = AE::time - $t1; 3957 $t1 = AE::time - $t1;
3893 warn "reload completed in ${t1}s\n"; 3958 info "reload completed in ${t1}s\n";
3894}; 3959};
3895 3960
3896our $RELOAD_WATCHER; # used only during reload 3961our $RELOAD_WATCHER; # used only during reload
3897 3962
3898sub reload_perl() { 3963sub reload_perl() {
3958 if ($NOW >= $NEXT_RUNTIME_WRITE) { 4023 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3959 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.; 4024 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
3960 Coro::async_pool { 4025 Coro::async_pool {
3961 $Coro::current->{desc} = "runtime saver"; 4026 $Coro::current->{desc} = "runtime saver";
3962 write_runtime_sync 4027 write_runtime_sync
3963 or warn "ERROR: unable to write runtime file: $!"; 4028 or error "ERROR: unable to write runtime file: $!";
3964 }; 4029 };
3965 } 4030 }
3966 4031
3967 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) { 4032 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3968 $sig->send; 4033 $sig->send;
3976 4041
3977 if (0) { 4042 if (0) {
3978 if ($NEXT_TICK) { 4043 if ($NEXT_TICK) {
3979 my $jitter = $TICK_START - $NEXT_TICK; 4044 my $jitter = $TICK_START - $NEXT_TICK;
3980 $JITTER = $JITTER * 0.75 + $jitter * 0.25; 4045 $JITTER = $JITTER * 0.75 + $jitter * 0.25;
3981 warn "jitter $JITTER\n";#d# 4046 debug "jitter $JITTER\n";#d#
3982 } 4047 }
3983 } 4048 }
3984} 4049}
3985 4050
3986{ 4051{

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines