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.532 by root, Thu Apr 29 07:32:34 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) = @_;
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
731 755
732 my $t1 = Time::HiRes::time; 756 my $t1 = Time::HiRes::time;
733 my $data = $process->(\@data); 757 my $data = $process->(\@data);
734 my $t2 = Time::HiRes::time; 758 my $t2 = Time::HiRes::time;
735 759
736 warn "cache: '$id' processed in ", $t2 - $t1, "s\n"; 760 info "cache: '$id' processed in ", $t2 - $t1, "s\n";
737 761
738 db_put cache => "$id/data", $data; 762 db_put cache => "$id/data", $data;
739 db_put cache => "$id/md5" , $md5; 763 db_put cache => "$id/md5" , $md5;
740 db_put cache => "$id/meta", $meta; 764 db_put cache => "$id/meta", $meta;
741 765
751 775
752=cut 776=cut
753 777
754sub datalog($@) { 778sub datalog($@) {
755 my ($type, %kv) = @_; 779 my ($type, %kv) = @_;
756 warn "DATALOG ", JSON::XS->new->ascii->encode ({ %kv, type => $type }); 780 info "DATALOG ", JSON::XS->new->ascii->encode ({ %kv, type => $type });
757} 781}
758 782
759=back 783=back
760 784
761=cut 785=cut
956 980
957 } elsif (exists $cb_id{$type}) { 981 } elsif (exists $cb_id{$type}) {
958 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg; 982 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
959 983
960 } elsif (ref $type) { 984 } elsif (ref $type) {
961 warn "attaching objects not supported, ignoring.\n"; 985 error "attaching objects not supported, ignoring.\n";
962 986
963 } else { 987 } else {
964 shift @arg; 988 shift @arg;
965 warn "attach argument '$type' not supported, ignoring.\n"; 989 error "attach argument '$type' not supported, ignoring.\n";
966 } 990 }
967 } 991 }
968} 992}
969 993
970sub _object_attach { 994sub _object_attach {
980 _attach $registry, $klass, @attach; 1004 _attach $registry, $klass, @attach;
981 } 1005 }
982 1006
983 $obj->{$name} = \%arg; 1007 $obj->{$name} = \%arg;
984 } else { 1008 } else {
985 warn "object uses attachment '$name' which is not available, postponing.\n"; 1009 info "object uses attachment '$name' which is not available, postponing.\n";
986 } 1010 }
987 1011
988 $obj->{_attachment}{$name} = undef; 1012 $obj->{_attachment}{$name} = undef;
989} 1013}
990 1014
1049 1073
1050 for (@$callbacks) { 1074 for (@$callbacks) {
1051 eval { &{$_->[1]} }; 1075 eval { &{$_->[1]} };
1052 1076
1053 if ($@) { 1077 if ($@) {
1054 warn "$@";
1055 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n"; 1078 error "$@", "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
1056 override; 1079 override;
1057 } 1080 }
1058 1081
1059 return 1 if $override; 1082 return 1 if $override;
1060 } 1083 }
1139 for (@$attach) { 1162 for (@$attach) {
1140 my ($klass, @attach) = @$_; 1163 my ($klass, @attach) = @$_;
1141 _attach $registry, $klass, @attach; 1164 _attach $registry, $klass, @attach;
1142 } 1165 }
1143 } else { 1166 } else {
1144 warn "object uses attachment '$name' that is not available, postponing.\n"; 1167 info "object uses attachment '$name' that is not available, postponing.\n";
1145 } 1168 }
1146 } 1169 }
1147} 1170}
1148 1171
1149cf::attachable->attach ( 1172cf::attachable->attach (
1176 my ($filename, $rdata, $objs) = @_; 1199 my ($filename, $rdata, $objs) = @_;
1177 1200
1178 sync_job { 1201 sync_job {
1179 if (length $$rdata) { 1202 if (length $$rdata) {
1180 utf8::decode (my $decname = $filename); 1203 utf8::decode (my $decname = $filename);
1181 warn sprintf "saving %s (%d,%d)\n", 1204 trace sprintf "saving %s (%d,%d)\n",
1182 $decname, length $$rdata, scalar @$objs; 1205 $decname, length $$rdata, scalar @$objs
1206 if $VERBOSE_IO;
1183 1207
1184 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) { 1208 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
1185 aio_chmod $fh, SAVE_MODE; 1209 aio_chmod $fh, SAVE_MODE;
1186 aio_write $fh, 0, (length $$rdata), $$rdata, 0; 1210 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
1187 if ($cf::USE_FSYNC) { 1211 if ($cf::USE_FSYNC) {
1209 aio_rename "$filename~", $filename; 1233 aio_rename "$filename~", $filename;
1210 1234
1211 $filename =~ s%/[^/]+$%%; 1235 $filename =~ s%/[^/]+$%%;
1212 aio_pathsync $filename if $cf::USE_FSYNC; 1236 aio_pathsync $filename if $cf::USE_FSYNC;
1213 } else { 1237 } else {
1214 warn "unable to save objects: $filename~: $!\n"; 1238 error "unable to save objects: $filename~: $!\n";
1215 } 1239 }
1216 } else { 1240 } else {
1217 aio_unlink $filename; 1241 aio_unlink $filename;
1218 aio_unlink "$filename.pst"; 1242 aio_unlink "$filename.pst";
1219 } 1243 }
1243 my $st = eval { Coro::Storable::thaw $av }; 1267 my $st = eval { Coro::Storable::thaw $av };
1244 $av = $st->{objs}; 1268 $av = $st->{objs};
1245 } 1269 }
1246 1270
1247 utf8::decode (my $decname = $filename); 1271 utf8::decode (my $decname = $filename);
1248 warn sprintf "loading %s (%d,%d)\n", 1272 trace sprintf "loading %s (%d,%d)\n",
1249 $decname, length $data, scalar @{$av || []}; 1273 $decname, length $data, scalar @{$av || []}
1274 if $VERBOSE_IO;
1250 1275
1251 ($data, $av) 1276 ($data, $av)
1252} 1277}
1253 1278
1254=head2 COMMAND CALLBACKS 1279=head2 COMMAND CALLBACKS
1346 1371
1347 $pl->ext_reply ($reply, @reply) 1372 $pl->ext_reply ($reply, @reply)
1348 if $reply; 1373 if $reply;
1349 1374
1350 } else { 1375 } else {
1351 warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n"; 1376 error "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1352 } 1377 }
1353 1378
1354 cf::override; 1379 cf::override;
1355 }, 1380 },
1356); 1381);
1367 1392
1368 $grp 1393 $grp
1369} 1394}
1370 1395
1371sub load_extensions { 1396sub load_extensions {
1397 info "loading extensions...";
1398
1372 cf::sync_job { 1399 cf::sync_job {
1373 my %todo; 1400 my %todo;
1374 1401
1375 for my $path (<$LIBDIR/*.ext>) { 1402 for my $path (<$LIBDIR/*.ext>) {
1376 next unless -r $path; 1403 next unless -r $path;
1416 for (split /,\s*/, $v->{meta}{depends}) { 1443 for (split /,\s*/, $v->{meta}{depends}) {
1417 next ext 1444 next ext
1418 unless exists $done{$_}; 1445 unless exists $done{$_};
1419 } 1446 }
1420 1447
1421 warn "... pass $pass, loading '$k' into '$v->{pkg}'\n"; 1448 trace "... pass $pass, loading '$k' into '$v->{pkg}'\n";
1422 1449
1423 my $active = eval $v->{source}; 1450 my $active = eval $v->{source};
1424 1451
1425 if (length $@) { 1452 if (length $@) {
1426 warn "$v->{path}: $@\n"; 1453 error "$v->{path}: $@\n";
1427 1454
1428 cf::cleanup "mandatory extension '$k' failed to load, exiting." 1455 cf::cleanup "mandatory extension '$k' failed to load, exiting."
1429 if exists $v->{meta}{mandatory}; 1456 if exists $v->{meta}{mandatory};
1457
1458 warn "$v->{base}: optional extension cannot be loaded, skipping.\n";
1459 delete $todo{$k};
1430 } else { 1460 } else {
1431 $done{$k} = delete $todo{$k}; 1461 $done{$k} = delete $todo{$k};
1432 push @EXTS, $v->{pkg}; 1462 push @EXTS, $v->{pkg};
1433 $progress = 1; 1463 $progress = 1;
1434 1464
1435 warn "$v->{base}: extension inactive.\n" 1465 info "$v->{base}: extension inactive.\n"
1436 unless $active; 1466 unless $active;
1437 } 1467 }
1438 } 1468 }
1439 1469
1440 unless ($progress) { 1470 unless ($progress) {
1612 $pl->password ("*"); # this should lock out the player until we have nuked the dir 1642 $pl->password ("*"); # this should lock out the player until we have nuked the dir
1613 1643
1614 $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active; 1644 $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1615 $pl->deactivate; 1645 $pl->deactivate;
1616 my $killer = cf::arch::get "killer_quit"; $pl->killer ($killer); $killer->destroy; 1646 my $killer = cf::arch::get "killer_quit"; $pl->killer ($killer); $killer->destroy;
1617 $pl->ob->check_score;
1618 $pl->invoke (cf::EVENT_PLAYER_QUIT); 1647 $pl->invoke (cf::EVENT_PLAYER_QUIT);
1619 $pl->ns->destroy if $pl->ns; 1648 $pl->ns->destroy if $pl->ns;
1620 1649
1621 my $path = playerdir $pl; 1650 my $path = playerdir $pl;
1622 my $temp = "$path~$cf::RUNTIME~deleting~"; 1651 my $temp = "$path~$cf::RUNTIME~deleting~";
1677 \@logins 1706 \@logins
1678} 1707}
1679 1708
1680=item $player->maps 1709=item $player->maps
1681 1710
1711=item cf::player::maps $login
1712
1682Returns an arrayref of map paths that are private for this 1713Returns an arrayref of map paths that are private for this
1683player. May block. 1714player. May block.
1684 1715
1685=cut 1716=cut
1686 1717
1747 1778
1748=cut 1779=cut
1749 1780
1750sub find_by_path($) { 1781sub find_by_path($) {
1751 my ($path) = @_; 1782 my ($path) = @_;
1783
1784 $path =~ s/^~[^\/]*//; # skip ~login
1752 1785
1753 my ($match, $specificity); 1786 my ($match, $specificity);
1754 1787
1755 for my $region (list) { 1788 for my $region (list) {
1756 if ($region->{match} && $path =~ $region->{match}) { 1789 if ($region->{match} && $path =~ $region->{match}) {
1820 1853
1821 $EXT_MAP{$pkg} = [$prio, qr<$regex>]; 1854 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1822} 1855}
1823 1856
1824# also paths starting with '/' 1857# also paths starting with '/'
1825$EXT_MAP{"cf::map"} = [0, qr{^(?=/)}]; 1858$EXT_MAP{"cf::map::wrap"} = [0, qr{^(?=/)}];
1826 1859
1827sub thawer_merge { 1860sub thawer_merge {
1828 my ($self, $merge) = @_; 1861 my ($self, $merge) = @_;
1829 1862
1830 # we have to keep some variables in memory intact 1863 # we have to keep some variables in memory intact
2140 or next; 2173 or next;
2141 $neigh = find $neigh, $map 2174 $neigh = find $neigh, $map
2142 or next; 2175 or next;
2143 $neigh->load; 2176 $neigh->load;
2144 2177
2178 # now find the diagonal neighbours
2145 push @neigh, 2179 push @neigh,
2146 [$neigh->tile_path (($_ + 3) % 4), $neigh], 2180 [$neigh->tile_path (($_ + 3) % 4), $neigh],
2147 [$neigh->tile_path (($_ + 1) % 4), $neigh]; 2181 [$neigh->tile_path (($_ + 1) % 4), $neigh];
2148 } 2182 }
2149 2183
2201 $MAP_PREFETCHER->prio (6); 2235 $MAP_PREFETCHER->prio (6);
2202 2236
2203 () 2237 ()
2204} 2238}
2205 2239
2240# common code, used by both ->save and ->swapout
2206sub save { 2241sub _save {
2207 my ($self) = @_; 2242 my ($self) = @_;
2208
2209 my $lock = cf::lock_acquire "map_data:$self->{path}";
2210 2243
2211 $self->{last_save} = $cf::RUNTIME; 2244 $self->{last_save} = $cf::RUNTIME;
2212 2245
2213 return unless $self->dirty; 2246 return unless $self->dirty;
2214 2247
2234 } else { 2267 } else {
2235 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES); 2268 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2236 } 2269 }
2237} 2270}
2238 2271
2272sub save {
2273 my ($self) = @_;
2274
2275 my $lock = cf::lock_acquire "map_data:$self->{path}";
2276
2277 $self->_save;
2278}
2279
2239sub swap_out { 2280sub swap_out {
2240 my ($self) = @_; 2281 my ($self) = @_;
2241 2282
2242 # save first because save cedes
2243 $self->save;
2244
2245 my $lock = cf::lock_acquire "map_data:$self->{path}"; 2283 my $lock = cf::lock_acquire "map_data:$self->{path}";
2246 2284
2247 return if $self->players;
2248 return if $self->in_memory != cf::MAP_ACTIVE; 2285 return if $self->in_memory != cf::MAP_ACTIVE;
2249 return if $self->{deny_save}; 2286 return if $self->{deny_save};
2287 return if $self->players;
2250 2288
2289 # first deactivate the map and "unlink" it from the core
2290 $self->deactivate;
2291 $_->clear_links_to ($self) for values %cf::MAP;
2251 $self->in_memory (cf::MAP_SWAPPED); 2292 $self->in_memory (cf::MAP_SWAPPED);
2293
2294 # then atomically save
2295 $self->_save;
2296
2297 # then free the map
2298 $self->clear;
2299}
2300
2301sub reset_at {
2302 my ($self) = @_;
2303
2304 # TODO: safety, remove and allow resettable per-player maps
2305 return 1e99 if $self->{deny_reset};
2306
2307 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2308 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2309
2310 $time + $to
2311}
2312
2313sub should_reset {
2314 my ($self) = @_;
2315
2316 $self->reset_at <= $cf::RUNTIME
2317}
2318
2319sub reset {
2320 my ($self) = @_;
2321
2322 my $lock = cf::lock_acquire "map_data:$self->{path}";
2323
2324 return if $self->players;
2325
2326 cf::trace "resetting map ", $self->path, "\n";
2327
2328 $self->in_memory (cf::MAP_SWAPPED);
2329
2330 # need to save uniques path
2331 unless ($self->{deny_save}) {
2332 my $uniq = $self->uniq_path; utf8::encode $uniq;
2333
2334 $self->_save_objects ($uniq, cf::IO_UNIQUES)
2335 if $uniq;
2336 }
2337
2338 delete $cf::MAP{$self->path};
2252 2339
2253 $self->deactivate; 2340 $self->deactivate;
2254 $_->clear_links_to ($self) for values %cf::MAP; 2341 $_->clear_links_to ($self) for values %cf::MAP;
2255 $self->clear; 2342 $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 2343
2301 $self->unlink_save; 2344 $self->unlink_save;
2302 $self->destroy; 2345 $self->destroy;
2303} 2346}
2304 2347
2312 2355
2313 delete $cf::MAP{$self->path}; 2356 delete $cf::MAP{$self->path};
2314 2357
2315 $self->unlink_save; 2358 $self->unlink_save;
2316 2359
2317 bless $self, "cf::map"; 2360 bless $self, "cf::map::wrap";
2318 delete $self->{deny_reset}; 2361 delete $self->{deny_reset};
2319 $self->{deny_save} = 1; 2362 $self->{deny_save} = 1;
2320 $self->reset_timeout (1); 2363 $self->reset_timeout (1);
2321 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++)); 2364 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2322 2365
2552 2595
2553Freezes the player and moves him/her to a special map (C<{link}>). 2596Freezes the player and moves him/her to a special map (C<{link}>).
2554 2597
2555The player should be reasonably safe there for short amounts of time (e.g. 2598The 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, 2599for 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 2600though, as the player cannot control the character while it is on the link
2558map. 2601map.
2559 2602
2560Will never block. 2603Will never block.
2561 2604
2562=item $player_object->leave_link ($map, $x, $y) 2605=item $player_object->leave_link ($map, $x, $y)
2583sub cf::object::player::enter_link { 2626sub cf::object::player::enter_link {
2584 my ($self) = @_; 2627 my ($self) = @_;
2585 2628
2586 $self->deactivate_recursive; 2629 $self->deactivate_recursive;
2587 2630
2631 ++$self->{_link_recursion};
2632
2588 return if UNIVERSAL::isa $self->map, "ext::map_link"; 2633 return if UNIVERSAL::isa $self->map, "ext::map_link";
2589 2634
2590 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y] 2635 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2591 if $self->map && $self->map->{path} ne "{link}"; 2636 if $self->map && $self->map->{path} ne "{link}";
2592 2637
2593 $self->enter_map ($LINK_MAP || link_map, 10, 10); 2638 $self->enter_map ($LINK_MAP || link_map, 3, 3);
2594} 2639}
2595 2640
2596sub cf::object::player::leave_link { 2641sub cf::object::player::leave_link {
2597 my ($self, $map, $x, $y) = @_; 2642 my ($self, $map, $x, $y) = @_;
2598 2643
2623 $map->load_neighbours; 2668 $map->load_neighbours;
2624 2669
2625 return unless $self->contr->active; 2670 return unless $self->contr->active;
2626 2671
2627 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext 2672 local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2628 $self->enter_map ($map, $x, $y); 2673 if ($self->enter_map ($map, $x, $y)) {
2629 2674 # entering was successful
2675 delete $self->{_link_recursion};
2630 # only activate afterwards, to support waiting in hooks 2676 # only activate afterwards, to support waiting in hooks
2631 $self->activate_recursive; 2677 $self->activate_recursive;
2632} 2678 }
2633 2679
2680}
2681
2634=item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]]) 2682=item $player_object->goto ($path, $x, $y[, $check->($map, $x, $y, $player)[, $done->($player)]])
2635 2683
2636Moves the player to the given map-path and coordinates by first freezing 2684Moves the player to the given map-path and coordinates by first freezing
2637her, loading and preparing them map, calling the provided $check callback 2685her, loading and preparing them map, calling the provided $check callback
2638that has to return the map if sucecssful, and then unfreezes the player on 2686that 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 2687the new (success) or old (failed) map position. In either case, $done will
2646 2694
2647our $GOTOGEN; 2695our $GOTOGEN;
2648 2696
2649sub cf::object::player::goto { 2697sub cf::object::player::goto {
2650 my ($self, $path, $x, $y, $check, $done) = @_; 2698 my ($self, $path, $x, $y, $check, $done) = @_;
2699
2700 if ($self->{_link_recursion} >= $MAX_LINKS) {
2701 error "FATAL: link recursion exceeded, ", $self->name, " goto $path $x $y, redirecting.";
2702 $self->failmsg ("Something went wrong inside the server - please contact an administrator!");
2703 ($path, $x, $y) = @$EMERGENCY_POSITION;
2704 }
2651 2705
2652 # do generation counting so two concurrent goto's will be executed in-order 2706 # do generation counting so two concurrent goto's will be executed in-order
2653 my $gen = $self->{_goto_generation} = ++$GOTOGEN; 2707 my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2654 2708
2655 $self->enter_link; 2709 $self->enter_link;
2679 my $map = eval { 2733 my $map = eval {
2680 my $map = defined $path ? cf::map::find $path : undef; 2734 my $map = defined $path ? cf::map::find $path : undef;
2681 2735
2682 if ($map) { 2736 if ($map) {
2683 $map = $map->customise_for ($self); 2737 $map = $map->customise_for ($self);
2684 $map = $check->($map) if $check && $map; 2738 $map = $check->($map, $x, $y, $self) if $check && $map;
2685 } else { 2739 } else {
2686 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED); 2740 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2687 } 2741 }
2688 2742
2689 $map 2743 $map
2697 if ($gen == $self->{_goto_generation}) { 2751 if ($gen == $self->{_goto_generation}) {
2698 delete $self->{_goto_generation}; 2752 delete $self->{_goto_generation};
2699 $self->leave_link ($map, $x, $y); 2753 $self->leave_link ($map, $x, $y);
2700 } 2754 }
2701 2755
2702 $done->() if $done; 2756 $done->($self) if $done;
2703 })->prio (1); 2757 })->prio (1);
2704} 2758}
2705 2759
2706=item $player_object->enter_exit ($exit_object) 2760=item $player_object->enter_exit ($exit_object)
2707 2761
2800 $self->message ("Something went wrong deep within the deliantra server. " 2854 $self->message ("Something went wrong deep within the deliantra server. "
2801 . "I'll try to bring you back to the map you were before. " 2855 . "I'll try to bring you back to the map you were before. "
2802 . "Please report this to the dungeon master!", 2856 . "Please report this to the dungeon master!",
2803 cf::NDI_UNIQUE | cf::NDI_RED); 2857 cf::NDI_UNIQUE | cf::NDI_RED);
2804 2858
2805 warn "ERROR in enter_exit: $@"; 2859 error "ERROR in enter_exit: $@";
2806 $self->leave_link; 2860 $self->leave_link;
2807 } 2861 }
2808 })->prio (1); 2862 })->prio (1);
2809} 2863}
2810 2864
3123 3177
3124 $ns->ext_reply ($reply, @reply) 3178 $ns->ext_reply ($reply, @reply)
3125 if $reply; 3179 if $reply;
3126 3180
3127 } else { 3181 } else {
3128 warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n"; 3182 error "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
3129 } 3183 }
3130 3184
3131 cf::override; 3185 cf::override;
3132 }, 3186 },
3133); 3187);
3263 local @cf::_safe_eval_args = values %vars; 3317 local @cf::_safe_eval_args = values %vars;
3264 @res = wantarray ? eval eval : scalar eval $eval; 3318 @res = wantarray ? eval eval : scalar eval $eval;
3265 } 3319 }
3266 3320
3267 if ($@) { 3321 if ($@) {
3268 warn "$@"; 3322 warn "$@",
3269 warn "while executing safe code '$code'\n"; 3323 "while executing safe code '$code'\n",
3270 warn "with arguments " . (join " ", %vars) . "\n"; 3324 "with arguments " . (join " ", %vars) . "\n";
3271 } 3325 }
3272 3326
3273 wantarray ? @res : $res[0] 3327 wantarray ? @res : $res[0]
3274} 3328}
3275 3329
3309 # for this (global event?) 3363 # for this (global event?)
3310 %ext::player_env::MUSIC_FACE_CACHE = (); 3364 %ext::player_env::MUSIC_FACE_CACHE = ();
3311 3365
3312 my $enc = JSON::XS->new->utf8->canonical->relaxed; 3366 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3313 3367
3314 warn "loading facedata from $path\n"; 3368 trace "loading facedata from $path\n";
3315 3369
3316 my $facedata; 3370 my $facedata;
3317 0 < aio_load $path, $facedata 3371 0 < aio_load $path, $facedata
3318 or die "$path: $!"; 3372 or die "$path: $!";
3319 3373
3353 3407
3354 if (my $smooth = cf::face::find $info->{smooth}) { 3408 if (my $smooth = cf::face::find $info->{smooth}) {
3355 cf::face::set_smooth $idx, $smooth; 3409 cf::face::set_smooth $idx, $smooth;
3356 cf::face::set_smoothlevel $idx, $info->{smoothlevel}; 3410 cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3357 } else { 3411 } else {
3358 warn "smooth face '$info->{smooth}' not found for face '$face'"; 3412 error "smooth face '$info->{smooth}' not found for face '$face'";
3359 } 3413 }
3360 3414
3361 cf::cede_to_tick; 3415 cf::cede_to_tick;
3362 } 3416 }
3363 } 3417 }
3381 my $idx = (cf::face::find $name) || cf::face::alloc $name; 3435 my $idx = (cf::face::find $name) || cf::face::alloc $name;
3382 3436
3383 cf::face::set_data $idx, 0, $info->{data}, $info->{hash}; 3437 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3384 cf::face::set_type $idx, $info->{type}; 3438 cf::face::set_type $idx, $info->{type};
3385 } else { 3439 } else {
3386 $RESOURCE{$name} = $info; 3440 $RESOURCE{$name} = $info; # unused
3387 } 3441 }
3388 3442
3389 cf::cede_to_tick; 3443 cf::cede_to_tick;
3390 } 3444 }
3391 } 3445 }
3392 3446
3393 cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE); 3447 cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3394 3448
3395 1 3449 1
3396} 3450}
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 3451
3418register_exticmd fx_want => sub { 3452register_exticmd fx_want => sub {
3419 my ($ns, $want) = @_; 3453 my ($ns, $want) = @_;
3420 3454
3421 while (my ($k, $v) = each %$want) { 3455 while (my ($k, $v) = each %$want) {
3460sub reload_treasures { 3494sub reload_treasures {
3461 load_resource_file "$DATADIR/treasures" 3495 load_resource_file "$DATADIR/treasures"
3462 or die "unable to load treasurelists\n"; 3496 or die "unable to load treasurelists\n";
3463} 3497}
3464 3498
3499sub reload_sound {
3500 trace "loading sound config from $DATADIR/sound\n";
3501
3502 0 < Coro::AIO::aio_load "$DATADIR/sound", my $data
3503 or die "$DATADIR/sound $!";
3504
3505 my $soundconf = JSON::XS->new->utf8->relaxed->decode ($data);
3506
3507 for (0 .. SOUND_CAST_SPELL_0 - 1) {
3508 my $sound = $soundconf->{compat}[$_]
3509 or next;
3510
3511 my $face = cf::face::find "sound/$sound->[1]";
3512 cf::sound::set $sound->[0] => $face;
3513 cf::sound::old_sound_index $_, $face; # gcfclient-compat
3514 }
3515
3516 while (my ($k, $v) = each %{$soundconf->{event}}) {
3517 my $face = cf::face::find "sound/$v";
3518 cf::sound::set $k => $face;
3519 }
3520}
3521
3465sub reload_resources { 3522sub reload_resources {
3466 warn "reloading resource files...\n"; 3523 trace "reloading resource files...\n";
3467 3524
3468 reload_facedata; 3525 reload_facedata;
3526 reload_sound;
3469 reload_archetypes; 3527 reload_archetypes;
3470 reload_regions; 3528 reload_regions;
3471 reload_treasures; 3529 reload_treasures;
3472 3530
3473 warn "finished reloading resource files\n"; 3531 trace "finished reloading resource files\n";
3474} 3532}
3475 3533
3476sub reload_config { 3534sub reload_config {
3477 warn "reloading config file...\n"; 3535 trace "reloading config file...\n";
3478 3536
3479 open my $fh, "<:utf8", "$CONFDIR/config" 3537 open my $fh, "<:utf8", "$CONFDIR/config"
3480 or return; 3538 or return;
3481 3539
3482 local $/; 3540 local $/;
3483 *CFG = YAML::XS::Load scalar <$fh>; 3541 *CFG = YAML::XS::Load scalar <$fh>;
3484 3542
3485 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37]; 3543 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_104_115", 49, 38];
3486 3544
3487 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset}; 3545 $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}; 3546 $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3489 3547
3490 if (exists $CFG{mlockall}) { 3548 if (exists $CFG{mlockall}) {
3493 and die "WARNING: m(un)lockall failed: $!\n"; 3551 and die "WARNING: m(un)lockall failed: $!\n";
3494 }; 3552 };
3495 warn $@ if $@; 3553 warn $@ if $@;
3496 } 3554 }
3497 3555
3498 warn "finished reloading resource files\n"; 3556 trace "finished reloading resource files\n";
3499} 3557}
3500 3558
3501sub pidfile() { 3559sub pidfile() {
3502 sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT 3560 sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3503 or die "$PIDFILE: $!"; 3561 or die "$PIDFILE: $!";
3516 seek $fh, 0, 0; 3574 seek $fh, 0, 0;
3517 print $fh $$; 3575 print $fh $$;
3518} 3576}
3519 3577
3520sub main_loop { 3578sub main_loop {
3521 warn "EV::loop starting\n"; 3579 trace "EV::loop starting\n";
3522 if (1) { 3580 if (1) {
3523 EV::loop; 3581 EV::loop;
3524 } 3582 }
3525 warn "EV::loop returned\n"; 3583 trace "EV::loop returned\n";
3526 goto &main_loop unless $REALLY_UNLOOP; 3584 goto &main_loop unless $REALLY_UNLOOP;
3527} 3585}
3528 3586
3529sub main { 3587sub main {
3530 cf::init_globals; # initialise logging 3588 cf::init_globals; # initialise logging
3531 3589
3532 LOG llevInfo, "Welcome to Deliantra, v" . VERSION; 3590 LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3533 LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team."; 3591 LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3534 LOG llevInfo, "Copyright (C) 1994 Mark Wedel."; 3592 LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3535 LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen."; 3593 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 3594
3542 $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority 3595 $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3543 3596
3544 # we must not ever block the main coroutine 3597 # we must not ever block the main coroutine
3545 local $Coro::idle = sub { 3598 local $Coro::idle = sub {
3551 }; 3604 };
3552 3605
3553 evthread_start IO::AIO::poll_fileno; 3606 evthread_start IO::AIO::poll_fileno;
3554 3607
3555 cf::sync_job { 3608 cf::sync_job {
3609 cf::init_experience;
3610 cf::init_anim;
3611 cf::init_attackmess;
3612 cf::init_dynamic;
3613
3556 cf::load_settings; 3614 cf::load_settings;
3557 cf::load_materials; 3615 cf::load_materials;
3558 3616
3559 reload_resources; 3617 reload_resources;
3560 reload_config; 3618 reload_config;
3576 use POSIX (); 3634 use POSIX ();
3577 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD}; 3635 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3578 3636
3579 (pop @POST_INIT)->(0) while @POST_INIT; 3637 (pop @POST_INIT)->(0) while @POST_INIT;
3580 }; 3638 };
3639
3640 cf::object::thawer::errors_are_fatal 0;
3641 info "parse errors in files are no longer fatal from this point on.\n";
3581 3642
3582 main_loop; 3643 main_loop;
3583} 3644}
3584 3645
3585############################################################################# 3646#############################################################################
3626 or return; 3687 or return;
3627 3688
3628 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE 3689 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3629 and return; 3690 and return;
3630 3691
3631 warn sprintf "runtime file written (%gs).\n", AE::time - $t0; 3692 trace sprintf "runtime file written (%gs).\n", AE::time - $t0;
3632 3693
3633 1 3694 1
3634} 3695}
3635 3696
3636our $uuid_lock; 3697our $uuid_lock;
3648 or return; 3709 or return;
3649 3710
3650 my $value = uuid_seq uuid_cur; 3711 my $value = uuid_seq uuid_cur;
3651 3712
3652 unless ($value) { 3713 unless ($value) {
3653 warn "cowardly refusing to write zero uuid value!\n"; 3714 info "cowardly refusing to write zero uuid value!\n";
3654 return; 3715 return;
3655 } 3716 }
3656 3717
3657 my $value = uuid_str $value + $uuid_skip; 3718 my $value = uuid_str $value + $uuid_skip;
3658 $uuid_skip = 0; 3719 $uuid_skip = 0;
3668 or return; 3729 or return;
3669 3730
3670 aio_rename "$uuid~", $uuid 3731 aio_rename "$uuid~", $uuid
3671 and return; 3732 and return;
3672 3733
3673 warn "uuid file written ($value).\n"; 3734 trace "uuid file written ($value).\n";
3674 3735
3675 1 3736 1
3676 3737
3677} 3738}
3678 3739
3684} 3745}
3685 3746
3686sub emergency_save() { 3747sub emergency_save() {
3687 my $freeze_guard = cf::freeze_mainloop; 3748 my $freeze_guard = cf::freeze_mainloop;
3688 3749
3689 warn "emergency_perl_save: enter\n"; 3750 info "emergency_perl_save: enter\n";
3690 3751
3691 cf::sync_job { 3752 cf::sync_job {
3692 # this is a trade-off: we want to be very quick here, so 3753 # 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 3754 # save all maps without fsync, and later call a global sync
3694 # (which in turn might be very very slow) 3755 # (which in turn might be very very slow)
3695 local $USE_FSYNC = 0; 3756 local $USE_FSYNC = 0;
3696 3757
3697 # use a peculiar iteration method to avoid tripping on perl 3758 # use a peculiar iteration method to avoid tripping on perl
3698 # refcount bugs in for. also avoids problems with players 3759 # refcount bugs in for. also avoids problems with players
3699 # and maps saved/destroyed asynchronously. 3760 # and maps saved/destroyed asynchronously.
3700 warn "emergency_perl_save: begin player save\n"; 3761 info "emergency_perl_save: begin player save\n";
3701 for my $login (keys %cf::PLAYER) { 3762 for my $login (keys %cf::PLAYER) {
3702 my $pl = $cf::PLAYER{$login} or next; 3763 my $pl = $cf::PLAYER{$login} or next;
3703 $pl->valid or next; 3764 $pl->valid or next;
3704 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt 3765 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3705 $pl->save; 3766 $pl->save;
3706 } 3767 }
3707 warn "emergency_perl_save: end player save\n"; 3768 info "emergency_perl_save: end player save\n";
3708 3769
3709 warn "emergency_perl_save: begin map save\n"; 3770 info "emergency_perl_save: begin map save\n";
3710 for my $path (keys %cf::MAP) { 3771 for my $path (keys %cf::MAP) {
3711 my $map = $cf::MAP{$path} or next; 3772 my $map = $cf::MAP{$path} or next;
3712 $map->valid or next; 3773 $map->valid or next;
3713 $map->save; 3774 $map->save;
3714 } 3775 }
3715 warn "emergency_perl_save: end map save\n"; 3776 info "emergency_perl_save: end map save\n";
3716 3777
3717 warn "emergency_perl_save: begin database checkpoint\n"; 3778 info "emergency_perl_save: begin database checkpoint\n";
3718 BDB::db_env_txn_checkpoint $DB_ENV; 3779 BDB::db_env_txn_checkpoint $DB_ENV;
3719 warn "emergency_perl_save: end database checkpoint\n"; 3780 info "emergency_perl_save: end database checkpoint\n";
3720 3781
3721 warn "emergency_perl_save: begin write uuid\n"; 3782 info "emergency_perl_save: begin write uuid\n";
3722 write_uuid_sync 1; 3783 write_uuid_sync 1;
3723 warn "emergency_perl_save: end write uuid\n"; 3784 info "emergency_perl_save: end write uuid\n";
3724 }; 3785 };
3725 3786
3726 warn "emergency_perl_save: starting sync()\n"; 3787 info "emergency_perl_save: starting sync()\n";
3727 IO::AIO::aio_sync sub { 3788 IO::AIO::aio_sync sub {
3728 warn "emergency_perl_save: finished sync()\n"; 3789 info "emergency_perl_save: finished sync()\n";
3729 }; 3790 };
3730 3791
3731 warn "emergency_perl_save: leave\n"; 3792 info "emergency_perl_save: leave\n";
3732} 3793}
3733 3794
3734sub post_cleanup { 3795sub post_cleanup {
3735 my ($make_core) = @_; 3796 my ($make_core) = @_;
3736 3797
3737 warn Carp::longmess "post_cleanup backtrace" 3798 error Carp::longmess "post_cleanup backtrace"
3738 if $make_core; 3799 if $make_core;
3739 3800
3740 my $fh = pidfile; 3801 my $fh = pidfile;
3741 unlink $PIDFILE if <$fh> == $$; 3802 unlink $PIDFILE if <$fh> == $$;
3742} 3803}
3762 my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH}; 3823 my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3763 for my $name (keys %$leaf_symtab) { 3824 for my $name (keys %$leaf_symtab) {
3764 _gv_clear *{"$pkg$name"}; 3825 _gv_clear *{"$pkg$name"};
3765# use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"}; 3826# use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3766 } 3827 }
3767 warn "cleared package $pkg\n";#d#
3768} 3828}
3769 3829
3770sub do_reload_perl() { 3830sub do_reload_perl() {
3771 # can/must only be called in main 3831 # can/must only be called in main
3772 if ($Coro::current != $Coro::main) { 3832 if (in_main) {
3773 warn "can only reload from main coroutine"; 3833 error "can only reload from main coroutine";
3774 return; 3834 return;
3775 } 3835 }
3776 3836
3777 return if $RELOAD++; 3837 return if $RELOAD++;
3778 3838
3779 my $t1 = AE::time; 3839 my $t1 = AE::time;
3780 3840
3781 while ($RELOAD) { 3841 while ($RELOAD) {
3782 warn "reloading..."; 3842 info "reloading...";
3783 3843
3784 warn "entering sync_job"; 3844 trace "entering sync_job";
3785 3845
3786 cf::sync_job { 3846 cf::sync_job {
3787 cf::write_runtime_sync; # external watchdog should not bark 3847 cf::write_runtime_sync; # external watchdog should not bark
3788 cf::emergency_save; 3848 cf::emergency_save;
3789 cf::write_runtime_sync; # external watchdog should not bark 3849 cf::write_runtime_sync; # external watchdog should not bark
3790 3850
3791 warn "syncing database to disk"; 3851 trace "syncing database to disk";
3792 BDB::db_env_txn_checkpoint $DB_ENV; 3852 BDB::db_env_txn_checkpoint $DB_ENV;
3793 3853
3794 # if anything goes wrong in here, we should simply crash as we already saved 3854 # if anything goes wrong in here, we should simply crash as we already saved
3795 3855
3796 warn "flushing outstanding aio requests"; 3856 trace "flushing outstanding aio requests";
3797 while (IO::AIO::nreqs || BDB::nreqs) { 3857 while (IO::AIO::nreqs || BDB::nreqs) {
3798 Coro::EV::timer_once 0.01; # let the sync_job do it's thing 3858 Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3799 } 3859 }
3800 3860
3801 warn "cancelling all extension coros"; 3861 trace "cancelling all extension coros";
3802 $_->cancel for values %EXT_CORO; 3862 $_->cancel for values %EXT_CORO;
3803 %EXT_CORO = (); 3863 %EXT_CORO = ();
3804 3864
3805 warn "removing commands"; 3865 trace "removing commands";
3806 %COMMAND = (); 3866 %COMMAND = ();
3807 3867
3808 warn "removing ext/exti commands"; 3868 trace "removing ext/exti commands";
3809 %EXTCMD = (); 3869 %EXTCMD = ();
3810 %EXTICMD = (); 3870 %EXTICMD = ();
3811 3871
3812 warn "unloading/nuking all extensions"; 3872 trace "unloading/nuking all extensions";
3813 for my $pkg (@EXTS) { 3873 for my $pkg (@EXTS) {
3814 warn "... unloading $pkg"; 3874 trace "... unloading $pkg";
3815 3875
3816 if (my $cb = $pkg->can ("unload")) { 3876 if (my $cb = $pkg->can ("unload")) {
3817 eval { 3877 eval {
3818 $cb->($pkg); 3878 $cb->($pkg);
3819 1 3879 1
3820 } or warn "$pkg unloaded, but with errors: $@"; 3880 } or error "$pkg unloaded, but with errors: $@";
3821 } 3881 }
3822 3882
3823 warn "... clearing $pkg"; 3883 trace "... clearing $pkg";
3824 clear_package $pkg; 3884 clear_package $pkg;
3825 } 3885 }
3826 3886
3827 warn "unloading all perl modules loaded from $LIBDIR"; 3887 trace "unloading all perl modules loaded from $LIBDIR";
3828 while (my ($k, $v) = each %INC) { 3888 while (my ($k, $v) = each %INC) {
3829 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/; 3889 next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3830 3890
3831 warn "... unloading $k"; 3891 trace "... unloading $k";
3832 delete $INC{$k}; 3892 delete $INC{$k};
3833 3893
3834 $k =~ s/\.pm$//; 3894 $k =~ s/\.pm$//;
3835 $k =~ s/\//::/g; 3895 $k =~ s/\//::/g;
3836 3896
3839 } 3899 }
3840 3900
3841 clear_package $k; 3901 clear_package $k;
3842 } 3902 }
3843 3903
3844 warn "getting rid of safe::, as good as possible"; 3904 trace "getting rid of safe::, as good as possible";
3845 clear_package "safe::$_" 3905 clear_package "safe::$_"
3846 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region); 3906 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3847 3907
3848 warn "unloading cf.pm \"a bit\""; 3908 trace "unloading cf.pm \"a bit\"";
3849 delete $INC{"cf.pm"}; 3909 delete $INC{"cf.pm"};
3850 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES; 3910 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES;
3851 3911
3852 # don't, removes xs symbols, too, 3912 # don't, removes xs symbols, too,
3853 # and global variables created in xs 3913 # and global variables created in xs
3854 #clear_package __PACKAGE__; 3914 #clear_package __PACKAGE__;
3855 3915
3856 warn "unload completed, starting to reload now"; 3916 info "unload completed, starting to reload now";
3857 3917
3858 warn "reloading cf.pm"; 3918 trace "reloading cf.pm";
3859 require cf; 3919 require cf;
3860 cf::_connect_to_perl_1; 3920 cf::_connect_to_perl_1;
3861 3921
3862 warn "loading config and database again"; 3922 trace "loading config and database again";
3863 cf::reload_config; 3923 cf::reload_config;
3864 3924
3865 warn "loading extensions"; 3925 trace "loading extensions";
3866 cf::load_extensions; 3926 cf::load_extensions;
3867 3927
3868 if ($REATTACH_ON_RELOAD) { 3928 if ($REATTACH_ON_RELOAD) {
3869 warn "reattaching attachments to objects/players"; 3929 trace "reattaching attachments to objects/players";
3870 _global_reattach; # objects, sockets 3930 _global_reattach; # objects, sockets
3871 warn "reattaching attachments to maps"; 3931 trace "reattaching attachments to maps";
3872 reattach $_ for values %MAP; 3932 reattach $_ for values %MAP;
3873 warn "reattaching attachments to players"; 3933 trace "reattaching attachments to players";
3874 reattach $_ for values %PLAYER; 3934 reattach $_ for values %PLAYER;
3875 } 3935 }
3876 3936
3877 warn "running post_init jobs"; 3937 trace "running post_init jobs";
3878 (pop @POST_INIT)->(1) while @POST_INIT; 3938 (pop @POST_INIT)->(1) while @POST_INIT;
3879 3939
3880 warn "leaving sync_job"; 3940 trace "leaving sync_job";
3881 3941
3882 1 3942 1
3883 } or do { 3943 } or do {
3884 warn $@; 3944 error $@;
3885 cf::cleanup "error while reloading, exiting."; 3945 cf::cleanup "error while reloading, exiting.";
3886 }; 3946 };
3887 3947
3888 warn "reloaded"; 3948 info "reloaded";
3889 --$RELOAD; 3949 --$RELOAD;
3890 } 3950 }
3891 3951
3892 $t1 = AE::time - $t1; 3952 $t1 = AE::time - $t1;
3893 warn "reload completed in ${t1}s\n"; 3953 info "reload completed in ${t1}s\n";
3894}; 3954};
3895 3955
3896our $RELOAD_WATCHER; # used only during reload 3956our $RELOAD_WATCHER; # used only during reload
3897 3957
3898sub reload_perl() { 3958sub reload_perl() {
3958 if ($NOW >= $NEXT_RUNTIME_WRITE) { 4018 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3959 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.; 4019 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
3960 Coro::async_pool { 4020 Coro::async_pool {
3961 $Coro::current->{desc} = "runtime saver"; 4021 $Coro::current->{desc} = "runtime saver";
3962 write_runtime_sync 4022 write_runtime_sync
3963 or warn "ERROR: unable to write runtime file: $!"; 4023 or error "ERROR: unable to write runtime file: $!";
3964 }; 4024 };
3965 } 4025 }
3966 4026
3967 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) { 4027 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3968 $sig->send; 4028 $sig->send;
3976 4036
3977 if (0) { 4037 if (0) {
3978 if ($NEXT_TICK) { 4038 if ($NEXT_TICK) {
3979 my $jitter = $TICK_START - $NEXT_TICK; 4039 my $jitter = $TICK_START - $NEXT_TICK;
3980 $JITTER = $JITTER * 0.75 + $jitter * 0.25; 4040 $JITTER = $JITTER * 0.75 + $jitter * 0.25;
3981 warn "jitter $JITTER\n";#d# 4041 debug "jitter $JITTER\n";#d#
3982 } 4042 }
3983 } 4043 }
3984} 4044}
3985 4045
3986{ 4046{

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines