1 | #! perl |
1 | #! perl |
2 | |
2 | |
3 | use Fcntl (); |
3 | use Fcntl (); |
4 | use Coro::AIO; |
4 | use Coro::AIO; |
5 | |
5 | |
6 | our $HIGHSCORE_ENTRIES = $cf::CFG{highscore_entries} || 1000; |
6 | CONF HIGHSCORE_ENTRIES = 1000 |
|
|
7 | CONF EXPORT_HIGHSCORE = "www.deliantra.net/highscore.json" |
|
|
8 | |
|
|
9 | our $HIGHSCORE; |
7 | |
10 | |
8 | # [name, title, exp, killer, where, hp, sp, gp, uuid] |
11 | # [name, title, exp, killer, where, hp, sp, gp, uuid] |
9 | |
12 | |
10 | sub load_hiscore() { |
13 | sub load_highscore() { |
11 | # if (0 <= aio_load "$cf::LOCALDIR/hiscore.json", my $json) { |
14 | # if (0 <= aio_load "$cf::LOCALDIR/hiscore.json", my $json) { |
12 | # return JSON::XS::decode_json $json; |
15 | # return JSON::XS::decode_json $json; |
13 | # } |
16 | # } |
14 | |
17 | |
15 | # try to convert old highscore file |
18 | # try to convert old highscore file |
… | |
… | |
19 | } |
22 | } |
20 | |
23 | |
21 | return []; |
24 | return []; |
22 | } |
25 | } |
23 | |
26 | |
24 | sub save_hiscore($) { |
27 | sub reload { |
25 | my ($hiscore) = @_; |
28 | my $lock = cf::lock_acquire "highscore"; |
26 | |
29 | |
27 | my $fh = aio_open "$cf::LOCALDIR/highscore~", Fcntl::O_WRONLY | Fcntl::O_CREAT | Fcntl::O_TRUNC, 0644 |
30 | cf::async_ext { |
28 | or return; |
31 | $lock; |
29 | |
32 | |
30 | $hiscore = join "", map "$_\n", map +(join ":", @$_), @$hiscore; |
33 | $HIGHSCORE = load_highscore; |
|
|
34 | }; |
|
|
35 | } |
31 | |
36 | |
32 | length $hiscore == aio_write $fh, 0, (length $hiscore), $hiscore, 0 |
37 | cf::post_init { |
33 | or return; |
38 | reload; |
|
|
39 | }; |
34 | |
40 | |
35 | # always fsync - this file is important |
41 | sub save_highscore() { |
36 | aio_fsync $fh |
42 | my $highscore = join "", map "$_\n", map +(join ":", @$_), @$HIGHSCORE; |
37 | and return; |
|
|
38 | |
43 | |
39 | close $fh |
44 | cf::replace_file "$cf::LOCALDIR/highscore", $highscore, 1; |
40 | or return; |
|
|
41 | |
|
|
42 | aio_rename "$cf::LOCALDIR/highscore~", "$cf::LOCALDIR/highscore" |
|
|
43 | and return; |
|
|
44 | |
|
|
45 | aio_pathsync "$cf::LOCALDIR"; |
|
|
46 | |
|
|
47 | cf::trace "saved highscore file.\n"; |
45 | cf::trace "saved highscore file.\n"; |
48 | |
46 | |
49 | 1 |
47 | 1 |
50 | } |
48 | } |
51 | |
49 | |
… | |
… | |
81 | $ob->stats->maxgrace, |
79 | $ob->stats->maxgrace, |
82 | $ob->uuid, |
80 | $ob->uuid, |
83 | int AE::now, |
81 | int AE::now, |
84 | ]; |
82 | ]; |
85 | |
83 | |
86 | cf::async { |
|
|
87 | my $guard = cf::lock_acquire "highscore:check"; |
84 | my $guard = cf::lock_acquire "highscore"; |
88 | |
85 | |
89 | # load hiscore, patch, save hiscore |
86 | cf::get_slot 0.01, 0, "highscore check"; |
90 | my $hiscore = load_hiscore; |
|
|
91 | |
87 | |
92 | cf::get_slot 0.01, 0, "highscore check"; |
88 | my ($pre, $ins, $save); |
93 | |
89 | |
94 | my ($pre, $ins, $save); |
90 | pop @$HIGHSCORE while @$HIGHSCORE > $HIGHSCORE_ENTRIES; |
95 | |
91 | |
96 | pop @$hiscore while @$hiscore > $HIGHSCORE_ENTRIES; |
92 | # find previous entry, and new position |
97 | |
93 | |
98 | # find previous entry, and new position |
94 | for (0 .. $#$HIGHSCORE) { |
|
|
95 | $pre //= $_ if $HIGHSCORE->[$_][0] eq $score->[0]; |
|
|
96 | $ins //= $_ if $HIGHSCORE->[$_][2] < $score->[2]; |
|
|
97 | } |
|
|
98 | cf::cede_to_tick; # we need an "interruptible" block... |
99 | |
99 | |
100 | for (0 .. $#$hiscore) { |
100 | my $msg; |
101 | $pre //= $_ if $hiscore->[$_][0] eq $score->[0]; |
|
|
102 | $ins //= $_ if $hiscore->[$_][2] < $score->[2]; |
|
|
103 | } |
|
|
104 | cf::cede_to_tick; # we need an "interruptible" block... |
|
|
105 | |
101 | |
106 | my $msg; |
|
|
107 | |
|
|
108 | if (defined $pre) { |
102 | if (defined $pre) { |
109 | # we are already in the list |
103 | # we are already in the list |
110 | if ($hiscore->[$pre][2] < $score->[2]) { |
104 | if ($HIGHSCORE->[$pre][2] < $score->[2]) { |
111 | $msg = "T<You beat your last score!>\n\n" |
105 | $msg = "T<You beat your last score!>\n\n" |
112 | . $SEP |
|
|
113 | . $HEADER |
|
|
114 | . $SEP |
|
|
115 | . (fmt $ins, $score) |
|
|
116 | . (fmt $pre, $hiscore->[$pre]) |
|
|
117 | . $SEP; |
|
|
118 | |
|
|
119 | splice @$hiscore, $pre, 1; |
|
|
120 | splice @$hiscore, $ins, 0, $score; |
|
|
121 | $save = 1; |
|
|
122 | } else { |
|
|
123 | $msg = "T<You did not beat your last score.>\n\n" |
|
|
124 | . $SEP |
|
|
125 | . $HEADER |
|
|
126 | . $SEP |
|
|
127 | . (fmt $pre , $hiscore->[$pre]) |
|
|
128 | . (fmt undef, $score) |
|
|
129 | . $SEP; |
|
|
130 | } |
|
|
131 | } elsif (defined $ins or @$hiscore < $HIGHSCORE_ENTRIES) { |
|
|
132 | $ins //= @$hiscore; |
|
|
133 | |
|
|
134 | $msg = "T<You entered the highscore list!>\n\n" |
|
|
135 | . $SEP |
106 | . $SEP |
136 | . $HEADER |
107 | . $HEADER |
137 | . $SEP |
108 | . $SEP |
138 | . (fmt $ins, $score) |
109 | . (fmt $ins, $score) |
|
|
110 | . (fmt $pre, $HIGHSCORE->[$pre]) |
139 | . $SEP; |
111 | . $SEP; |
140 | |
112 | |
|
|
113 | splice @$HIGHSCORE, $pre, 1; |
141 | splice @$hiscore, $ins, 0, $score; |
114 | splice @$HIGHSCORE, $ins, 0, $score; |
142 | $save = 1; |
115 | $save = 1; |
143 | } else { |
116 | } else { |
144 | $msg = "T<You did not enter the highscore list.>\n\n" |
117 | $msg = "T<You did not beat your last score.>\n\n" |
145 | . $SEP |
118 | . $SEP |
146 | . $HEADER |
119 | . $HEADER |
147 | . $SEP |
120 | . $SEP |
148 | . (fmt -1 + (scalar @$hiscore), $hiscore->[-1]) |
121 | . (fmt $pre , $HIGHSCORE->[$pre]) |
149 | . (fmt undef, $score) |
122 | . (fmt undef, $score) |
150 | . $SEP; |
123 | . $SEP; |
151 | } |
124 | } |
|
|
125 | } elsif (defined $ins or @$HIGHSCORE < $HIGHSCORE_ENTRIES) { |
|
|
126 | $ins //= @$HIGHSCORE; |
152 | |
127 | |
153 | cf::info $msg;#d# remove once working stable |
128 | $msg = "T<You entered the highscore list!>\n\n" |
|
|
129 | . $SEP |
|
|
130 | . $HEADER |
|
|
131 | . $SEP |
|
|
132 | . (fmt $ins, $score) |
|
|
133 | . $SEP; |
154 | |
134 | |
155 | $ob->send_msg ($SCORE_CHANNEL => $msg, cf::NDI_CLEAR); |
135 | splice @$HIGHSCORE, $ins, 0, $score; |
|
|
136 | $save = 1; |
|
|
137 | } else { |
|
|
138 | $msg = "T<You did not enter the highscore list.>\n\n" |
|
|
139 | . $SEP |
|
|
140 | . $HEADER |
|
|
141 | . $SEP |
|
|
142 | . (fmt -1 + (scalar @$HIGHSCORE), $HIGHSCORE->[-1]) |
|
|
143 | . (fmt undef, $score) |
|
|
144 | . $SEP; |
|
|
145 | } |
156 | |
146 | |
|
|
147 | cf::info $msg;#d# remove once working stable |
|
|
148 | |
|
|
149 | $ob->send_msg ($SCORE_CHANNEL => $msg, cf::NDI_CLEAR); |
|
|
150 | |
157 | if ($save) { |
151 | if ($save) { |
158 | save_hiscore $hiscore |
152 | save_highscore |
159 | or die "unable to write highscore file: $!"; |
153 | or die "unable to write highscore file: $!"; |
|
|
154 | |
|
|
155 | if (defined $EXPORT_HIGHSCORE) { |
|
|
156 | cf::get_slot 0.05, 0, "highscore export"; |
|
|
157 | |
|
|
158 | my $rank; |
|
|
159 | my @highscore = map { |
|
|
160 | my ($name, $title, $exp, $killer, $map, $hp, $sp, $grace) = @$_; |
|
|
161 | [++$rank, $name, $title, (cf::exp_to_level $exp), $exp, $killer, $map, $hp, $sp, $grace] |
|
|
162 | } @$HIGHSCORE; |
|
|
163 | |
|
|
164 | cf::replace_file $EXPORT_HIGHSCORE, cf::encode_json { |
|
|
165 | version => 1, |
|
|
166 | date => $cf::NOW, |
|
|
167 | data => \@highscore, |
|
|
168 | }; |
160 | } |
169 | } |
161 | }; |
170 | } |
162 | } |
171 | } |
163 | |
172 | |