--- deliantra/server/lib/cf.pm 2007/01/13 23:06:13 1.166 +++ deliantra/server/lib/cf.pm 2007/01/14 22:14:35 1.176 @@ -10,7 +10,7 @@ use Safe; use Safe::Hole; -use Coro 3.3 (); +use Coro 3.4 (); use Coro::Event; use Coro::Timer; use Coro::Signal; @@ -46,6 +46,8 @@ our $TICK = MAX_TIME * 1e-6; our $TICK_WATCHER; +our $AIO_POLL_WATCHER; +our $WRITE_RUNTIME_WATCHER; our $NEXT_TICK; our $NOW; @@ -259,7 +261,11 @@ sub freeze_mainloop { return unless $TICK_WATCHER->is_active; - my $guard = Coro::guard { $TICK_WATCHER->start }; + my $guard = Coro::guard { + $TICK_WATCHER->start; + $WRITE_RUNTIME_WATCHER->start; + }; + $WRITE_RUNTIME_WATCHER->stop; $TICK_WATCHER->stop; $guard } @@ -340,8 +346,6 @@ } sub write_runtime { - return unless $TICK_WATCHER->is_active; - my $runtime = cf::localdir . "/runtime"; my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644 @@ -501,6 +505,12 @@ my %attachment; +sub cf::attachable::thawer_merge { + # simply override everything except _meta + local $_[0]{_meta}; + %{$_[0]} = %{$_[1]}; +} + sub _attach_cb($$$$) { my ($registry, $event, $prio, $cb) = @_; @@ -682,6 +692,8 @@ # basically do the same as instantiate, without calling instantiate my ($obj) = @_; + bless $obj, ref $obj; # re-bless in case extensions have been reloaded + my $registry = $obj->registry; @$registry = (); @@ -902,20 +914,6 @@ } ############################################################################# -# load/save/clean perl data associated with a map - -*cf::mapsupport::on_clean = sub { - my ($map) = @_; - - my $path = $map->tmpname; - defined $path or return; - - unlink "$path.pst"; -}; - -cf::map->attach (prio => -10000, package => cf::mapsupport::); - -############################################################################# =head2 CORE EXTENSIONS @@ -1079,6 +1077,7 @@ next if /\.(?:pl|pst)$/; next unless /^$PATH_SEP/o; + s/\.map$//; push @paths, "~" . $pl->ob->name . "/" . $_; } @@ -1116,7 +1115,8 @@ use Coro::AIO; use overload - '""' => \&as_string; + '""' => \&as_string, + fallback => 1; our $MAX_RESET = 3600; our $DEFAULT_RESET = 3000; @@ -1153,6 +1153,18 @@ # also paths starting with '/' $EXT_MAP{"cf::map"} = qr{^(?=/)}; +sub thawer_merge { + my ($self, $merge) = @_; + + # we have to keep some variables in memory intact + local $self->{path}; + local $self->{load_path}; + local $self->{deny_save}; + local $self->{deny_reset}; + + $self->SUPER::thawer_merge ($merge); +} + sub normalise { my ($path, $base) = @_; @@ -1222,13 +1234,6 @@ &as_string } -# escape the /'s in the path -sub _escaped_path { - (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g; - - $path -} - # the original (read-only) location sub load_path { my ($self) = @_; @@ -1240,14 +1245,16 @@ sub save_path { my ($self) = @_; - sprintf "%s/%s/%s.map", cf::localdir, cf::tmpdir, $self->_escaped_path + (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g; + sprintf "%s/%s/%s", cf::localdir, cf::tmpdir, $path } # the unique path, undef == no special unique path sub uniq_path { my ($self) = @_; - sprintf "%s/%s/%s", cf::localdir, cf::uniquedir, $self->_escaped_path + (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g; + sprintf "%s/%s/%s", cf::localdir, cf::uniquedir, $path } # and all this just because we cannot iterate over @@ -1263,8 +1270,8 @@ my ($self) = @_; utf8::encode (my $save = $self->save_path); - IO::AIO::aioreq_pri 4; IO::AIO::aio_unlink $save; - IO::AIO::aioreq_pri 4; IO::AIO::aio_unlink "$save.pst"; + IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save; + IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst"; } sub load_header_from($) { @@ -1335,6 +1342,7 @@ or return; if ($map->should_reset) { + $cf::WAIT_FOR_TICK->wait; $map->reset; undef $guard; $map = find $path @@ -1514,7 +1522,7 @@ my ($self) = @_; # TODO: safety, remove and allow resettable per-player maps - return 1e99 if $self->isa ("ext::map_per_player"); + return 1e99 if $self->isa ("ext::map_per_player");#d# return 1e99 if $self->{deny_reset}; my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access; @@ -1529,24 +1537,10 @@ $self->reset_at <= $cf::RUNTIME } -sub rename { - my ($self, $new_path) = @_; - - normalise $new_path; - - $self->unlink_save; - - delete $cf::MAP{$self->path}; - $self->{path} = $new_path; $self->path ($self->{path}); - $cf::MAP{$self->path} = $self; - - $self->save; -} - sub reset { my ($self) = @_; - my $lock = cf::lock_acquire "map_data:" . $self->path; + my $lock = cf::lock_acquire "map_data:$self->{path}"; return if $self->players; return if $self->isa ("ext::map_per_player");#d# @@ -1555,6 +1549,9 @@ delete $cf::MAP{$self->path}; + $self->in_memory (cf::MAP_SWAPPED); + $self->clear; + $_->clear_links_to ($self) for values %cf::MAP; $self->unlink_save; @@ -1566,9 +1563,18 @@ sub nuke { my ($self) = @_; + delete $cf::MAP{$self->path}; + + $self->unlink_save; + + bless $self, "cf::map"; + delete $self->{deny_reset}; $self->{deny_save} = 1; $self->reset_timeout (1); - $self->rename ("{nuke}/" . ($nuke_counter++)); + $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++)); + + $cf::MAP{$self->path} = $self; + $self->reset; # polite request, might not happen } @@ -1590,6 +1596,7 @@ next if /\.pst$/; next unless /^$PATH_SEP/o; + s/\.map$//; push @paths, $_; } @@ -2223,7 +2230,9 @@ # we must not ever block the main coroutine local $Coro::idle = sub { Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d# - async { Event::one_event }; + (async { + Event::one_event; + })->prio (Coro::PRIO_MAX); }; cfg_load; @@ -2256,7 +2265,7 @@ cf::sync_job { # use a peculiar iteration method to avoid tripping on perl # refcount bugs in for. also avoids problems with players - # and maps saved/Destroyed asynchronously. + # and maps saved/destroyed asynchronously. warn "begin emergency player save\n"; for my $login (keys %cf::PLAYER) { my $pl = $cf::PLAYER{$login} or next; @@ -2381,13 +2390,21 @@ warn "reloaded"; }; +our $RELOAD_WATCHER; # used only during reload + register_command "reload" => sub { my ($who, $arg) = @_; if ($who->flag (FLAG_WIZ)) { - $who->message ("start of reload."); - reload; - $who->message ("end of reload."); + $who->message ("reloading server."); + + # doing reload synchronously and two reloads happen back-to-back, + # coro crashes during coro_state_free->destroy here. + + $RELOAD_WATCHER ||= Event->timer (after => 0, data => WF_AUTOCANCEL, cb => sub { + reload; + undef $RELOAD_WATCHER; + }); } }; @@ -2418,7 +2435,7 @@ IO::AIO::max_poll_time $TICK * 0.2; -Event->io ( +$AIO_POLL_WATCHER = Event->io ( fd => IO::AIO::poll_fileno, poll => 'r', prio => 5, @@ -2426,7 +2443,7 @@ cb => \&IO::AIO::poll_cb, ); -Event->timer ( +$WRITE_RUNTIME_WATCHER = Event->timer ( data => WF_AUTOCANCEL, after => 0, interval => 10,