#! perl # this plug-in prefetches maps. everytime a player enters a map, # it will asynchronously prefetch files from disk (it will not load them # into the server, but into the OS cache only). use Errno (); use Time::HiRes; use Fcntl; use IO::AIO; # find all potential exit paths, this is slow, so this info is cached sub find_exits { my ($map) = @_; my %exit; for my $x (0 .. $map->width - 1) { for my $y (0 .. $map->height - 1) { for (grep $_->type == 66, $map->at ($x, $y)) { my $path = $_->slaying; next if 3 > length $path; $path = cf::maps_directory cf::path_combine_and_normalize $map->path, $path; $exit{$path}++; } } } [keys %exit] } my @PREFETCH; my %MAP_TIMEOUT; sub prefetch; sub prefetch { while (my $path = shift @PREFETCH) { my $NOW = Time::HiRes::time; next if $MAP_TIMEOUT{$path} > $NOW; $MAP_TIMEOUT{$path} = $NOW + 60 + rand 60; if (my $map = cf::map::has_been_loaded $path) { next if $map->in_memory == cf::MAP_IN_MEMORY; $path = $map->tmppath if $map->in_memory == cf::MAP_SWAPPED; } aio_open $path, O_RDONLY, 0, sub { my $fh = shift or return; aio_readahead $fh, 0, -s $fh, sub { my $time = Time::HiRes::time - $NOW; warn "LONG PREFETCH $path $time\n" if $time > 0.3; prefetch; }; }; last; } } my %MAP_EXITS; sub on_mapenter { my ($ob) = @_; my $exit = $MAP_EXITS{$ob->map->path} ||= find_exits $ob->map; push @PREFETCH, @$exit; prefetch; } sub on_clock { # boy how I hate polling IO::AIO::poll_cb; }