ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC.pm
Revision: 1.137
Committed: Sat Dec 9 21:26:45 2006 UTC (17 years, 5 months ago) by root
Branch: MAIN
Changes since 1.136: +1 -2 lines
Log Message:
- do no longer depend on Scalar::Util for weaken
- use a different, cleaner, more correct and less efficient event propagation mechanism
- this fixes the segfault bug in map $_.

File Contents

# Content
1 =head1 NAME
2
3 CFPlus - undocumented utility garbage for our crossfire client
4
5 =head1 SYNOPSIS
6
7 use CFPlus;
8
9 =head1 DESCRIPTION
10
11 =over 4
12
13 =cut
14
15 package CFPlus;
16
17 use Carp ();
18
19 BEGIN {
20 $VERSION = '0.97';
21
22 use XSLoader;
23 XSLoader::load "CFPlus", $VERSION;
24 }
25
26 use utf8;
27
28 use AnyEvent ();
29 use BerkeleyDB;
30 use Pod::POM ();
31 use File::Path ();
32 use Storable (); # finally
33
34 BEGIN {
35 use Crossfire::Protocol::Base ();
36 *to_json = \&Crossfire::Protocol::Base::to_json;
37 *from_json = \&Crossfire::Protocol::Base::from_json;
38 }
39
40 =item guard { BLOCK }
41
42 Returns an object that executes the given block as soon as it is destroyed.
43
44 =cut
45
46 sub guard(&) {
47 bless \(my $cb = $_[0]), "CFPlus::Guard"
48 }
49
50 sub CFPlus::Guard::DESTROY {
51 ${$_[0]}->()
52 }
53
54 =item shorten $string[, $maxlength]
55
56 =cut
57
58 sub shorten($;$) {
59 my ($str, $len) = @_;
60 substr $str, $len, (length $str), "..." if $len + 3 <= length $str;
61 $str
62 }
63
64 sub asxml($) {
65 local $_ = $_[0];
66
67 s/&/&amp;/g;
68 s/>/&gt;/g;
69 s/</&lt;/g;
70
71 $_
72 }
73
74 sub socketpipe() {
75 socketpair my $fh1, my $fh2, Socket::AF_UNIX, Socket::SOCK_STREAM, Socket::PF_UNSPEC
76 or die "cannot establish bidiretcional pipe: $!\n";
77
78 ($fh1, $fh2)
79 }
80
81 sub background(&;&) {
82 my ($bg, $cb) = @_;
83
84 my ($fh_r, $fh_w) = CFPlus::socketpipe;
85
86 my $pid = fork;
87
88 if (defined $pid && !$pid) {
89 local $SIG{__DIE__};
90
91 open STDOUT, ">&", $fh_w;
92 open STDERR, ">&", $fh_w;
93 close $fh_r;
94 close $fh_w;
95
96 $| = 1;
97
98 eval { $bg->() };
99
100 if ($@) {
101 my $msg = $@;
102 $msg =~ s/\n+/\n/;
103 warn "FATAL: $msg";
104 CFPlus::_exit 1;
105 }
106
107 # win32 is fucked up, of course. exit will clean stuff up,
108 # which destroys our database etc. _exit will exit ALL
109 # forked processes, because of the dreaded fork emulation.
110 CFPlus::_exit 0;
111 }
112
113 close $fh_w;
114
115 my $buffer;
116
117 my $w; $w = AnyEvent->io (fh => $fh_r, poll => 'r', cb => sub {
118 unless (sysread $fh_r, $buffer, 4096, length $buffer) {
119 undef $w;
120 $cb->();
121 return;
122 }
123
124 while ($buffer =~ s/^(.*)\n//) {
125 my $line = $1;
126 $line =~ s/\s+$//;
127 utf8::decode $line;
128 if ($line =~ /^\x{e877}json_msg (.*)$/s) {
129 $cb->(from_json $1);
130 } else {
131 ::message ({
132 markup => "background($pid): " . CFPlus::asxml $line,
133 });
134 }
135 }
136 });
137 }
138
139 sub background_msg {
140 my ($msg) = @_;
141
142 $msg = "\x{e877}json_msg " . to_json $msg;
143 $msg =~ s/\n//g;
144 utf8::encode $msg;
145 print $msg, "\n";
146 }
147
148 package CFPlus::Database;
149
150 our @ISA = BerkeleyDB::Btree::;
151
152 sub get($$) {
153 my $data;
154
155 $_[0]->db_get ($_[1], $data) == 0
156 ? $data
157 : ()
158 }
159
160 my %DB_SYNC;
161
162 sub put($$$) {
163 my ($db, $key, $data) = @_;
164
165 my $hkey = $db + 0;
166 CFPlus::weaken $db;
167 $DB_SYNC{$hkey} ||= AnyEvent->timer (after => 5, cb => sub {
168 delete $DB_SYNC{$hkey};
169 $db->db_sync if $db;
170 });
171
172 $db->db_put ($key => $data)
173 }
174
175 package CFPlus;
176
177 sub find_rcfile($) {
178 my $path;
179
180 for (grep !ref, @INC) {
181 $path = "$_/CFPlus/resources/$_[0]";
182 return $path if -r $path;
183 }
184
185 die "FATAL: can't find required file $_[0]\n";
186 }
187
188 sub read_cfg {
189 my ($file) = @_;
190
191 open my $fh, $file
192 or return;
193
194 local $/;
195 my $CFG = <$fh>;
196
197 if ($CFG =~ /^---/) { ## TODO compatibility cruft, remove
198 require YAML;
199 utf8::decode $CFG;
200 $::CFG = YAML::Load ($CFG);
201 } elsif ($CFG =~ /^\{/) {
202 $::CFG = from_json $CFG;
203 } else {
204 $::CFG = eval $CFG; ## todo comaptibility cruft
205 }
206 }
207
208 sub write_cfg {
209 my ($file) = @_;
210
211 $::CFG->{VERSION} = $::VERSION;
212
213 open my $fh, ">:utf8", $file
214 or return;
215 print $fh to_json $::CFG;
216 }
217
218 sub http_proxy {
219 my @proxy = win32_proxy_info;
220
221 if (@proxy) {
222 "http://" . (@proxy < 2 ? "" : @proxy < 3 ? "$proxy[1]\@" : "$proxy[1]:$proxy[2]\@") . $proxy[0]
223 } elsif (exists $ENV{http_proxy}) {
224 $ENV{http_proxy}
225 } else {
226 ()
227 }
228 }
229
230 sub set_proxy {
231 my $proxy = http_proxy
232 or return;
233
234 $ENV{http_proxy} = $proxy;
235 }
236
237 sub lwp_useragent {
238 require LWP::UserAgent;
239
240 CFPlus::set_proxy;
241
242 my $ua = LWP::UserAgent->new (
243 agent => "cfplus $VERSION",
244 keep_alive => 1,
245 env_proxy => 1,
246 timeout => 30,
247 );
248 }
249
250 sub lwp_check($) {
251 my ($res) = @_;
252
253 $res->is_error
254 and die $res->status_line;
255
256 $res
257 }
258
259 our $DB_ENV;
260 our $DB_STATE;
261
262 sub db_table($) {
263 my ($table) = @_;
264
265 $table =~ s/([^a-zA-Z0-9_\-])/sprintf "=%x=", ord $1/ge;
266
267 new CFPlus::Database
268 -Env => $DB_ENV,
269 -Filename => $table,
270 # -Filename => "database",
271 # -Subname => $table,
272 -Property => DB_CHKSUM,
273 -Flags => DB_CREATE | DB_UPGRADE,
274 or die "unable to create/open database table $_[0]: $BerkeleyDB::Error"
275 }
276
277 our $DB_HOME = "$Crossfire::VARDIR/cfplus";
278
279 sub open_db {
280 use strict;
281
282 mkdir $DB_HOME, 0777;
283 my $recover = $BerkeleyDB::db_version >= 4.4
284 ? eval "DB_REGISTER | DB_RECOVER"
285 : 0;
286
287 $DB_ENV = new BerkeleyDB::Env
288 -Home => $DB_HOME,
289 -Cachesize => 1_000_000,
290 -ErrFile => "$DB_HOME/errorlog.txt",
291 # -ErrPrefix => "DATABASE",
292 -Verbose => 1,
293 -Flags => DB_CREATE | DB_RECOVER | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | $recover,
294 -SetFlags => DB_AUTO_COMMIT | DB_LOG_AUTOREMOVE,
295 or die "unable to create/open database home $DB_HOME: $BerkeleyDB::Error";
296
297 $DB_STATE = db_table "state";
298
299 1
300 }
301
302 unless (eval { open_db }) {
303 File::Path::rmtree $DB_HOME;
304 open_db;
305 }
306
307 package CFPlus::Layout;
308
309 $CFPlus::OpenGL::SHUTDOWN_HOOK{"CFPlus::Layout"} = sub {
310 reset_glyph_cache;
311 };
312
313 1;
314
315 =back
316
317 =head1 AUTHOR
318
319 Marc Lehmann <schmorp@schmorp.de>
320 http://home.schmorp.de/
321
322 =cut
323