ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/maps/perl/map-prefetch.ext
Revision: 1.13
Committed: Sun Sep 17 18:17:17 2006 UTC (17 years, 8 months ago) by root
Branch: MAIN
Changes since 1.12: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.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 root 1.6 Event->io (fd => IO::AIO::poll_fileno,
13     poll => 'r',
14     cb => \&IO::AIO::poll_cb);
15    
16 root 1.2 # find all potential exit paths, this is slow, so this info is cached
17 root 1.1 sub find_exits {
18     my ($map) = @_;
19    
20     my %exit;
21    
22 root 1.6 # normal exits
23 root 1.1 for my $x (0 .. $map->width - 1) {
24     for my $y (0 .. $map->height - 1) {
25     for (grep $_->type == 66, $map->at ($x, $y)) {
26     my $path = $_->slaying;
27    
28     next if 3 > length $path;
29    
30 root 1.6 # TODO: improve unique exit detection etc.
31     $exit{cf::maps_directory cf::path_combine_and_normalize $map->path, $path}++;
32 root 1.1 }
33     }
34     }
35    
36 root 1.6 # tiled maps
37     for (0..3) {
38     my $path = $map->tile_path ($_)
39     or next;
40    
41     $exit{cf::maps_directory cf::path_combine_and_normalize $map->path, $path}++;
42     }
43    
44 root 1.1 [keys %exit]
45     }
46    
47 root 1.6 my $PREFETCHING;
48 root 1.5 my @PREFETCH;
49 root 1.6 my %FILE_TIMEOUT;
50 root 1.1
51 root 1.6 sub _prefetch;
52 root 1.1
53 root 1.6 my $empty_cb = sub { };
54 root 1.1
55 root 1.6 sub load_file {
56     my ($path, $cb) = @_;
57 root 1.1
58 root 1.6 my $NOW = Time::HiRes::time;
59 root 1.1
60 root 1.6 aio_open $path, O_RDONLY, 0, sub {
61     my $fh = shift
62     or return $cb->(), _prefetch;
63 root 1.1
64 root 1.6 aio_readahead $fh, 0, -s $fh, sub {
65     my $time = Time::HiRes::time - $NOW;
66     warn "LONG PREFETCH $path $time\n" if $time > 0.3;
67 root 1.5
68 root 1.6 $cb->(), _prefetch;
69 root 1.1 };
70 root 1.6 };
71     }
72    
73     sub prefetch($$;$) {
74     my ($type, $path, $cb) = @_;
75    
76     push @PREFETCH, [$type, $path, $cb || $empty_cb];
77     _prefetch unless $PREFETCHING;
78     }
79    
80     sub _prefetch {
81     $PREFETCHING = 1;
82    
83     while (@PREFETCH) {
84     my ($type, $path, $cb) = @{ shift @PREFETCH };
85    
86     my $NOW = Time::HiRes::time;
87     $cb->(), next if $FILE_TIMEOUT{$path} > $NOW;
88     $FILE_TIMEOUT{$path} = $NOW + 60 + rand 60;
89 root 1.5
90 root 1.6 if ($type eq "map") {
91     if (my $map = cf::map::has_been_loaded $path) {
92     $cb->(), next if $map->in_memory == cf::MAP_IN_MEMORY;
93    
94 root 1.8 prefetch file => $map->tmpname
95 root 1.6 if $map->in_memory == cf::MAP_SWAPPED;
96     }
97     }
98    
99     load_file $path, $cb;
100     return;
101 root 1.1 }
102 root 1.6
103     $PREFETCHING = 0;
104 root 1.1 }
105    
106 root 1.5 my %MAP_EXITS;
107    
108 root 1.7 sub prefetch_map($) {
109 root 1.6 my ($map) = @_;
110    
111     my $exit = $MAP_EXITS{$map->path} ||= find_exits $map;
112     prefetch map => $_ for @$exit;
113     }
114    
115 root 1.11 cf::attach_to_players prio => -900,
116 root 1.12 on_map_change => sub {
117 root 1.13 my ($pl, $new, $x, $x) = @_;
118 root 1.11
119 root 1.12 prefetch_map $new;
120 root 1.11 },
121     ;
122 root 1.5
123 root 1.10 if (0) { #test#
124 root 1.6 # prefetch a few players/second
125     {
126     my @players;
127    
128     Event->timer (interval => 0.2, cb => sub {
129     @players = map $_->ob->name, cf::player::list unless @players;
130     my $player = cf::player::find pop @players
131     or return;
132    
133 root 1.7 if (my $map = $player->ob->map) {
134     prefetch_map $map;
135     }
136    
137 root 1.6 prefetch map => +($player->get_savebed)[0];
138     });
139 root 1.5 }
140    
141 root 1.6 # prefetch all .pl files every few minutes (thats only a "few" megabytes)
142     Event->timer (after => 1, interval => 600, cb => sub {
143     my $playerdir = cf::localdir . "/" . cf::playerdir;
144    
145     aio_readdir $playerdir, sub {
146     my ($players) = @_;
147     my $prefetch; $prefetch = sub {
148     my $player = pop @$players
149     or return;
150    
151     load_file "$playerdir/$player/$player.pl", $prefetch;
152     };
153    
154     $prefetch->(); $prefetch->();
155     };
156     });
157 root 1.9 }
158 root 1.6
159 root 1.1
160