--- deliantra/server/ext/login.ext 2007/01/05 20:04:02 1.10 +++ deliantra/server/ext/login.ext 2007/01/07 02:39:14 1.11 @@ -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,9 @@ 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; last; } elsif (can_cleanup $buf, $mtime) { Coro::Timer::sleep 1; @@ -171,7 +176,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 +215,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 +250,17 @@ "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; } }); } +# "Quitting will delete your character PERMANENTLY. If you are sure you want to do this, then use the quit_character command instead of quit." +# "Quitting will delete your character.\nAre you sure you want to quit (y/n):" +# quit, quit_character, save + cf::object->attach ( type => cf::SAVEBED, on_apply => sub { @@ -260,21 +268,26 @@ return cf::override 0 unless $ob->type == cf::PLAYER; + my $pl = $ob->pl; + # 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->{clean_save} = 1; + $pl->save; + }; } }); }, @@ -303,3 +316,38 @@ 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; + } + }; + warn $@ if $@; + Coro::cede; + }; + } +}; + +$SCHEDULER->prio (1); +