ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/ext/map-scheduler.ext
Revision: 1.35
Committed: Sun Jun 10 04:05:47 2007 UTC (16 years, 11 months ago) by root
Branch: MAIN
Changes since 1.34: +31 -7 lines
Log Message:
go through all per-player maps on startup and load those
that have a resettable base map. this is similar to going tghrough
all temp amps and loading them so the server can reset them.

this is quite a high overhead compared to e.g. putting reswettable
per-player maps into the tmp directory, too, but we might want to iterate
over all players and their maps anyways at some point, to upgrade them to
new formats etc. or to gather global statistics.

File Contents

# User Rev Content
1 root 1.22 #! perl # MANDATORY
2 root 1.1
3     # this extension swaps out maps and resets them, in essence managing
4     # the reset/swap policy of the server.
5 root 1.18 # it also contains the map prefetching logic
6 root 1.1
7 root 1.24 #our $DEACTIVATE_TIMEOUT = 20; # number of seconds after which maps get deactivated to save cpu
8 root 1.34 our $SWAP_TIMEOUT = $cf::CFG{swap_timeout} || 300; # number of seconds after which inactive maps get swapped out
9     our $SCHEDULE_INTERVAL = $cf::CFG{schedule_interval} || .8; # time the map scheduler sleeps between runs
10     our $SAVE_TIMEOUT = $cf::CFG{save_timeout} || 30; # save maps every n seconds
11     our $SWAP_LOAD1 = $cf::CFG{swap_load1} || .1; # start aggressively swapping at this load
12     our $SWAP_LOAD2 = $cf::CFG{swap_load2} || .4; # swap as fast as possible at this load
13 root 1.1
14 root 1.17 cf::async_ext {
15 root 1.12 $Coro::current->prio (Coro::PRIO_MIN);
16 root 1.35
17 root 1.14 # load the header of swapped-out maps.
18 root 1.12 # this is not a correctness issue, it simply saves diskspace
19     # because old files will get cleaned up on reset time
20 root 1.35 Coro::Timer::sleep 0.25;
21 root 1.12
22 root 1.35 for my $map (@{ cf::map::tmp_maps or [] }) {
23 root 1.12 cf::map::find $map;
24 root 1.35 cf::wait_for_tick; Coro::cede;
25 root 1.12 }
26    
27     #TODO: should also preload random maps...
28 root 1.35
29     # now hunt for resettable per-player maps
30     for my $login (@{ cf::player::list_logins or [] }) {
31     for my $path (@{ cf::player::maps $login or [] }) {
32     Coro::cede;
33    
34     $path =~ /^~[^\/]+(\/.*)$/
35     or next; # doh
36    
37     my $base = cf::map::find $1;
38    
39     # skip maps without base maps on the assumption
40     # that those are old, unresettable maps
41     next unless $base;
42    
43     # skip unresettable maps, for speed
44     next if $base->{deny_reset};
45    
46     my $map = cf::map::find $path;
47     cf::wait_for_tick;
48    
49     if ($map->{deny_reset}) {
50     warn "found noreset map with resettable base map, resetting: $path\n";
51     delete $map->{deny_reset};
52     }
53     }
54     }
55 root 1.12 };
56    
57 root 1.17 our $SCHEDULER = cf::async_ext {
58 root 1.1 while () {
59 root 1.34 Coro::Event::do_timer (after => $SCHEDULE_INTERVAL)
60     unless $cf::LOADAVG > $SWAP_LOAD2;
61 root 1.1
62 root 1.9 # this weird form of iteration over values is used because
63     # the hash changes underneath us frequently, and for
64     # keeps a direct reference to the value without (in 5.8 perls)
65     # keeping a reference, so this is prone to crashes or worse.
66 root 1.8 my @maps = keys %cf::MAP;
67     for (@maps) {
68     my $map = $cf::MAP{$_}
69     or next;
70     $map->valid or next;
71 root 1.9
72 root 1.1 eval {
73     # not yet, because maps might become visible to players nearby
74 root 1.3 # we need to remove the map from %cf::MAP and all tiled map links
75 root 1.1 # if ($last_access + $DEACTIVATE_TIMEOUT <= $cf::RUNTIME) {
76     # $map->deactivate;
77     # delete $map->{active};
78     # }
79     if ($map->should_reset) {
80     $map->reset;
81 root 1.3 } elsif ($map->in_memory == cf::MAP_IN_MEMORY) {
82 root 1.34 my $max_idle = cf::clamp +(cf::lerp $cf::LOADAVG, $SWAP_LOAD1, $SWAP_LOAD2, $SWAP_TIMEOUT, 0), 0, $SWAP_TIMEOUT;
83    
84 root 1.26 if ($map->last_access + $max_idle <= $cf::RUNTIME && !$map->players) {
85 root 1.25 $map->swap_out;
86 root 1.34 cf::wait_for_tick unless $cf::LOADAVG > $SWAP_LOAD2;
87 root 1.3 } elsif ($map->{last_save} + $SAVE_TIMEOUT <= $cf::RUNTIME) {
88 root 1.25 $map->save;
89 root 1.34 cf::wait_for_tick unless $cf::LOADAVG > $SWAP_LOAD2;
90 root 1.3 }
91 root 1.1 }
92     };
93     warn $@ if $@;
94 root 1.34 cf::cede_to_tick;
95 root 1.8 };
96 root 1.1 }
97     };
98 root 1.5
99 root 1.1 $SCHEDULER->prio (-2);
100