--- deliantra/server/ext/login.ext 2007/01/05 20:04:02 1.10 +++ deliantra/server/ext/login.ext 2007/01/08 12:23:32 1.15 @@ -37,7 +37,7 @@ sub check_playing { my ($ns, $user) = @_; - return unless cf::player::find $user; + return unless cf::player::find_active $user; $ns->send_drawinfo ( "That player is already logged in on this server. " @@ -54,6 +54,14 @@ 1 } +sub check_clean_save { + my ($pl) = @_; + + unless (delete $pl->{clean_save}) { + #d#TODO + } +} + # delete a player directory, be non-blocking AND synchronous... # (thats hard, so we crap out and fork). sub nuke_playerdir { @@ -125,9 +133,6 @@ ); } - my $dir = "$PLAYERDIR/$user"; - my $plfile = "$dir/$user.pl"; - # lock this username for the remainder of this login session if ($cf::LOGIN_LOCK{$user}) { $ns->send_drawinfo ( @@ -142,7 +147,7 @@ check_playing $ns, $user and next; # try to read the user file and check the password - if (my $fh = aio_open $plfile, O_RDONLY, 0) { + if (my $fh = aio_open cf::player::path $user, O_RDONLY, 0) { my $mtime = (stat $fh)[9]; 0 < aio_read $fh, 0, 16384, my $buf, 0 or next; @@ -152,9 +157,10 @@ if ($hash eq crypt $pass, $hash) { nuke_str $pass; # password matches, wonderful - my $pl = cf::player::load $plfile or next; - $pl->enable_save (1); + my $pl = cf::player::find $user or next; $pl->connect ($ns); + check_clean_save $pl; + $pl->{clean_save} = 1; last; } elsif (can_cleanup $buf, $mtime) { Coro::Timer::sleep 1; @@ -171,7 +177,7 @@ or next; # check if the file hasn't changed - aio_stat $plfile and next; + aio_stat cf::player::path $user and next; $mtime == (stat _)[9] or next; nuke_playerdir $user; @@ -210,8 +216,7 @@ nuke_str $pass2; - my $pl = cf::player::create; - $pl->ob->name ($user); + my $pl = cf::player::new $user; $pl->password (crypt $pass, join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]); nuke_str $pass; $pl->connect ($ns); @@ -246,13 +251,42 @@ "Now choose a character.\nPress any key to change outlook.\nPress `d' when you're pleased.\n"); $ns->state (cf::ST_CHANGE_CLASS); - $pl->enable_save (1);#d# too early + delete $pl->{deny_save};#d# too early last; } }); } +cf::register_command quit => sub { + my ($ob, $arg) = @_; + + $ob->reply (undef, + "Quitting will delete your character PERMANENTLY: It will be gone forever and any progress will be lost. " + . "If you are sure you want to do this, then use the quit_character command instead of quit.", + cf::NDI_UNIQUE | cf::NDI_RED); +}; + +cf::register_command quit_character => sub { + my ($ob, $arg) = @_; + + my $pl = $ob->contr; + + $pl->ns->query (cf::CS_QUERY_SINGLECHAR, "Do you want to PERMANENTLY delete your character and all associated data (y/n)?", sub { + if ($_[0] !~ /^[yY]/) { + $ob->reply (undef, + "Ok, not not quitting then.", + cf::NDI_UNIQUE | cf::NDI_RED); + } else { + $ob->reply (undef, + "Ok, quitting, hope to see you again.", + cf::NDI_UNIQUE | cf::NDI_RED); + $pl->ns->flush; + $pl->quit_character; + } + }); +}; + cf::object->attach ( type => cf::SAVEBED, on_apply => sub { @@ -260,21 +294,23 @@ return cf::override 0 unless $ob->type == cf::PLAYER; + my $pl = $ob->contr; + # update respawn position - $ob->contr->savebed ($bed->map->path, $bed->x, $bed->y); + $pl->savebed ($bed->map->path, $bed->x, $bed->y); - $ob->contr->killer ("left"); + $pl->killer ("left"); $ob->check_score; $ob->reply (undef, "In the future, you will wake up here when you die."); - $ob->contr->save (1); - $ob->contr->ns->query (cf::CS_QUERY_SINGLECHAR, "Do you want to continue playing (y/n)?", sub { + $pl->ns->query (cf::CS_QUERY_SINGLECHAR, "Do you want to continue playing (y/n)?", sub { if ($_[0] !~ /^[yY]/) { - $ob->contr->invoke (cf::EVENT_PLAYER_LOGOUT, 1); - $ob->contr->ns->destroy; + $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1); + $pl->deactivate; + $pl->ns->destroy; } else { - $ob->contr->enable_save (1); + cf::async { $pl->save }; } }); }, @@ -303,3 +339,46 @@ on_addme => \&addme, ); +############################################################################# + +our $SCHEDULE_INTERVAL = 10; # time the player scheduler sleeps between runs +our $SWAP_TIMEOUT = 30; # time after which an unused player is evicted form memory +our $SAVE_TIMEOUT = 20; # save players every n seconds +our $SAVE_INTERVAL = 0.1; # save at max. one player every $SAVE_INTERVAL + +our $SCHEDULER = cf::async_ext { + while () { + Coro::Timer::sleep $SCHEDULE_INTERVAL; + + # this weird form of iteration over values is used because + # the hash changes underneath us frequently, and for + # keeps a direct reference to the value without (in 5.8 perls) + # keeping a reference, so this is prone to crashes or worse. + my @players = keys %cf::PLAYER; + for (@players) { + my $pl = $cf::PLAYER{$_} + or next; + $pl->valid or next; + + eval { + if ($pl->{last_save} + $SAVE_TIMEOUT <= $cf::RUNTIME) { + $pl->save; + Coro::Timer::sleep $SAVE_INTERVAL; + } + my $ob = $pl->ob; + Scalar::Util::weaken $pl; # 2 == from object + from perl + Scalar::Util::weaken $ob; # 2 == one from being an object + ??? + my $a_ = $pl->refcnt; + my $b_ = $ob->refcnt; + my $a = $pl->refcnt_cnt; + my $b = $ob->refcnt_cnt; + warn "rc $a,$a_ $b,$b_\n";#d# + }; + warn $@ if $@; + Coro::cede; + }; + } +}; + +$SCHEDULER->prio (1); +