--- deliantra/server/lib/cf.pm 2007/01/01 11:21:55 1.110 +++ deliantra/server/lib/cf.pm 2007/01/01 17:50:26 1.117 @@ -199,28 +199,34 @@ sub sync_job(&) { my ($job) = @_; - my $busy = 1; - my @res; - - my $coro = Coro::async { - @res = eval { $job->() }; - warn $@ if $@; - undef $busy; - }; - if ($Coro::current == $Coro::main) { + # this is the main coro, too bad, we have to block + # till the operation succeeds, freezing the server :/ + # TODO: use suspend/resume instead + # (but this is cancel-safe) local $FREEZE = 1; - $coro->prio (Coro::PRIO_MAX); + + my $busy = 1; + my @res; + + (Coro::async { + @res = eval { $job->() }; + warn $@ if $@; + undef $busy; + })->prio (Coro::PRIO_MAX); + while ($busy) { Coro::cede_notself; Event::one_event unless Coro::nready; } + + wantarray ? @res : $res[0] } else { - $coro->join; + # we are in another coroutine, how wonderful, everything just works + + $job->() } - - wantarray ? @res : $res[0] } =item $coro = cf::coro { BLOCK } @@ -254,7 +260,7 @@ my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644 or return; - my $value = $cf::RUNTIME; + my $value = $cf::RUNTIME + 1 + 10; # 10 is the runtime save interval, for a monotonic clock (aio_write $fh, 0, (length $value), $value, 0) <= 0 and return; @@ -285,7 +291,17 @@ my $self = bless { }, $class; - if ($path =~ s{^\?random/}{}) { + # {... are special paths that are not touched + # ?xxx/... are special absolute paths + # ?random/... random maps + # /! non-realised random map exit + # /... normal maps + # ~/... per-player maps without a specific player (DO NOT USE) + # ~user/... per-player map of a specific user + + if ($path =~ /^{/) { + # fine as it is + } elsif ($path =~ s{^\?random/}{}) { Coro::AIO::aio_load "$cf::RANDOM_MAPS/$path.meta", my $data; $self->{random} = cf::from_json $data; } else { @@ -800,7 +816,7 @@ unless (aio_stat "$filename.pst") { (aio_load "$filename.pst", $av) >= 0 or return; - $av = eval { (Storable::thaw <$av>)->{objs} }; + $av = eval { (Storable::thaw $av)->{objs} }; } return ($data, $av); @@ -1048,8 +1064,6 @@ our $MAX_RESET = 7200; our $DEFAULT_RESET = 3600; -$MAX_RESET = 10;#d# -$DEFAULT_RESET = 10;#d# sub generate_random_map { my ($path, $rmp) = @_; @@ -1092,7 +1106,6 @@ or return; $map->{load_path} = $path; - use Data::Dumper; warn Dumper $map;#d# $map } @@ -1102,7 +1115,7 @@ #warn "find_map<$path,$origin>\n";#d# - $path = ref $path ? $path : new cf::path $path, $origin && $origin->path; + $path = new cf::path $path, $origin && $origin->path; my $key = $path->as_string; $cf::MAP{$key} || do { @@ -1122,6 +1135,7 @@ $map or return; + $map->{load_original} = 1; $map->{instantiate_time} = $cf::RUNTIME; $map->instantiate; @@ -1131,9 +1145,13 @@ $map->path ($key); $map->{path} = $path; + $map->{last_save} = $cf::RUNTIME; $map->last_access ($cf::RUNTIME); - $map->reset if $map->should_reset; + if ($map->should_reset) { + $map->reset; + $map = find_map $path; + } $cf::MAP{$key} = $map } @@ -1152,6 +1170,9 @@ $self->load_objects ($self->{load_path}, 1) or return; + $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1) + if delete $self->{load_original}; + if (my $uniq = $path->uniq_path) { utf8::encode $uniq; if (aio_open $uniq, O_RDONLY, 0) { @@ -1195,19 +1216,17 @@ sub save { my ($self) = @_; - my $save = $self->{path}->save_path; utf8::encode $save; - my $uniq = $self->{path}->uniq_path; utf8::encode $uniq; - $self->{last_save} = $cf::RUNTIME; return unless $self->dirty; + my $save = $self->{path}->save_path; utf8::encode $save; + my $uniq = $self->{path}->uniq_path; utf8::encode $uniq; + $self->{load_path} = $save; return if $self->{deny_save}; - warn "saving map ", $self->path; - if ($uniq) { $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS); $self->save_objects ($uniq, cf::IO_UNIQUES); @@ -1228,17 +1247,44 @@ $self->in_memory (cf::MAP_SWAPPED); } -sub should_reset { - my ($map) = @_; +sub reset_at { + my ($self) = @_; # TODO: safety, remove and allow resettable per-player maps - return if $map->{path}{user_rel};#d# - return if $map->{deny_reset}; - #return unless $map->reset_timeout; + return 1e99 if $self->{path}{user_rel}; + return 1e99 if $self->{deny_reset}; - my $time = $map->fixed_resettime ? $map->{instantiate_time} : $map->last_access; + my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access; + my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET; - $time + ($map->reset_timeout || $DEFAULT_RESET) < $cf::RUNTIME + $time + $to +} + +sub should_reset { + my ($self) = @_; + + $self->reset_at <= $cf::RUNTIME +} + +sub unlink_save { + my ($self) = @_; + + utf8::encode (my $save = $self->{path}->save_path); + aioreq_pri 3; IO::AIO::aio_unlink $save; + aioreq_pri 3; IO::AIO::aio_unlink "$save.pst"; +} + +sub rename { + my ($self, $new_path) = @_; + + $self->unlink_save; + + delete $cf::MAP{$self->path}; + $self->{path} = new cf::path $new_path; + $self->path ($self->{path}->as_string); + $cf::MAP{$self->path} = $self; + + $self->save; } sub reset { @@ -1249,15 +1295,23 @@ warn "resetting map ", $self->path;#d# - utf8::encode (my $save = $self->{path}->save_path); - aioreq_pri 3; IO::AIO::aio_unlink $save; - aioreq_pri 3; IO::AIO::aio_unlink "$save.pst"; + delete $cf::MAP{$self->path}; $_->clear_links_to ($self) for values %cf::MAP; - $self->clear; - $self->in_memory (cf::MAP_SWAPPED); - utf8::encode ($self->{load_path} = $self->{path}->load_path); + $self->unlink_save; + $self->destroy; +} + +my $nuke_counter = "aaaa"; + +sub nuke { + my ($self) = @_; + + $self->{deny_save} = 1; + $self->reset_timeout (1); + $self->rename ("{nuke}/" . ($nuke_counter++)); + $self->reset; # polite request, might not happen } sub customise_for { @@ -1332,6 +1386,24 @@ : $cf::CFG{"may_$access"}) } +=item $player_object->enter_link + +Freezes the player and moves him/her to a special map (C<{link}>). + +The player should be reaosnably safe there for short amounts of time. You +I call C as soon as possible, though. + +=item $player_object->leave_link ($map, $x, $y) + +Moves the player out of the specila link map onto the given map. If the +map is not valid (or omitted), the player will be moved back to the +location he/she was before the call to C, or, if that fails, +to the emergency map position. + +Might block. + +=cut + sub cf::object::player::enter_link { my ($self) = @_; @@ -1350,8 +1422,6 @@ my $link_pos = delete $self->{_link_pos}; unless ($map) { - $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED); - # restore original map position ($map, $x, $y) = @{ $link_pos || [] }; $map = cf::map::find_map $map; @@ -1394,6 +1464,8 @@ warn "entering ", $map->path, " at ($x, $y)\n" if $map; + $map or $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED); + $self->leave_link ($map, $x, $y); })->prio (1); } @@ -1813,7 +1885,7 @@ sub main { # we must not ever block the main coroutine local $Coro::idle = sub { - Carp::cluck "FATAL: Coro::idle was called, major BUG\n";#d# + Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d# (Coro::unblock_sub { Event::one_event; })->(); @@ -1828,7 +1900,7 @@ ############################################################################# # initialisation -sub perl_reload() { +sub reload() { # can/must only be called in main if ($Coro::current != $Coro::main) { warn "can only reload from main coroutine\n"; @@ -1965,12 +2037,12 @@ register "", __PACKAGE__; -register_command "perl-reload" => sub { +register_command "reload" => sub { my ($who, $arg) = @_; if ($who->flag (FLAG_WIZ)) { $who->message ("start of reload."); - perl_reload; + reload; $who->message ("end of reload."); } };