ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/ext/map-tags.ext
Revision: 1.17
Committed: Fri Oct 23 03:08:34 2009 UTC (14 years, 7 months ago) by root
Branch: MAIN
Changes since 1.16: +3 -14 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #! perl # mandatory
2    
3 root 1.3 our $SCHEDULE_INTERVAL = $cf::CFG{extractor_schedule_interval} || 3600;
4 root 1.1
5     use JSON::XS;
6    
7 root 1.16 our $db_mapinfo = cf::sync_job { cf::db_table "tag-mapinfo" }; # info/cache for maps
8     our $db_target = cf::sync_job { cf::db_table "tag-target" }; # tag => maps
9 root 1.1
10     sub remove_tag_target {
11     my ($txn, $tag, $target) = @_;
12     # - U O
13    
14     utf8::encode $tag;
15     BDB::db_get $db_target, $txn, $tag, my $data;
16     my @tags = split /\x00/, $data;
17     @tags = grep $_ ne $target, @tags;
18    
19     if (@tags) {
20     BDB::db_put $db_target, $txn, $tag, join "\x00", @tags;
21     } else {
22     BDB::db_del $db_target, $txn, $tag;
23     }
24     }
25    
26     sub add_tag_target {
27     my ($txn, $tag, $target) = @_;
28    
29     utf8::encode $tag;
30     BDB::db_put $db_target, $txn, $tag, my $data;
31     my @tags = split /\x00/, $data;
32     push @tags, $target;
33     BDB::db_put $db_target, $txn, $tag, join "\x00", @tags;
34     }
35    
36     sub scan_map($$) {
37     my ($key, $file) = @_;
38     # unicode key, octets file
39    
40     Coro::AIO::aio_stat $file
41     and next;
42    
43     my $hash = join ",", 1, (stat _)[7,9], $file;
44    
45     my $old_tags;
46    
47     my $txn = $cf::DB_ENV->txn_begin;
48    
49     utf8::encode $key;
50     BDB::db_get $db_mapinfo, $txn, $key, my $data;
51    
52     unless ($!) {
53 root 1.13 $data = decode_json $data;
54 root 1.1 return if $data->{hash} eq $hash;
55     $old_tags = $data->{tags};
56     }
57    
58     $old_tags ||= [];
59    
60     my $f = new_from_file cf::object::thawer $file
61     or return;
62    
63     my @tags = sort $f->extract_tags;
64 root 1.13 $data = encode_json { hash => $hash, tags => \@tags };
65 root 1.1
66     BDB::db_put $db_mapinfo, $txn, $key, $data;
67    
68     # 1. remove tags no longer existing
69     for my $tag (@$old_tags) {
70     next if grep $_ eq $tag, @tags;
71     remove_tag_target $txn, $tag, $key;
72     }
73    
74     # 2. add tags that are new
75     for my $tag (@tags) {
76     next if grep $_ eq $tag, @$old_tags;
77     add_tag_target $txn, $tag, $key;
78     }
79    
80     # we don't actually care if it succeeds or not, as we
81     # will just retry an hour later
82 root 1.7 BDB::db_txn_finish $txn;
83 root 1.1
84 root 1.8 warn "tag-updated $file (= $key) <@tags>\n"
85     if @tags;
86 root 1.1 }
87    
88     sub scan_static {
89 root 1.17 my $maps = cf::map::static_maps;
90 root 1.1
91 root 1.17 scan_map "s$_", "$cf::MAPDIR$_.map";
92     for @$maps;
93 root 1.1 }
94    
95 root 1.5 sub reload {
96 root 1.8 my $guard = cf::lock_acquire "map-tags::reload";
97    
98 root 1.10 my $start = EV::time;
99 root 1.1
100 root 1.5 # 1. check for maps no longer existing
101 root 1.6 {
102     my @delkeys;
103    
104     my $cursor = $db_mapinfo->cursor;
105     for (;;) {
106     BDB::db_c_get $cursor, my $key, my $data, BDB::NEXT;
107     last if $!;
108    
109 root 1.13 my $data = JSON::XS::decode_json $data;
110 root 1.6 my ($ver, undef, undef, $path) = split /,/, $data->{hash}, 4;
111     push @delkeys, [$key, $data->{tags}]
112     if $ver != 1 || Coro::AIO::aio_stat $path;
113     }
114     BDB::db_c_close $cursor;
115    
116     for (@delkeys) {
117     my ($key, $tags) = @$_;
118     my $txn = $cf::DB_ENV->txn_begin;
119     BDB::db_del $db_mapinfo, $txn, $key;
120     for my $tag (@{ $tags || [] }) {
121     remove_tag_target $txn, $tag, $key;
122     }
123 root 1.7 BDB::db_txn_finish $txn;
124 root 1.6 }
125     }
126 root 1.1
127 root 1.5 # 2. scan all static maps
128     scan_static $cf::MAPDIR, "/";
129 root 1.1
130 root 1.5 # 3. scan all dynamic maps
131     for my $path (@{ cf::map::tmp_maps or [] }, @{ cf::map::random_maps or [] }) {
132 root 1.1 # my $map = cf::map::find $path;
133     # extract_map_tags "t/$map", $path;
134 root 1.5 }
135    
136 root 1.1 # now hunt for all per-player maps
137     # scan_dir $cf::PLAYERDIR
138     # for my $login (@{ cf::player::list_logins or [] }) {
139     # for my $path (@{ cf::player::maps $login or [] }) {
140     # cf::cede_to_tick;
141     #
142     # $path =~ /^~[^\/]+(\/.*)$/
143     # or next; # doh
144     #
145     # my $base = cf::map::find $1;
146     #
147     # # skip maps without base maps on the assumption
148     # # that those are old, unresettable maps
149     # next unless $base;
150     #
151     # # skip unresettable maps, for speed
152     # next if $base->{deny_reset};
153     #
154     # my $map = cf::map::find $path;
155     #
156     # if ($map->{deny_reset}) {
157     # warn "found noreset map with resettable base map, resetting: $path\n";
158     # delete $map->{deny_reset};
159     # }
160     # }
161     # }
162    
163 root 1.10 warn sprintf "map-tag scan (%fs)", EV::time - $start;
164 root 1.5 }
165    
166 root 1.11 our $RELOAD_SCHEDULER = cf::periodic $SCHEDULE_INTERVAL, Coro::unblock_sub {
167     $Coro::current->prio (Coro::PRIO_MIN);
168     $Coro::current->desc ("map-tag scanner");
169     reload;
170 root 1.10 };
171 root 1.1
172 root 1.15 cf::post_init {
173     $RELOAD_SCHEDULER->invoke (0); # force at startup
174     };
175 root 1.12
176 root 1.5 # find all objects with the given tag, or at least try to
177 root 1.2 sub find($) {
178     my ($tag) = @_;
179 root 1.1
180 root 1.3 utf8::encode (my $key = $tag);
181     BDB::db_get $db_target, undef, $key, my $data;
182 root 1.2 utf8::decode $data;
183    
184 root 1.4 map { $_->load; $_->find_tagged_objects ($tag) }
185 root 1.2 grep $_,
186     map { cf::map::find $_ }
187     grep s/^s//,
188     split /\x00/, $data
189     }