#! perl use Fcntl (); use Coro::AIO; CONF HIGHSCORE_ENTRIES = 1000 CONF EXPORT_HIGHSCORE = undef our $HIGHSCORE; # [name, title, exp, killer, where, hp, sp, gp, uuid] sub load_highscore() { # if (0 <= aio_load "$cf::LOCALDIR/hiscore.json", my $json) { # return JSON::XS::decode_json $json; # } # try to convert old highscore file if (0 <= aio_load "$cf::LOCALDIR/highscore", my $data) { # warn "converting from old $cf::LOCALDIR/highscore file\n"; return [map [split /:/], split /\n/, $data]; } return []; } sub reload { my $lock = cf::lock_acquire "highscore"; cf::async_ext { $lock; $HIGHSCORE = load_highscore; }; } cf::post_init { reload; }; sub save_highscore() { my $highscore = join "", map "$_\n", map +(join ":", @$_), @$HIGHSCORE; cf::replace_file "$cf::LOCALDIR/highscore", $highscore, 1; cf::trace "saved highscore file.\n"; 1 } our $HEADER = " | rank | name | experience | reason | HP |mana |grace|\n"; our $SEP = " +------+--------------------|--------------|----------------------+-----+-----+-----+\n"; our $FORMAT = " | %4s | %-18.18s | %12s | %-20.20s | %3s | %3s | %3s |\n"; our $SCORE_CHANNEL = { id => "highscore", title => "Highscore", tooltip => "Your highscore ranking.", }; sub fmt($$) { my ($pos, $score) = @_; my ($name, $title, $exp, $killer, $map, $hp, $sp, $grace) = @$score; sprintf $FORMAT, defined $pos ? $pos + 1 : "-", $name, $exp, $killer, $hp, $sp, $grace } sub check($) { my ($ob) = @_; my $score = [ $ob->name, length $ob->title ? $ob->title : $ob->contr->title, $ob->stats->exp, $ob->contr->killer_name, $ob->map ? $ob->map->name || $ob->map->path : "", $ob->stats->maxhp, $ob->stats->maxsp, $ob->stats->maxgrace, $ob->uuid, int AE::now, ]; my $guard = cf::lock_acquire "highscore"; cf::get_slot 0.01, 0, "highscore check"; my ($pre, $ins, $save); pop @$HIGHSCORE while @$HIGHSCORE > $HIGHSCORE_ENTRIES; # find previous entry, and new position for (0 .. $#$HIGHSCORE) { $pre //= $_ if $HIGHSCORE->[$_][0] eq $score->[0]; $ins //= $_ if $HIGHSCORE->[$_][2] < $score->[2]; } cf::cede_to_tick; # we need an "interruptible" block... my $msg; if (defined $pre) { # we are already in the list if ($HIGHSCORE->[$pre][2] < $score->[2]) { $msg = "T\n\n" . $SEP . $HEADER . $SEP . (fmt $ins, $score) . (fmt $pre, $HIGHSCORE->[$pre]) . $SEP; splice @$HIGHSCORE, $pre, 1; splice @$HIGHSCORE, $ins, 0, $score; $save = 1; } else { $msg = "T\n\n" . $SEP . $HEADER . $SEP . (fmt $pre , $HIGHSCORE->[$pre]) . (fmt undef, $score) . $SEP; } } elsif (defined $ins or @$HIGHSCORE < $HIGHSCORE_ENTRIES) { $ins //= @$HIGHSCORE; $msg = "T\n\n" . $SEP . $HEADER . $SEP . (fmt $ins, $score) . $SEP; splice @$HIGHSCORE, $ins, 0, $score; $save = 1; } else { $msg = "T\n\n" . $SEP . $HEADER . $SEP . (fmt -1 + (scalar @$HIGHSCORE), $HIGHSCORE->[-1]) . (fmt undef, $score) . $SEP; } cf::info $msg;#d# remove once working stable $ob->send_msg ($SCORE_CHANNEL => $msg, cf::NDI_CLEAR); if ($save) { save_highscore or die "unable to write highscore file: $!"; if (defined $EXPORT_HIGHSCORE) { cf::get_slot 0.05, 0, "highscore export"; my $rank; my @highscore = map { my ($name, $title, $exp, $killer, $map, $hp, $sp, $grace) = @$_; [++$rank, $name, $title, (cf::exp_to_level $exp), $exp, $killer, $map, $hp, $sp, $grace] } @$HIGHSCORE; cf::replace_file $EXPORT_HIGHSCORE, cf::encode_json { version => 1, date => $cf::NOW, data => \@highscore, }; } } }