#! perl # mandatory use Coro::AIO; cf::map->register (qr{^\?random/([0-9a-f]{32})}); sub init { my ($self) = @_; $self->{random_id} = $1; if (0 < Coro::AIO::aio_load "$cf::RANDOMDIR/$self->{random_id}.meta", my $data) { $self->{random} = cf::decode_json $data; $self->{random}{custom} ||= "$self->{random}{origin_map}+$self->{random}{origin_x}+$self->{random}{origin_y}"; } else { warn "unable to read meta file for $self->{random_id}\n"; return 0; } 1 } sub thawer_merge { # we have to keep some variables in memory intact local $_[0]{random_id}; local $_[0]{random}; $_[0]->SUPER::thawer_merge ($_[1]); } sub visible_name { my ($self) = @_; my $rmp = $self->{random}; "random map at $rmp->{custom}, level $rmp->{dungeon_level}" } sub save_path { my ($self) = @_; sprintf "%s/%s.map", $cf::RANDOMDIR, $self->{random_id} } sub uniq_path { undef } sub load_header_orig { my ($self) = @_; return unless $self->{random}; $self->generate_random_map ($self->{random}); $self->activate; 1 } sub clean_random_maps { my $files = Coro::AIO::aio_readdirx $cf::RANDOMDIR, IO::AIO::READDIR_STAT_ORDER or return; my $META_TIMEOUT = $cf::CFG{map_random_meta_timeout} || 86400 * 7; for my $file (@$files) { next unless $file =~ /\.meta$/; Coro::AIO::aio_stat "$cf::RANDOMDIR/$file" and next; my $age = $cf::NOW - (stat _)[8]; if ($age > $META_TIMEOUT) { warn "resetting random meta data for $file"; IO::AIO::aio_unlink "$cf::RANDOMDIR/$file"; } } } # called by the random map generator sub find_style_; sub find_style_($$) { my ($path, $difficulty) = @_; my $map; $map = cf::map::find $path unless aio_stat "$cf::MAPDIR/$path.map"; unless ($map) { # search files and/or dirs if (my ($dirs, $nondirs) = aio_scandir "$cf::MAPDIR/$path/", 1) { my @entries = sort grep s/\.map$//, @$nondirs; if ($difficulty < 0) { # pick a fully random map, but only a map, do not recurse $map = cf::map::find "$path/$entries[cf::rmg_rndm scalar @entries]" if @entries; } else { # pick a map with nearest difficulty value ("mapname_.map") @entries = sort @$dirs unless @entries; my $min_diff = 1e99; for my $name (@entries) { if ($name =~ /_(\d+)$/) { my $diff = abs $difficulty - $1 + 0.5; # prefer the more difficult version ($map, $min_diff) = ($name, $diff) if $diff < $min_diff; } } unless ($map) { # no map with given pattern found, choose a random map $map = $entries[cf::rmg_rndm scalar @entries]; } $map = find_style_ "$path/$map", $difficulty if $map; } } } $map } sub find_style($$$) { my ($dir, $name, $difficulty) = @_; cf::cede_to_tick; my $map = find_style_ $name ? "$dir/$name" : $dir, $difficulty; if ($map) { $map->load; $map->deactivate; } #warn "return $dir,$name,$difficulty => $map\n" if $difficulty >= 0;#d# $map } # clean up old temp maps regularly our $CLEAN_RANDOM_MAPS = cf::periodic 3600, Coro::unblock_sub { clean_random_maps; }; # map generator stresstest, NEVER enable under normal circumstances if ($ENV{STRESSTEST}) { cf::async { my $seed = 0; while () { my $map = cf::map::new; $map->generate_random_map ({ region => "scorn", random_seed => $seed++, xsize => (int rand 100) + 1, ysize => (int rand 100) + 1, }); warn sprintf "%d: %dx%d o# %d\n", $seed, $map->width, $map->height, &cf::object::objects_size;#d# $map->destroy; } }; } # prefetch test, load some ocean-maps if (0) { cf::async { # 0.58 Coro::Timer::sleep 2; for my $x (200..219) { for my $y (200..219) { (cf::map::find "/world/world_$x\_$y")->load; } } }; } # save test if (1) { cf::async { # 0.093 Coro::Timer::sleep 2; my $map = cf::map::find "/mlab/citydeclouds2"; $map->load_header; $map->load; $map->post_load_original; my $m=100; for (1..50) { my $t=EV::time; $map->_save_objects ("/tmp/x", cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES); $t = EV::time-$t; $m=$t if $m>$t; warn $m; } }; } 1