--- deliantra/server/lib/cf.pm 2007/01/08 14:11:05 1.149 +++ deliantra/server/lib/cf.pm 2007/01/09 15:36:19 1.154 @@ -17,6 +17,7 @@ use Coro::Semaphore; use Coro::AIO; +use Data::Dumper; use Digest::MD5; use Fcntl; use IO::AIO 2.32 (); @@ -156,8 +157,29 @@ =over 4 +=item dumpval $ref + =cut +sub dumpval { + eval { + local $SIG{__DIE__}; + my $d; + if (1) { + $d = new Data::Dumper([$_[0]], ["*var"]); + $d->Terse(1); + $d->Indent(2); + $d->Quotekeys(0); + $d->Useqq(1); + #$d->Bless(...); + $d->Seen($_[1]) if @_ > 1; + $d = $d->Dump(); + } + $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge; + $d + } || "[unable to dump $_[0]: '$@']"; +} + use JSON::Syck (); # TODO# replace by JSON::PC once working =item $ref = cf::from_json $json @@ -337,6 +359,9 @@ package cf::path; +# used to convert map paths into valid unix filenames by repalcing / by ∕ +our $PATH_SEP = "∕"; # U+2215, chosen purely for visual reasons + sub new { my ($class, $path, $base) = @_; @@ -409,8 +434,7 @@ # escape the /'s in the path sub _escaped_path { - # ∕ is U+2215 - (my $path = $_[0]{path}) =~ s/\//∕/g; + (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g; $path } @@ -1078,6 +1102,8 @@ package cf::player; +use Coro::AIO; + =head3 cf::player =over 4 @@ -1121,8 +1147,12 @@ my $guard = cf::lock_acquire "user_find:$login"; - $cf::PLAYER{$login} ||= (load_pl path $login or return); - }; + $cf::PLAYER{$_[0]} || do { + my $pl = load_pl path $login + or return; + $cf::PLAYER{$login} = $pl + } + } } sub save($) { @@ -1135,7 +1165,7 @@ return if $pl->{deny_save}; - Coro::AIO::aio_mkdir playerdir $pl, 0770; + aio_mkdir playerdir $pl, 0770; $pl->{last_save} = $cf::RUNTIME; $pl->save_pl ($path); @@ -1155,6 +1185,13 @@ $self } +=item $pl->quit_character + +Nukes the player without looking back. If logged in, the connection will +be destroyed. May block for a long time. + +=cut + sub quit_character { my ($pl) = @_; @@ -1168,12 +1205,62 @@ my $path = playerdir $pl; my $temp = "$path~$cf::RUNTIME~deleting~"; - IO::AIO::aio_rename $path, $temp, sub { - delete $cf::PLAYER{$pl->ob->name}; - $pl->destroy; + aio_rename $path, $temp; + delete $cf::PLAYER{$pl->ob->name}; + $pl->destroy; + IO::AIO::aio_rmtree $temp; +} - IO::AIO::aio_rmtree $temp; - }; +=item cf::player::list_logins + +Returns am arrayref of all valid playernames in the system, can take a +while and may block, so not sync_job-capable, ever. + +=cut + +sub list_logins { + my $dirs = aio_readdir cf::localdir . "/" . cf::playerdir + or return []; + + my @logins; + + for my $login (@$dirs) { + my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next; + aio_read $fh, 0, 512, my $buf, 0 or next; + $buf !~ /^password -------------$/ or next; # official not-valid tag + + utf8::decode $login; + push @logins, $login; + } + + \@logins +} + +=item $player->maps + +Returns an arrayref of cf::path's of all maps that are private for this +player. May block. + +=cut + +sub maps($) { + my ($pl) = @_; + + my $files = aio_readdir playerdir $pl + or return; + + my @paths; + + for (@$files) { + utf8::decode $_; + next if /\.(?:pl|pst)$/; + next unless /^$PATH_SEP/; + + s/$PATH_SEP/\//g; + push @paths, new cf::path "~" . $pl->ob->name . "/" . $_; + } + + \@paths } =item $player->ext_reply ($msgid, $msgtype, %msg) @@ -1671,18 +1758,18 @@ sub cf::object::player::goto { my ($self, $path, $x, $y) = @_; + $path = new cf::path $path; + $path ne "/" or Carp::cluck ("oy");#d# + $self->enter_link; (async { - $path = new cf::path $path; - my $map = cf::map::find $path->as_string; $map = $map->customise_for ($self) if $map; # warn "entering ", $map->path, " at ($x, $y)\n" # if $map; - $map or $map->cluck ("oy");#d# $map or $self->message ("The exit to '" . ($path->visible_name) . "' is closed", cf::NDI_UNIQUE | cf::NDI_RED); $self->leave_link ($map, $x, $y);