1 |
#! perl |
2 |
|
3 |
# this plug-in prefetches maps. everytime a player enters a map, |
4 |
# it will asynchronously prefetch files from disk (it will not load them |
5 |
# into the server, but into the OS cache only). |
6 |
|
7 |
use Errno (); |
8 |
use Time::HiRes; |
9 |
use Fcntl; |
10 |
use IO::AIO; |
11 |
|
12 |
# find all potential exit paths, this is slow, so this info is cached |
13 |
sub find_exits { |
14 |
my ($map) = @_; |
15 |
|
16 |
my %exit; |
17 |
|
18 |
for my $x (0 .. $map->width - 1) { |
19 |
for my $y (0 .. $map->height - 1) { |
20 |
for (grep $_->type == 66, $map->at ($x, $y)) { |
21 |
my $path = $_->slaying; |
22 |
|
23 |
next if 3 > length $path; |
24 |
|
25 |
$path = cf::maps_directory cf::path_combine_and_normalize $map->path, $path; |
26 |
|
27 |
$exit{$path}++; |
28 |
} |
29 |
} |
30 |
} |
31 |
|
32 |
[keys %exit] |
33 |
} |
34 |
|
35 |
my %MAP_EXITS; |
36 |
my %MAP_TIMEOUT; |
37 |
|
38 |
sub on_mapenter { |
39 |
my ($ob) = @_; |
40 |
|
41 |
my $exit = $MAP_EXITS{$ob->map->path} ||= find_exits $ob->map; |
42 |
|
43 |
my $NOW = Time::HiRes::time; |
44 |
|
45 |
for my $path (@$exit) { |
46 |
next if $MAP_TIMEOUT{$path} > $NOW; |
47 |
|
48 |
if (my $map = cf::map::has_been_loaded $path) { |
49 |
next if $map->in_memory == cf::MAP_IN_MEMORY; |
50 |
|
51 |
$path = $map->tmppath if $map->in_memory == cf::MAP_SWAPPED; |
52 |
} |
53 |
|
54 |
$MAP_TIMEOUT{$path} = $NOW + 60 + rand 60; |
55 |
|
56 |
aio_open $path, O_RDONLY, 0, sub { |
57 |
my ($fh) = @_ |
58 |
or return; |
59 |
aio_readahead $fh, 0, -s $fh, sub { |
60 |
my $time = Time::HiRes::time - $NOW; |
61 |
warn "LONG PREFETCH $path $time\n" if $time > 0.15; |
62 |
}; |
63 |
}; |
64 |
} |
65 |
} |
66 |
|
67 |
sub on_clock { |
68 |
# boy how I hate polling |
69 |
IO::AIO::poll_cb; |
70 |
} |
71 |
|
72 |
|