--- deliantra/server/lib/cf.pm 2007/02/12 01:32:49 1.210 +++ deliantra/server/lib/cf.pm 2007/03/11 02:12:44 1.223 @@ -45,13 +45,14 @@ our %EXT_CORO = (); # coroutines bound to extensions our %EXT_MAP = (); # pluggable maps +our $RELOAD; # number of reloads so far our @EVENT; our $LIBDIR = datadir . "/ext"; our $TICK = MAX_TIME * 1e-6; our $TICK_WATCHER; our $AIO_POLL_WATCHER; -our $WRITE_RUNTIME_WATCHER; +our $NEXT_RUNTIME_WRITE; # when should the runtime file be written our $NEXT_TICK; our $NOW; our $USE_FSYNC = 1; # use fsync to write maps - default off @@ -275,9 +276,7 @@ my $guard = Coro::guard { $TICK_WATCHER->start; - $WRITE_RUNTIME_WATCHER->start; }; - $WRITE_RUNTIME_WATCHER->stop; $TICK_WATCHER->stop; $guard } @@ -358,6 +357,8 @@ } sub write_runtime { + my $guard = cf::lock_acquire "write_runtime"; + my $runtime = cf::localdir . "/runtime"; my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644 @@ -789,12 +790,6 @@ aio_unlink $filename; aio_unlink "$filename.pst"; } - - #d##TODO# nuke non .map-files if exist - if ($filename =~ s/\.map$//) { - aio_unlink $filename; - aio_unlink "$filename.pst"; - } } } @@ -811,9 +806,6 @@ my ($data, $av); - #d#TODO remove .map if file does not exist - aio_stat $filename and $filename =~ s/\.map$//; - (aio_load $filename, $data) >= 0 or return; @@ -1041,6 +1033,8 @@ sub quit_character { my ($pl) = @_; + my $name = $pl->ob->name; + $pl->{deny_save} = 1; $pl->password ("*"); # this should lock out the player until we nuked the dir @@ -1054,9 +1048,29 @@ aio_rename $path, $temp; delete $cf::PLAYER{$pl->ob->name}; $pl->destroy; + + my $prefix = qr<^~\Q$name\E/>; + + # nuke player maps + $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP; + IO::AIO::aio_rmtree $temp; } +=item $pl->kick + +Kicks a player out of the game. This destroys the connection. + +=cut + +sub kick { + my ($pl, $kicker) = @_; + + $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker); + $pl->killer ("kicked"); + $pl->ns->destroy; +} + =item cf::player::list_logins Returns am arrayref of all valid playernames in the system, can take a @@ -1309,11 +1323,6 @@ utf8::encode (my $save = $self->save_path); IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save; IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst"; - - #d#TODO remove .map and also nuke - $save =~ s/\.map// or return;#d# - IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;#d# - IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";#d# } sub load_header_from($) { @@ -1822,6 +1831,8 @@ return unless $self->contr->active; $self->activate_recursive; + + local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext $self->enter_map ($map, $x, $y); } @@ -1844,12 +1855,12 @@ if ($pl->ob->{_link_pos}) { $pl->ob->enter_link; (async { - # we need this sleep as the login has a concurrent enter_exit running - # and this sleep increases chances of the player not ending up in scorn $pl->ob->reply (undef, "There was an internal problem at your last logout, " . "the server will try to bring you to your intended destination in a second.", cf::NDI_RED); + # we need this sleep as the login has a concurrent enter_exit running + # and this sleep increases chances of the player not ending up in scorn Coro::Timer::sleep 1; $pl->ob->leave_link; })->prio (2); @@ -2258,9 +2269,38 @@ ############################################################################# # the server's init and main functions -sub load_resources { - load_regions sprintf "%s/%s/regions", cf::datadir, cf::mapdir +sub load_faces { + my $path = sprintf "%s/faces", cf::datadir; + + warn "loading faces from $path\n"; + + my $faces; + 0 < aio_load $path, $faces + or die "$path: $!"; + + Coro::cede; + $faces = Storable::thaw $faces; + Coro::cede; + + while (my ($face, $info) = each %$faces) { + my $idx = (cf::face::find $face) || cf::face::alloc $face; + cf::face::set $idx, $info->{visibility}, $info->{magicmap}; + cf::face::set_data $idx, 0, $info->{data32}, $info->{chksum32}; + Coro::cede; + } + + 1 +} + +sub reload_resources { + load_resource_file sprintf "%s/%s/regions", cf::datadir, cf::mapdir or die "unable to load regions file\n";#d# + load_faces + or die "unable to load faces\n";#d# +} + +sub init { + reload_resources; } sub cfg_load { @@ -2284,10 +2324,6 @@ } } -sub init { - load_resources; -} - sub main { # we must not ever block the main coroutine local $Coro::idle = sub { @@ -2356,6 +2392,13 @@ warn "leave emergency perl save\n"; } +sub post_cleanup { + my ($make_core) = @_; + + warn Carp::longmess "post_cleanup backtrace" + if $make_core; +} + sub reload() { # can/must only be called in main if ($Coro::current != $Coro::main) { @@ -2365,12 +2408,16 @@ warn "reloading..."; - warn "cancelling server ticker"; - $TICK_WATCHER->cancel; + warn "entering sync_job"; - cf::emergency_save; + cf::sync_job { + cf::write_runtime; # external watchdog should not bark + cf::emergency_save; + cf::write_runtime; # external watchdog should not bark + + warn "syncing database to disk"; + BDB::db_env_txn_checkpoint $DB_ENV; - eval { # if anything goes wrong in here, we should simply crash as we already saved warn "cancelling all WF_AUTOCANCEL watchers"; @@ -2378,9 +2425,6 @@ $_->cancel if $_->data & WF_AUTOCANCEL; } - warn "syncing database to disk"; - BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { }; - warn "flushing outstanding aio requests"; for (;;) { BDB::flush; @@ -2390,6 +2434,8 @@ warn "iterate..."; } + ++$RELOAD; + warn "cancelling all extension coros"; $_->cancel for values %EXT_CORO; %EXT_CORO = (); @@ -2456,23 +2502,23 @@ cf::load_extensions; warn "reattaching attachments to objects/players"; - _global_reattach; + _global_reattach; # objects, sockets warn "reattaching attachments to maps"; reattach $_ for values %MAP; + warn "reattaching attachments to players"; + reattach $_ for values %PLAYER; warn "loading reloadable resources"; - load_resources; + reload_resources; - warn "restarting server ticker"; + warn "leaving sync_job"; - $TICK_WATCHER->start; - }; - - if ($@) { + 1 + } or do { warn $@; warn "error while reloading, exiting."; exit 1; - } + }; warn "reloaded"; }; @@ -2523,12 +2569,17 @@ $RUNTIME += $TICK; $NEXT_TICK += $TICK; + if ($NOW >= $NEXT_RUNTIME_WRITE) { + $NEXT_RUNTIME_WRITE = $NOW + 10; + Coro::async_pool { + write_runtime + or warn "ERROR: unable to write runtime file: $!"; + }; + } + $WAIT_FOR_TICK->broadcast; $WAIT_FOR_TICK_ONE->send if $WAIT_FOR_TICK_ONE->awaited; - Event::sweep; - Coro::cede_notself; - # my $AFTER = Event::time; # warn $AFTER - $NOW;#d# @@ -2597,26 +2648,14 @@ IO::AIO::max_poll_time $TICK * 0.1; $AIO_POLL_WATCHER = Event->io ( reentrant => 0, + data => WF_AUTOCANCEL, fd => IO::AIO::poll_fileno, poll => 'r', prio => 6, - data => WF_AUTOCANCEL, cb => \&IO::AIO::poll_cb, ); } -$WRITE_RUNTIME_WATCHER = Event->timer ( - reentrant => 0, - data => WF_AUTOCANCEL, - after => 1, - interval => 10, - prio => 6, # keep it lowest so it acts like a watchdog - cb => Coro::unblock_sub { - write_runtime - or warn "ERROR: unable to write runtime file: $!"; - }, -); - END { cf::emergency_save } 1