ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.336
Committed: Tue Aug 21 00:31:18 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.335: +19 -7 lines
Log Message:
fixed exti handling, implemented generic resource request 'framework'

File Contents

# User Rev Content
1 root 1.1 package cf;
2    
3 root 1.96 use utf8;
4     use strict;
5    
6 root 1.1 use Symbol;
7     use List::Util;
8 root 1.250 use Socket;
9 root 1.6 use Storable;
10 root 1.224 use Event;
11 root 1.23 use Opcode;
12     use Safe;
13     use Safe::Hole;
14 root 1.19
15 root 1.332 use Coro 3.64 ();
16 root 1.224 use Coro::State;
17 root 1.250 use Coro::Handle;
18 root 1.96 use Coro::Event;
19     use Coro::Timer;
20     use Coro::Signal;
21     use Coro::Semaphore;
22 root 1.105 use Coro::AIO;
23 root 1.237 use Coro::Storable;
24 root 1.332 use Coro::Util ();
25 root 1.96
26 root 1.288 use JSON::XS 1.4 ();
27 root 1.206 use BDB ();
28 root 1.154 use Data::Dumper;
29 root 1.108 use Digest::MD5;
30 root 1.105 use Fcntl;
31 root 1.227 use YAML::Syck ();
32 root 1.145 use IO::AIO 2.32 ();
33 root 1.32 use Time::HiRes;
34 root 1.208 use Compress::LZF;
35 root 1.302 use Digest::MD5 ();
36 root 1.208
37 root 1.227 # configure various modules to our taste
38     #
39 root 1.237 $Storable::canonical = 1; # reduce rsync transfers
40 root 1.224 Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator
41 root 1.208 Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later
42 root 1.227
43 root 1.224 $Event::Eval = 1; # no idea why this is required, but it is
44 root 1.1
45 root 1.72 # work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
46     $YAML::Syck::ImplicitUnicode = 1;
47    
48 root 1.139 $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
49 root 1.1
50 root 1.227 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
51    
52 root 1.85 our %COMMAND = ();
53     our %COMMAND_TIME = ();
54 root 1.159
55     our @EXTS = (); # list of extension package names
56 root 1.85 our %EXTCMD = ();
57 root 1.287 our %EXTICMD = ();
58 root 1.159 our %EXT_CORO = (); # coroutines bound to extensions
59 root 1.161 our %EXT_MAP = (); # pluggable maps
60 root 1.85
61 root 1.223 our $RELOAD; # number of reloads so far
62 root 1.1 our @EVENT;
63 root 1.253
64     our $CONFDIR = confdir;
65     our $DATADIR = datadir;
66     our $LIBDIR = "$DATADIR/ext";
67     our $PODDIR = "$DATADIR/pod";
68     our $MAPDIR = "$DATADIR/" . mapdir;
69     our $LOCALDIR = localdir;
70     our $TMPDIR = "$LOCALDIR/" . tmpdir;
71     our $UNIQUEDIR = "$LOCALDIR/" . uniquedir;
72     our $PLAYERDIR = "$LOCALDIR/" . playerdir;
73     our $RANDOMDIR = "$LOCALDIR/random";
74     our $BDBDIR = "$LOCALDIR/db";
75 root 1.1
76 root 1.245 our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!)
77 root 1.35 our $TICK_WATCHER;
78 root 1.168 our $AIO_POLL_WATCHER;
79 root 1.214 our $NEXT_RUNTIME_WRITE; # when should the runtime file be written
80 root 1.35 our $NEXT_TICK;
81 root 1.103 our $NOW;
82 root 1.205 our $USE_FSYNC = 1; # use fsync to write maps - default off
83 root 1.35
84 root 1.206 our $BDB_POLL_WATCHER;
85     our $DB_ENV;
86    
87 root 1.70 our %CFG;
88    
89 root 1.84 our $UPTIME; $UPTIME ||= time;
90 root 1.103 our $RUNTIME;
91    
92 root 1.143 our %PLAYER; # all users
93     our %MAP; # all maps
94 root 1.166 our $LINK_MAP; # the special {link} map, which is always available
95 root 1.103
96 root 1.166 # used to convert map paths into valid unix filenames by replacing / by ∕
97     our $PATH_SEP = "∕"; # U+2215, chosen purely for visual reasons
98    
99 root 1.265 our $LOAD; # a number between 0 (idle) and 1 (too many objects)
100     our $LOADAVG; # same thing, but with alpha-smoothing
101     our $tick_start; # for load detecting purposes
102    
103 root 1.103 binmode STDOUT;
104     binmode STDERR;
105    
106     # read virtual server time, if available
107 root 1.253 unless ($RUNTIME || !-e "$LOCALDIR/runtime") {
108     open my $fh, "<", "$LOCALDIR/runtime"
109 root 1.103 or die "unable to read runtime file: $!";
110     $RUNTIME = <$fh> + 0.;
111     }
112    
113 root 1.253 mkdir $_
114     for $LOCALDIR, $TMPDIR, $UNIQUEDIR, $PLAYERDIR, $RANDOMDIR, $BDBDIR;
115 root 1.103
116 root 1.131 our $EMERGENCY_POSITION;
117 root 1.110
118 root 1.199 sub cf::map::normalise;
119    
120 root 1.70 #############################################################################
121    
122     =head2 GLOBAL VARIABLES
123    
124     =over 4
125    
126 root 1.83 =item $cf::UPTIME
127    
128     The timestamp of the server start (so not actually an uptime).
129    
130 root 1.103 =item $cf::RUNTIME
131    
132     The time this server has run, starts at 0 and is increased by $cf::TICK on
133     every server tick.
134    
135 root 1.253 =item $cf::CONFDIR $cf::DATADIR $cf::LIBDIR $cf::PODDIR
136     $cf::MAPDIR $cf::LOCALDIR $cf::TMPDIR $cf::UNIQUEDIR
137     $cf::PLAYERDIR $cf::RANDOMDIR $cf::BDBDIR
138    
139     Various directories - "/etc", read-only install directory, perl-library
140     directory, pod-directory, read-only maps directory, "/var", "/var/tmp",
141     unique-items directory, player file directory, random maps directory and
142     database environment.
143 root 1.70
144 root 1.103 =item $cf::NOW
145    
146     The time of the last (current) server tick.
147    
148 root 1.70 =item $cf::TICK
149    
150     The interval between server ticks, in seconds.
151    
152 root 1.265 =item $cf::LOADAVG
153    
154     The current CPU load on the server (alpha-smoothed), as a value between 0
155     (none) and 1 (overloaded), indicating how much time is spent on processing
156     objects per tick. Healthy values are < 0.5.
157    
158     =item $cf::LOAD
159    
160     The raw value load value from the last tick.
161    
162 root 1.70 =item %cf::CFG
163    
164     Configuration for the server, loaded from C</etc/crossfire/config>, or
165     from wherever your confdir points to.
166    
167 root 1.239 =item cf::wait_for_tick, cf::wait_for_tick_begin
168 root 1.155
169 root 1.239 These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only
170     returns directly I<after> the tick processing (and consequently, can only wake one process
171     per tick), while cf::wait_for_tick wakes up all waiters after tick processing.
172 root 1.155
173 elmex 1.310 =item @cf::INVOKE_RESULTS
174    
175     This array contains the results of the last C<invoke ()> call. When
176     C<cf::override> is called C<@cf::INVOKE_RESULTS> is set to the parameters of
177     that call.
178    
179 root 1.70 =back
180    
181     =cut
182    
183 root 1.1 BEGIN {
184     *CORE::GLOBAL::warn = sub {
185     my $msg = join "", @_;
186 root 1.103
187 root 1.1 $msg .= "\n"
188     unless $msg =~ /\n$/;
189    
190 root 1.255 $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge;
191 root 1.250
192     utf8::encode $msg;
193 root 1.146 LOG llevError, $msg;
194 root 1.1 };
195     }
196    
197 root 1.93 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
198     @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
199     @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
200     @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
201     @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
202 root 1.273 @safe::cf::arch::ISA = @cf::arch::ISA = 'cf::object';
203     @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object'; # not really true (yet)
204 root 1.25
205 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
206 root 1.25 # within the Safe compartment.
207 root 1.86 for my $pkg (qw(
208 root 1.100 cf::global cf::attachable
209 root 1.86 cf::object cf::object::player
210 root 1.89 cf::client cf::player
211 root 1.86 cf::arch cf::living
212     cf::map cf::party cf::region
213     )) {
214 root 1.25 no strict 'refs';
215 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
216 root 1.25 }
217 root 1.1
218 root 1.18 $Event::DIED = sub {
219     warn "error in event callback: @_";
220     };
221    
222 root 1.281 #############################################################################
223    
224 root 1.70 =head2 UTILITY FUNCTIONS
225    
226     =over 4
227    
228 root 1.154 =item dumpval $ref
229    
230 root 1.70 =cut
231 root 1.44
232 root 1.154 sub dumpval {
233     eval {
234     local $SIG{__DIE__};
235     my $d;
236     if (1) {
237     $d = new Data::Dumper([$_[0]], ["*var"]);
238     $d->Terse(1);
239     $d->Indent(2);
240     $d->Quotekeys(0);
241     $d->Useqq(1);
242     #$d->Bless(...);
243     $d->Seen($_[1]) if @_ > 1;
244     $d = $d->Dump();
245     }
246     $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge;
247     $d
248     } || "[unable to dump $_[0]: '$@']";
249     }
250    
251 root 1.70 =item $ref = cf::from_json $json
252    
253     Converts a JSON string into the corresponding perl data structure.
254    
255     =item $json = cf::to_json $ref
256    
257     Converts a perl data structure into its JSON representation.
258    
259 root 1.287 =cut
260    
261 root 1.290 our $json_coder = JSON::XS->new->utf8->max_size (1e6); # accept ~1mb max
262 root 1.287
263     sub to_json ($) { $json_coder->encode ($_[0]) }
264     sub from_json ($) { $json_coder->decode ($_[0]) }
265    
266 root 1.120 =item cf::lock_wait $string
267    
268     Wait until the given lock is available. See cf::lock_acquire.
269    
270     =item my $lock = cf::lock_acquire $string
271    
272     Wait until the given lock is available and then acquires it and returns
273 root 1.135 a Coro::guard object. If the guard object gets destroyed (goes out of scope,
274 root 1.120 for example when the coroutine gets canceled), the lock is automatically
275     returned.
276    
277 root 1.133 Lock names should begin with a unique identifier (for example, cf::map::find
278     uses map_find and cf::map::load uses map_load).
279 root 1.120
280 root 1.253 =item $locked = cf::lock_active $string
281    
282     Return true if the lock is currently active, i.e. somebody has locked it.
283    
284 root 1.120 =cut
285    
286     our %LOCK;
287    
288     sub lock_wait($) {
289     my ($key) = @_;
290    
291     # wait for lock, if any
292     while ($LOCK{$key}) {
293     push @{ $LOCK{$key} }, $Coro::current;
294     Coro::schedule;
295     }
296     }
297    
298     sub lock_acquire($) {
299     my ($key) = @_;
300    
301     # wait, to be sure we are not locked
302     lock_wait $key;
303    
304     $LOCK{$key} = [];
305    
306 root 1.135 Coro::guard {
307 root 1.120 # wake up all waiters, to be on the safe side
308     $_->ready for @{ delete $LOCK{$key} };
309     }
310     }
311    
312 root 1.253 sub lock_active($) {
313     my ($key) = @_;
314    
315     ! ! $LOCK{$key}
316     }
317    
318 root 1.133 sub freeze_mainloop {
319     return unless $TICK_WATCHER->is_active;
320    
321 root 1.168 my $guard = Coro::guard {
322     $TICK_WATCHER->start;
323     };
324 root 1.133 $TICK_WATCHER->stop;
325     $guard
326     }
327    
328 root 1.315 =item cf::get_slot $time[, $priority[, $name]]
329 root 1.314
330     Allocate $time seconds of blocking CPU time at priority C<$priority>:
331     This call blocks and returns only when you have at least C<$time> seconds
332     of cpu time till the next tick. The slot is only valid till the next cede.
333    
334 root 1.315 The optional C<$name> can be used to identify the job to run. It might be
335     used for statistical purposes and should identify the same time-class.
336    
337 root 1.314 Useful for short background jobs.
338    
339     =cut
340    
341     our @SLOT_QUEUE;
342     our $SLOT_QUEUE;
343    
344     $SLOT_QUEUE->cancel if $SLOT_QUEUE;
345     $SLOT_QUEUE = Coro::async {
346     my $signal = new Coro::Signal;
347    
348     while () {
349     next_job:
350     my $avail = cf::till_tick;
351     if ($avail > 0.01) {
352     for (0 .. $#SLOT_QUEUE) {
353     if ($SLOT_QUEUE[$_][0] < $avail) {
354     my $job = splice @SLOT_QUEUE, $_, 1, ();
355     $job->[2]->send;
356     Coro::cede;
357     goto next_job;
358     }
359     }
360     }
361    
362     if (@SLOT_QUEUE) {
363     # we do not use wait_For_tick() as it returns immediately when tick is inactive
364     push @cf::WAIT_FOR_TICK, $signal;
365     $signal->wait;
366     } else {
367     Coro::schedule;
368     }
369     }
370     };
371    
372 root 1.315 sub get_slot($;$$) {
373     my ($time, $pri, $name) = @_;
374    
375     $time = $TICK * .6 if $time > $TICK * .6;
376     my $sig = new Coro::Signal;
377 root 1.314
378 root 1.315 push @SLOT_QUEUE, [$time, $pri, $sig, $name];
379 root 1.314 @SLOT_QUEUE = sort { $b->[1] <=> $a->[1] } @SLOT_QUEUE;
380     $SLOT_QUEUE->ready;
381     $sig->wait;
382     }
383    
384 root 1.140 =item cf::async { BLOCK }
385    
386     Currently the same as Coro::async_pool, meaning you cannot use
387     C<on_destroy>, C<join> or other gimmicks on these coroutines. The only
388     thing you are allowed to do is call C<prio> on it.
389    
390     =cut
391    
392     BEGIN { *async = \&Coro::async_pool }
393    
394 root 1.106 =item cf::sync_job { BLOCK }
395    
396 root 1.281 The design of Crossfire TRT requires that the main coroutine ($Coro::main)
397     is always able to handle events or runnable, as Crossfire TRT is only
398     partly reentrant. Thus "blocking" it by e.g. waiting for I/O is not
399     acceptable.
400 root 1.106
401     If it must be done, put the blocking parts into C<sync_job>. This will run
402     the given BLOCK in another coroutine while waiting for the result. The
403     server will be frozen during this time, so the block should either finish
404     fast or be very important.
405    
406     =cut
407    
408 root 1.105 sub sync_job(&) {
409     my ($job) = @_;
410    
411     if ($Coro::current == $Coro::main) {
412 root 1.265 my $time = Event::time;
413    
414 root 1.112 # this is the main coro, too bad, we have to block
415     # till the operation succeeds, freezing the server :/
416    
417 root 1.110 # TODO: use suspend/resume instead
418 root 1.112 # (but this is cancel-safe)
419 root 1.133 my $freeze_guard = freeze_mainloop;
420 root 1.112
421     my $busy = 1;
422     my @res;
423    
424 root 1.140 (async {
425 root 1.112 @res = eval { $job->() };
426     warn $@ if $@;
427     undef $busy;
428     })->prio (Coro::PRIO_MAX);
429    
430 root 1.105 while ($busy) {
431 root 1.141 Coro::cede or Event::one_event;
432 root 1.105 }
433 root 1.112
434 root 1.265 $time = Event::time - $time;
435    
436 root 1.266 LOG llevError | logBacktrace, Carp::longmess "long sync job"
437     if $time > $TICK * 0.5 && $TICK_WATCHER->is_active;
438 root 1.265
439     $tick_start += $time; # do not account sync jobs to server load
440    
441 root 1.112 wantarray ? @res : $res[0]
442 root 1.105 } else {
443 root 1.112 # we are in another coroutine, how wonderful, everything just works
444    
445     $job->()
446 root 1.105 }
447     }
448    
449 root 1.140 =item $coro = cf::async_ext { BLOCK }
450 root 1.103
451 root 1.159 Like async, but this coro is automatically being canceled when the
452 root 1.140 extension calling this is being unloaded.
453 root 1.103
454     =cut
455    
456 root 1.140 sub async_ext(&) {
457 root 1.103 my $cb = shift;
458    
459 root 1.140 my $coro = &Coro::async ($cb);
460 root 1.103
461     $coro->on_destroy (sub {
462     delete $EXT_CORO{$coro+0};
463     });
464     $EXT_CORO{$coro+0} = $coro;
465    
466     $coro
467     }
468    
469 root 1.281 =item fork_call { }, $args
470    
471     Executes the given code block with the given arguments in a seperate
472     process, returning the results. Everything must be serialisable with
473     Coro::Storable. May, of course, block. Note that the executed sub may
474     never block itself or use any form of Event handling.
475    
476     =cut
477    
478     sub fork_call(&@) {
479     my ($cb, @args) = @_;
480    
481 root 1.332 # we seemingly have to make a local copy of the whole thing,
482     # otherwise perl prematurely frees the stuff :/
483     # TODO: investigate and fix (liekly this will be rather laborious)
484 root 1.281
485 root 1.332 my @res = Coro::Util::fork_eval {
486 root 1.281 reset_signals;
487 root 1.332 &$cb
488     }, @args;
489 root 1.298
490 root 1.332 wantarray ? @res : $res[-1]
491 root 1.281 }
492    
493     =item $value = cf::db_get $family => $key
494    
495     Returns a single value from the environment database.
496    
497     =item cf::db_put $family => $key => $value
498    
499     Stores the given C<$value> in the family. It can currently store binary
500     data only (use Compress::LZF::sfreeze_cr/sthaw to convert to/from binary).
501    
502     =cut
503    
504     our $DB;
505    
506     sub db_init {
507     unless ($DB) {
508     $DB = BDB::db_create $DB_ENV;
509    
510     cf::sync_job {
511     eval {
512     $DB->set_flags (BDB::CHKSUM);
513    
514     BDB::db_open $DB, undef, "db", undef, BDB::BTREE,
515     BDB::CREATE | BDB::AUTO_COMMIT, 0666;
516     cf::cleanup "db_open(db): $!" if $!;
517     };
518     cf::cleanup "db_open(db): $@" if $@;
519     };
520     }
521     }
522    
523     sub db_get($$) {
524     my $key = "$_[0]/$_[1]";
525    
526     cf::sync_job {
527     BDB::db_get $DB, undef, $key, my $data;
528    
529     $! ? ()
530     : $data
531     }
532     }
533    
534     sub db_put($$$) {
535     BDB::dbreq_pri 4;
536     BDB::db_put $DB, undef, "$_[0]/$_[1]", $_[2], 0, sub { };
537     }
538    
539     =item cf::cache $id => [$paths...], $processversion => $process
540    
541     Generic caching function that returns the value of the resource $id,
542     caching and regenerating as required.
543    
544     This function can block.
545    
546     =cut
547    
548     sub cache {
549     my ($id, $src, $processversion, $process) = @_;
550    
551     my $meta =
552     join "\x00",
553     $processversion,
554     map {
555     aio_stat $_
556     and Carp::croak "$_: $!";
557 root 1.264
558 root 1.281 ($_, (stat _)[7,9])
559     } @$src;
560 root 1.264
561 root 1.281 my $dbmeta = db_get cache => "$id/meta";
562     if ($dbmeta ne $meta) {
563     # changed, we may need to process
564 root 1.264
565 root 1.281 my @data;
566     my $md5;
567 root 1.219
568 root 1.281 for (0 .. $#$src) {
569     0 <= aio_load $src->[$_], $data[$_]
570     or Carp::croak "$src->[$_]: $!";
571     }
572 root 1.108
573 root 1.281 # if processing is expensive, check
574     # checksum first
575     if (1) {
576     $md5 =
577     join "\x00",
578     $processversion,
579     map {
580     Coro::cede;
581     ($src->[$_], Digest::MD5::md5_hex $data[$_])
582     } 0.. $#$src;
583    
584 root 1.186
585 root 1.281 my $dbmd5 = db_get cache => "$id/md5";
586     if ($dbmd5 eq $md5) {
587     db_put cache => "$id/meta", $meta;
588 root 1.108
589 root 1.281 return db_get cache => "$id/data";
590     }
591     }
592 root 1.108
593 root 1.281 my $t1 = Time::HiRes::time;
594     my $data = $process->(\@data);
595     my $t2 = Time::HiRes::time;
596 root 1.264
597 root 1.281 warn "cache: '$id' processed in ", $t2 - $t1, "s\n";
598 root 1.108
599 root 1.281 db_put cache => "$id/data", $data;
600     db_put cache => "$id/md5" , $md5;
601     db_put cache => "$id/meta", $meta;
602 root 1.108
603 root 1.281 return $data;
604     }
605 root 1.263
606 root 1.281 db_get cache => "$id/data"
607 root 1.108 }
608    
609 root 1.230 =item cf::datalog type => key => value, ...
610    
611     Log a datalog packet of the given type with the given key-value pairs.
612    
613     =cut
614    
615     sub datalog($@) {
616     my ($type, %kv) = @_;
617     warn "DATALOG ", JSON::XS->new->ascii->encode ({ %kv, type => $type });
618     }
619    
620 root 1.70 =back
621    
622 root 1.71 =cut
623    
624 root 1.44 #############################################################################
625 root 1.39
626 root 1.93 =head2 ATTACHABLE OBJECTS
627    
628 root 1.94 Many objects in crossfire are so-called attachable objects. That means you can
629     attach callbacks/event handlers (a collection of which is called an "attachment")
630     to it. All such attachable objects support the following methods.
631    
632     In the following description, CLASS can be any of C<global>, C<object>
633     C<player>, C<client> or C<map> (i.e. the attachable objects in
634 root 1.281 Crossfire TRT).
635 root 1.55
636     =over 4
637    
638 root 1.94 =item $attachable->attach ($attachment, key => $value...)
639    
640     =item $attachable->detach ($attachment)
641    
642     Attach/detach a pre-registered attachment to a specific object and give it
643     the specified key/value pairs as arguments.
644    
645     Example, attach a minesweeper attachment to the given object, making it a
646     10x10 minesweeper game:
647 root 1.46
648 root 1.94 $obj->attach (minesweeper => width => 10, height => 10);
649 root 1.53
650 root 1.93 =item $bool = $attachable->attached ($name)
651 root 1.46
652 root 1.93 Checks wether the named attachment is currently attached to the object.
653 root 1.46
654 root 1.94 =item cf::CLASS->attach ...
655 root 1.46
656 root 1.94 =item cf::CLASS->detach ...
657 root 1.92
658 root 1.94 Define an anonymous attachment and attach it to all objects of the given
659     CLASS. See the next function for an explanation of its arguments.
660 root 1.92
661 root 1.93 You can attach to global events by using the C<cf::global> class.
662 root 1.92
663 root 1.94 Example, log all player logins:
664    
665     cf::player->attach (
666     on_login => sub {
667     my ($pl) = @_;
668     ...
669     },
670     );
671    
672     Example, attach to the jeweler skill:
673    
674     cf::object->attach (
675     type => cf::SKILL,
676     subtype => cf::SK_JEWELER,
677     on_use_skill => sub {
678     my ($sk, $ob, $part, $dir, $msg) = @_;
679     ...
680     },
681     );
682    
683     =item cf::CLASS::attachment $name, ...
684    
685     Register an attachment by C<$name> through which attachable objects of the
686     given CLASS can refer to this attachment.
687    
688     Some classes such as crossfire maps and objects can specify attachments
689     that are attached at load/instantiate time, thus the need for a name.
690    
691     These calls expect any number of the following handler/hook descriptions:
692 root 1.46
693     =over 4
694    
695     =item prio => $number
696    
697     Set the priority for all following handlers/hooks (unless overwritten
698     by another C<prio> setting). Lower priority handlers get executed
699     earlier. The default priority is C<0>, and many built-in handlers are
700     registered at priority C<-1000>, so lower priorities should not be used
701     unless you know what you are doing.
702    
703 root 1.93 =item type => $type
704    
705     (Only for C<< cf::object->attach >> calls), limits the attachment to the
706     given type of objects only (the additional parameter C<subtype> can be
707     used to further limit to the given subtype).
708    
709 root 1.46 =item on_I<event> => \&cb
710    
711     Call the given code reference whenever the named event happens (event is
712     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
713     handlers are recognised generally depends on the type of object these
714     handlers attach to).
715    
716     See F<include/eventinc.h> for the full list of events supported, and their
717     class.
718    
719     =item package => package::
720    
721     Look for sub functions of the name C<< on_I<event> >> in the given
722     package and register them. Only handlers for eevents supported by the
723     object/class are recognised.
724    
725     =back
726    
727 root 1.94 Example, define an attachment called "sockpuppet" that calls the given
728     event handler when a monster attacks:
729    
730     cf::object::attachment sockpuppet =>
731     on_skill_attack => sub {
732     my ($self, $victim) = @_;
733     ...
734     }
735     }
736    
737 root 1.96 =item $attachable->valid
738    
739     Just because you have a perl object does not mean that the corresponding
740     C-level object still exists. If you try to access an object that has no
741     valid C counterpart anymore you get an exception at runtime. This method
742     can be used to test for existence of the C object part without causing an
743     exception.
744    
745 root 1.39 =cut
746    
747 root 1.40 # the following variables are defined in .xs and must not be re-created
748 root 1.100 our @CB_GLOBAL = (); # registry for all global events
749     our @CB_ATTACHABLE = (); # registry for all attachables
750     our @CB_OBJECT = (); # all objects (should not be used except in emergency)
751     our @CB_PLAYER = ();
752     our @CB_CLIENT = ();
753     our @CB_TYPE = (); # registry for type (cf-object class) based events
754     our @CB_MAP = ();
755 root 1.39
756 root 1.45 my %attachment;
757    
758 root 1.170 sub cf::attachable::thawer_merge {
759     # simply override everything except _meta
760     local $_[0]{_meta};
761     %{$_[0]} = %{$_[1]};
762     }
763    
764 root 1.93 sub _attach_cb($$$$) {
765     my ($registry, $event, $prio, $cb) = @_;
766 root 1.39
767     use sort 'stable';
768    
769     $cb = [$prio, $cb];
770    
771     @{$registry->[$event]} = sort
772     { $a->[0] cmp $b->[0] }
773     @{$registry->[$event] || []}, $cb;
774     }
775    
776 root 1.100 # hack
777     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
778    
779 root 1.39 # attach handles attaching event callbacks
780     # the only thing the caller has to do is pass the correct
781     # registry (== where the callback attaches to).
782 root 1.93 sub _attach {
783 root 1.45 my ($registry, $klass, @arg) = @_;
784 root 1.39
785 root 1.93 my $object_type;
786 root 1.39 my $prio = 0;
787     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
788    
789 root 1.100 #TODO: get rid of this hack
790     if ($attachable_klass{$klass}) {
791     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
792     }
793    
794 root 1.45 while (@arg) {
795     my $type = shift @arg;
796 root 1.39
797     if ($type eq "prio") {
798 root 1.45 $prio = shift @arg;
799 root 1.39
800 root 1.93 } elsif ($type eq "type") {
801     $object_type = shift @arg;
802     $registry = $CB_TYPE[$object_type] ||= [];
803    
804     } elsif ($type eq "subtype") {
805     defined $object_type or Carp::croak "subtype specified without type";
806     my $object_subtype = shift @arg;
807 root 1.267 $registry = $CB_TYPE[$object_type + $object_subtype * NUM_TYPES] ||= [];
808 root 1.93
809 root 1.39 } elsif ($type eq "package") {
810 root 1.45 my $pkg = shift @arg;
811 root 1.39
812     while (my ($name, $id) = each %cb_id) {
813     if (my $cb = $pkg->can ($name)) {
814 root 1.93 _attach_cb $registry, $id, $prio, $cb;
815 root 1.39 }
816     }
817    
818     } elsif (exists $cb_id{$type}) {
819 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
820 root 1.39
821     } elsif (ref $type) {
822     warn "attaching objects not supported, ignoring.\n";
823    
824     } else {
825 root 1.45 shift @arg;
826 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
827     }
828     }
829     }
830    
831 root 1.93 sub _object_attach {
832 root 1.48 my ($obj, $name, %arg) = @_;
833 root 1.46
834 root 1.55 return if exists $obj->{_attachment}{$name};
835    
836 root 1.46 if (my $attach = $attachment{$name}) {
837     my $registry = $obj->registry;
838    
839 root 1.47 for (@$attach) {
840     my ($klass, @attach) = @$_;
841 root 1.93 _attach $registry, $klass, @attach;
842 root 1.47 }
843 root 1.46
844 root 1.48 $obj->{$name} = \%arg;
845 root 1.46 } else {
846 root 1.280 warn "object uses attachment '$name' which is not available, postponing.\n";
847 root 1.46 }
848    
849 root 1.50 $obj->{_attachment}{$name} = undef;
850 root 1.46 }
851    
852 root 1.93 sub cf::attachable::attach {
853     if (ref $_[0]) {
854     _object_attach @_;
855     } else {
856     _attach shift->_attach_registry, @_;
857     }
858 root 1.267 _recalc_want;
859 root 1.55 };
860 root 1.46
861 root 1.54 # all those should be optimised
862 root 1.93 sub cf::attachable::detach {
863 root 1.54 my ($obj, $name) = @_;
864 root 1.46
865 root 1.93 if (ref $obj) {
866     delete $obj->{_attachment}{$name};
867     reattach ($obj);
868     } else {
869     Carp::croak "cannot, currently, detach class attachments";
870     }
871 root 1.267 _recalc_want;
872 root 1.55 };
873    
874 root 1.93 sub cf::attachable::attached {
875 root 1.55 my ($obj, $name) = @_;
876    
877     exists $obj->{_attachment}{$name}
878 root 1.39 }
879    
880 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
881 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
882     sub cf::\L$klass\E::_attach_registry {
883     (\\\@CB_$klass, KLASS_$klass)
884     }
885 root 1.45
886 root 1.93 sub cf::\L$klass\E::attachment {
887     my \$name = shift;
888 root 1.39
889 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
890     }
891     ";
892     die if $@;
893 root 1.52 }
894    
895 root 1.39 our $override;
896 elmex 1.310 our @INVOKE_RESULTS = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
897 root 1.39
898 root 1.45 sub override {
899     $override = 1;
900 elmex 1.310 @INVOKE_RESULTS = (@_);
901 root 1.39 }
902    
903 root 1.45 sub do_invoke {
904 root 1.39 my $event = shift;
905 root 1.40 my $callbacks = shift;
906 root 1.39
907 elmex 1.310 @INVOKE_RESULTS = ();
908 root 1.45
909 root 1.39 local $override;
910    
911 root 1.40 for (@$callbacks) {
912 root 1.39 eval { &{$_->[1]} };
913    
914     if ($@) {
915     warn "$@";
916 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
917 root 1.39 override;
918     }
919    
920     return 1 if $override;
921     }
922    
923     0
924     }
925    
926 root 1.96 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
927 root 1.55
928 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
929 root 1.55
930 root 1.96 Generate an object-specific event with the given arguments.
931 root 1.55
932 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
933 root 1.55 removed in future versions), and there is no public API to access override
934 elmex 1.310 results (if you must, access C<@cf::INVOKE_RESULTS> directly).
935 root 1.55
936     =back
937    
938 root 1.71 =cut
939    
940 root 1.70 #############################################################################
941 root 1.45 # object support
942 root 1.312 #
943    
944     sub _can_merge {
945     my ($ob1, $ob2) = @_;
946    
947     local $Storable::canonical = 1;
948     my $fob1 = Storable::freeze $ob1;
949     my $fob2 = Storable::freeze $ob2;
950    
951     $fob1 eq $fob2
952     }
953 root 1.45
954 root 1.102 sub reattach {
955     # basically do the same as instantiate, without calling instantiate
956     my ($obj) = @_;
957    
958 root 1.169 bless $obj, ref $obj; # re-bless in case extensions have been reloaded
959    
960 root 1.102 my $registry = $obj->registry;
961    
962     @$registry = ();
963    
964     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
965    
966     for my $name (keys %{ $obj->{_attachment} || {} }) {
967     if (my $attach = $attachment{$name}) {
968     for (@$attach) {
969     my ($klass, @attach) = @$_;
970     _attach $registry, $klass, @attach;
971     }
972     } else {
973     warn "object uses attachment '$name' that is not available, postponing.\n";
974     }
975     }
976     }
977    
978 root 1.100 cf::attachable->attach (
979     prio => -1000000,
980     on_instantiate => sub {
981     my ($obj, $data) = @_;
982 root 1.45
983 root 1.100 $data = from_json $data;
984 root 1.45
985 root 1.100 for (@$data) {
986     my ($name, $args) = @$_;
987 root 1.49
988 root 1.100 $obj->attach ($name, %{$args || {} });
989     }
990     },
991 root 1.102 on_reattach => \&reattach,
992 root 1.100 on_clone => sub {
993     my ($src, $dst) = @_;
994    
995     @{$dst->registry} = @{$src->registry};
996    
997     %$dst = %$src;
998    
999     %{$dst->{_attachment}} = %{$src->{_attachment}}
1000     if exists $src->{_attachment};
1001     },
1002     );
1003 root 1.45
1004 root 1.46 sub object_freezer_save {
1005 root 1.59 my ($filename, $rdata, $objs) = @_;
1006 root 1.46
1007 root 1.105 sync_job {
1008     if (length $$rdata) {
1009     warn sprintf "saving %s (%d,%d)\n",
1010     $filename, length $$rdata, scalar @$objs;
1011 root 1.60
1012 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
1013 root 1.60 chmod SAVE_MODE, $fh;
1014 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
1015 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
1016 root 1.60 close $fh;
1017 root 1.105
1018     if (@$objs) {
1019     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
1020     chmod SAVE_MODE, $fh;
1021     my $data = Storable::nfreeze { version => 1, objs => $objs };
1022     aio_write $fh, 0, (length $data), $data, 0;
1023 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
1024 root 1.105 close $fh;
1025     aio_rename "$filename.pst~", "$filename.pst";
1026     }
1027     } else {
1028     aio_unlink "$filename.pst";
1029     }
1030    
1031     aio_rename "$filename~", $filename;
1032 root 1.60 } else {
1033 root 1.105 warn "FATAL: $filename~: $!\n";
1034 root 1.60 }
1035 root 1.59 } else {
1036 root 1.105 aio_unlink $filename;
1037     aio_unlink "$filename.pst";
1038 root 1.59 }
1039 root 1.45 }
1040     }
1041    
1042 root 1.80 sub object_freezer_as_string {
1043     my ($rdata, $objs) = @_;
1044    
1045     use Data::Dumper;
1046    
1047 root 1.81 $$rdata . Dumper $objs
1048 root 1.80 }
1049    
1050 root 1.46 sub object_thawer_load {
1051     my ($filename) = @_;
1052    
1053 root 1.105 my ($data, $av);
1054 root 1.61
1055 root 1.105 (aio_load $filename, $data) >= 0
1056     or return;
1057 root 1.61
1058 root 1.105 unless (aio_stat "$filename.pst") {
1059     (aio_load "$filename.pst", $av) >= 0
1060     or return;
1061 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
1062 root 1.61 }
1063 root 1.45
1064 root 1.118 warn sprintf "loading %s (%d)\n",
1065 root 1.274 $filename, length $data, scalar @{$av || []};
1066 root 1.105 return ($data, $av);
1067 root 1.45 }
1068    
1069 root 1.281 =head2 COMMAND CALLBACKS
1070    
1071     =over 4
1072    
1073     =cut
1074    
1075 root 1.45 #############################################################################
1076 root 1.85 # command handling &c
1077 root 1.39
1078 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
1079 root 1.1
1080 root 1.85 Register a callback for execution when the client sends the user command
1081     $name.
1082 root 1.5
1083 root 1.85 =cut
1084 root 1.5
1085 root 1.85 sub register_command {
1086     my ($name, $cb) = @_;
1087 root 1.5
1088 root 1.85 my $caller = caller;
1089     #warn "registering command '$name/$time' to '$caller'";
1090 root 1.1
1091 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
1092 root 1.1 }
1093    
1094 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
1095 root 1.1
1096 root 1.287 Register a callback for execution when the client sends an (synchronous)
1097     extcmd packet. Ext commands will be processed in the order they are
1098     received by the server, like other user commands. The first argument is
1099     the logged-in player. Ext commands can only be processed after a player
1100     has logged in successfully.
1101    
1102     If the callback returns something, it is sent back as if reply was being
1103     called.
1104    
1105     =item cf::register_exticmd $name => \&callback($ns,$packet);
1106    
1107     Register a callback for execution when the client sends an (asynchronous)
1108     exticmd packet. Exti commands are processed by the server as soon as they
1109     are received, i.e. out of order w.r.t. other commands. The first argument
1110     is a client socket. Exti commands can be received anytime, even before
1111     log-in.
1112 root 1.1
1113 root 1.85 If the callback returns something, it is sent back as if reply was being
1114     called.
1115 root 1.1
1116 root 1.85 =cut
1117 root 1.1
1118 root 1.16 sub register_extcmd {
1119     my ($name, $cb) = @_;
1120    
1121 root 1.159 $EXTCMD{$name} = $cb;
1122 root 1.16 }
1123    
1124 root 1.287 sub register_exticmd {
1125     my ($name, $cb) = @_;
1126    
1127     $EXTICMD{$name} = $cb;
1128     }
1129    
1130 root 1.93 cf::player->attach (
1131 root 1.85 on_command => sub {
1132     my ($pl, $name, $params) = @_;
1133    
1134     my $cb = $COMMAND{$name}
1135     or return;
1136    
1137     for my $cmd (@$cb) {
1138     $cmd->[1]->($pl->ob, $params);
1139     }
1140    
1141     cf::override;
1142     },
1143     on_extcmd => sub {
1144     my ($pl, $buf) = @_;
1145    
1146 root 1.290 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) };
1147 root 1.85
1148     if (ref $msg) {
1149 root 1.316 my ($type, $reply, @payload) =
1150     "ARRAY" eq ref $msg
1151     ? @$msg
1152     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
1153    
1154     if (my $cb = $EXTCMD{$type}) {
1155     my @reply = $cb->($pl, @payload);
1156    
1157     $pl->ext_reply ($reply, @reply)
1158     if $reply;
1159 root 1.85 }
1160     } else {
1161     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1162     }
1163    
1164     cf::override;
1165     },
1166 root 1.93 );
1167 root 1.85
1168 root 1.278 sub load_extensions {
1169     cf::sync_job {
1170     my %todo;
1171    
1172     for my $path (<$LIBDIR/*.ext>) {
1173     next unless -r $path;
1174    
1175     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
1176     my $base = $1;
1177     my $pkg = $1;
1178     $pkg =~ s/[^[:word:]]/_/g;
1179     $pkg = "ext::$pkg";
1180    
1181     open my $fh, "<:utf8", $path
1182     or die "$path: $!";
1183    
1184     my $source = do { local $/; <$fh> };
1185 root 1.1
1186 root 1.278 my %ext = (
1187     path => $path,
1188     base => $base,
1189     pkg => $pkg,
1190     );
1191 root 1.1
1192 root 1.279 $ext{meta} = { map { (split /=/, $_, 2)[0, 1] } split /\s+/, $1 }
1193     if $source =~ /\A#!.*?perl.*?#\s*(.*)$/m;
1194 root 1.1
1195 root 1.278 $ext{source} =
1196     "package $pkg; use strict; use utf8;\n"
1197     . "#line 1 \"$path\"\n{\n"
1198     . $source
1199     . "\n};\n1";
1200 root 1.1
1201 root 1.278 $todo{$base} = \%ext;
1202 root 1.166 }
1203 root 1.1
1204 root 1.278 my %done;
1205     while (%todo) {
1206     my $progress;
1207    
1208     while (my ($k, $v) = each %todo) {
1209 root 1.279 for (split /,\s*/, $v->{meta}{depends}) {
1210 root 1.278 goto skip
1211     unless exists $done{$_};
1212     }
1213    
1214     warn "... loading '$k' into '$v->{pkg}'\n";
1215    
1216     unless (eval $v->{source}) {
1217     my $msg = $@ ? "$v->{path}: $@\n"
1218 root 1.279 : "$v->{base}: extension inactive.\n";
1219 root 1.278
1220     if (exists $v->{meta}{mandatory}) {
1221     warn $msg;
1222     warn "mandatory extension failed to load, exiting.\n";
1223     exit 1;
1224     }
1225    
1226 root 1.279 warn $msg;
1227 root 1.278 }
1228    
1229     $done{$k} = delete $todo{$k};
1230     push @EXTS, $v->{pkg};
1231 root 1.279 $progress = 1;
1232 root 1.278 }
1233 root 1.1
1234 root 1.278 skip:
1235     die "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n"
1236     unless $progress;
1237     }
1238     };
1239 root 1.1 }
1240    
1241 root 1.8 #############################################################################
1242 root 1.70
1243 root 1.281 =back
1244    
1245 root 1.70 =head2 CORE EXTENSIONS
1246    
1247     Functions and methods that extend core crossfire objects.
1248    
1249 root 1.143 =cut
1250    
1251     package cf::player;
1252    
1253 root 1.154 use Coro::AIO;
1254    
1255 root 1.95 =head3 cf::player
1256    
1257 root 1.70 =over 4
1258 root 1.22
1259 root 1.143 =item cf::player::find $login
1260 root 1.23
1261 root 1.143 Returns the given player object, loading it if necessary (might block).
1262 root 1.23
1263     =cut
1264    
1265 root 1.145 sub playerdir($) {
1266 root 1.253 "$PLAYERDIR/"
1267 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1268     }
1269    
1270 root 1.143 sub path($) {
1271 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1272    
1273 root 1.234 (playerdir $login) . "/playerdata"
1274 root 1.143 }
1275    
1276     sub find_active($) {
1277     $cf::PLAYER{$_[0]}
1278     and $cf::PLAYER{$_[0]}->active
1279     and $cf::PLAYER{$_[0]}
1280     }
1281    
1282     sub exists($) {
1283     my ($login) = @_;
1284    
1285     $cf::PLAYER{$login}
1286 root 1.180 or cf::sync_job { !aio_stat path $login }
1287 root 1.143 }
1288    
1289     sub find($) {
1290     return $cf::PLAYER{$_[0]} || do {
1291     my $login = $_[0];
1292    
1293     my $guard = cf::lock_acquire "user_find:$login";
1294    
1295 root 1.151 $cf::PLAYER{$_[0]} || do {
1296 root 1.234 # rename old playerfiles to new ones
1297     #TODO: remove when no longer required
1298     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1299     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1300     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1301     aio_unlink +(playerdir $login) . "/$login.pl";
1302    
1303 root 1.151 my $pl = load_pl path $login
1304     or return;
1305     $cf::PLAYER{$login} = $pl
1306     }
1307     }
1308 root 1.143 }
1309    
1310     sub save($) {
1311     my ($pl) = @_;
1312    
1313     return if $pl->{deny_save};
1314    
1315     my $path = path $pl;
1316     my $guard = cf::lock_acquire "user_save:$path";
1317    
1318     return if $pl->{deny_save};
1319 root 1.146
1320 root 1.154 aio_mkdir playerdir $pl, 0770;
1321 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1322    
1323     $pl->save_pl ($path);
1324     Coro::cede;
1325     }
1326    
1327     sub new($) {
1328     my ($login) = @_;
1329    
1330     my $self = create;
1331    
1332     $self->ob->name ($login);
1333     $self->{deny_save} = 1;
1334    
1335     $cf::PLAYER{$login} = $self;
1336    
1337     $self
1338 root 1.23 }
1339    
1340 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1341    
1342     =cut
1343    
1344     sub send_msg {
1345     my $ns = shift->ns
1346     or return;
1347     $ns->send_msg (@_);
1348     }
1349    
1350 root 1.154 =item $pl->quit_character
1351    
1352     Nukes the player without looking back. If logged in, the connection will
1353     be destroyed. May block for a long time.
1354    
1355     =cut
1356    
1357 root 1.145 sub quit_character {
1358     my ($pl) = @_;
1359    
1360 root 1.220 my $name = $pl->ob->name;
1361    
1362 root 1.145 $pl->{deny_save} = 1;
1363     $pl->password ("*"); # this should lock out the player until we nuked the dir
1364    
1365     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1366     $pl->deactivate;
1367     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1368     $pl->ns->destroy if $pl->ns;
1369    
1370     my $path = playerdir $pl;
1371     my $temp = "$path~$cf::RUNTIME~deleting~";
1372 root 1.154 aio_rename $path, $temp;
1373 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1374     $pl->destroy;
1375 root 1.220
1376     my $prefix = qr<^~\Q$name\E/>;
1377    
1378     # nuke player maps
1379     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1380    
1381 root 1.150 IO::AIO::aio_rmtree $temp;
1382 root 1.145 }
1383    
1384 pippijn 1.221 =item $pl->kick
1385    
1386     Kicks a player out of the game. This destroys the connection.
1387    
1388     =cut
1389    
1390     sub kick {
1391     my ($pl, $kicker) = @_;
1392    
1393     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1394     $pl->killer ("kicked");
1395     $pl->ns->destroy;
1396     }
1397    
1398 root 1.154 =item cf::player::list_logins
1399    
1400     Returns am arrayref of all valid playernames in the system, can take a
1401     while and may block, so not sync_job-capable, ever.
1402    
1403     =cut
1404    
1405     sub list_logins {
1406 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1407 root 1.154 or return [];
1408    
1409     my @logins;
1410    
1411     for my $login (@$dirs) {
1412     my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next;
1413     aio_read $fh, 0, 512, my $buf, 0 or next;
1414 root 1.155 $buf !~ /^password -------------$/m or next; # official not-valid tag
1415 root 1.154
1416     utf8::decode $login;
1417     push @logins, $login;
1418     }
1419    
1420     \@logins
1421     }
1422    
1423     =item $player->maps
1424    
1425 root 1.166 Returns an arrayref of map paths that are private for this
1426 root 1.154 player. May block.
1427    
1428     =cut
1429    
1430     sub maps($) {
1431     my ($pl) = @_;
1432    
1433 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1434    
1435 root 1.154 my $files = aio_readdir playerdir $pl
1436     or return;
1437    
1438     my @paths;
1439    
1440     for (@$files) {
1441     utf8::decode $_;
1442     next if /\.(?:pl|pst)$/;
1443 root 1.158 next unless /^$PATH_SEP/o;
1444 root 1.154
1445 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1446 root 1.154 }
1447    
1448     \@paths
1449     }
1450    
1451 root 1.283 =item $protocol_xml = $player->expand_cfpod ($crossfire_pod)
1452    
1453     Expand crossfire pod fragments into protocol xml.
1454    
1455     =cut
1456    
1457     sub expand_cfpod {
1458     ((my $self), (local $_)) = @_;
1459    
1460     # escape & and <
1461     s/&/&amp;/g;
1462     s/(?<![BIUGH])</&lt;/g;
1463    
1464     # this is buggy, it needs to properly take care of nested <'s
1465    
1466     1 while
1467     # replace B<>, I<>, U<> etc.
1468     s/B<([^\>]*)>/<b>$1<\/b>/
1469     || s/I<([^\>]*)>/<i>$1<\/i>/
1470     || s/U<([^\>]*)>/<u>$1<\/u>/
1471     # replace G<male|female> tags
1472     || s{G<([^>|]*)\|([^>]*)>}{
1473     $self->gender ? $2 : $1
1474     }ge
1475     # replace H<hint text>
1476 root 1.291 || s{H<([^\>]*)>}
1477     {
1478     ("<fg name=\"lightblue\">[$1 (Use hintmode to suppress hints)]</fg>",
1479     "<fg name=\"lightblue\">[Hint suppressed, see hintmode]</fg>",
1480     "")
1481     [$self->{hintmode}]
1482     }ge;
1483 root 1.283
1484     # create single paragraphs (very hackish)
1485     s/(?<=\S)\n(?=\w)/ /g;
1486    
1487 root 1.291 # compress some whitespace
1488 root 1.295 s/\s+\n/\n/g; # ws line-ends
1489     s/\n\n+/\n/g; # double lines
1490     s/^\n+//; # beginning lines
1491     s/\n+$//; # ending lines
1492 root 1.293
1493 root 1.283 $_
1494     }
1495    
1496 root 1.291 sub hintmode {
1497     $_[0]{hintmode} = $_[1] if @_ > 1;
1498     $_[0]{hintmode}
1499     }
1500    
1501 root 1.316 =item $player->ext_reply ($msgid, @msg)
1502 root 1.95
1503     Sends an ext reply to the player.
1504    
1505     =cut
1506    
1507 root 1.316 sub ext_reply($$@) {
1508     my ($self, $id, @msg) = @_;
1509 root 1.95
1510 root 1.336 $self->ns->ext_reply ($id, @msg)
1511 root 1.95 }
1512    
1513 root 1.316 =item $player->ext_msg ($type, @msg)
1514 root 1.231
1515     Sends an ext event to the client.
1516    
1517     =cut
1518    
1519 root 1.316 sub ext_msg($$@) {
1520     my ($self, $type, @msg) = @_;
1521 root 1.231
1522 root 1.316 $self->ns->ext_msg ($type, @msg);
1523 root 1.231 }
1524    
1525 root 1.238 =head3 cf::region
1526    
1527     =over 4
1528    
1529     =cut
1530    
1531     package cf::region;
1532    
1533     =item cf::region::find_by_path $path
1534    
1535 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1536 root 1.238
1537     =cut
1538    
1539     sub find_by_path($) {
1540     my ($path) = @_;
1541    
1542     my ($match, $specificity);
1543    
1544     for my $region (list) {
1545 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1546 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1547     if $region->specificity > $specificity;
1548     }
1549     }
1550    
1551     $match
1552     }
1553 root 1.143
1554 root 1.95 =back
1555    
1556 root 1.110 =head3 cf::map
1557    
1558     =over 4
1559    
1560     =cut
1561    
1562     package cf::map;
1563    
1564     use Fcntl;
1565     use Coro::AIO;
1566    
1567 root 1.166 use overload
1568 root 1.173 '""' => \&as_string,
1569     fallback => 1;
1570 root 1.166
1571 root 1.133 our $MAX_RESET = 3600;
1572     our $DEFAULT_RESET = 3000;
1573 root 1.110
1574     sub generate_random_map {
1575 root 1.166 my ($self, $rmp) = @_;
1576 root 1.110 # mit "rum" bekleckern, nicht
1577 root 1.166 $self->_create_random_map (
1578 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1579     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1580     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1581     $rmp->{exit_on_final_map},
1582     $rmp->{xsize}, $rmp->{ysize},
1583     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1584     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1585     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1586     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1587     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1588 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1589     )
1590 root 1.110 }
1591    
1592 root 1.187 =item cf::map->register ($regex, $prio)
1593    
1594     Register a handler for the map path matching the given regex at the
1595     givne priority (higher is better, built-in handlers have priority 0, the
1596     default).
1597    
1598     =cut
1599    
1600 root 1.166 sub register {
1601 root 1.187 my (undef, $regex, $prio) = @_;
1602 root 1.166 my $pkg = caller;
1603    
1604     no strict;
1605     push @{"$pkg\::ISA"}, __PACKAGE__;
1606    
1607 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1608 root 1.166 }
1609    
1610     # also paths starting with '/'
1611 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1612 root 1.166
1613 root 1.170 sub thawer_merge {
1614 root 1.172 my ($self, $merge) = @_;
1615    
1616 root 1.170 # we have to keep some variables in memory intact
1617 root 1.172 local $self->{path};
1618     local $self->{load_path};
1619 root 1.170
1620 root 1.172 $self->SUPER::thawer_merge ($merge);
1621 root 1.170 }
1622    
1623 root 1.166 sub normalise {
1624     my ($path, $base) = @_;
1625    
1626 root 1.192 $path = "$path"; # make sure its a string
1627    
1628 root 1.199 $path =~ s/\.map$//;
1629    
1630 root 1.166 # map plan:
1631     #
1632     # /! non-realised random map exit (special hack!)
1633     # {... are special paths that are not being touched
1634     # ?xxx/... are special absolute paths
1635     # ?random/... random maps
1636     # /... normal maps
1637     # ~user/... per-player map of a specific user
1638    
1639     $path =~ s/$PATH_SEP/\//go;
1640    
1641     # treat it as relative path if it starts with
1642     # something that looks reasonable
1643     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1644     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1645    
1646     $base =~ s{[^/]+/?$}{};
1647     $path = "$base/$path";
1648     }
1649    
1650     for ($path) {
1651     redo if s{//}{/};
1652     redo if s{/\.?/}{/};
1653     redo if s{/[^/]+/\.\./}{/};
1654     }
1655    
1656     $path
1657     }
1658    
1659     sub new_from_path {
1660     my (undef, $path, $base) = @_;
1661    
1662     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1663    
1664     $path = normalise $path, $base;
1665    
1666 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1667     if ($path =~ $EXT_MAP{$pkg}[1]) {
1668 root 1.166 my $self = bless cf::map::new, $pkg;
1669     $self->{path} = $path; $self->path ($path);
1670     $self->init; # pass $1 etc.
1671     return $self;
1672     }
1673     }
1674    
1675 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1676 root 1.166 ()
1677     }
1678    
1679     sub init {
1680     my ($self) = @_;
1681    
1682     $self
1683     }
1684    
1685     sub as_string {
1686     my ($self) = @_;
1687    
1688     "$self->{path}"
1689     }
1690    
1691     # the displayed name, this is a one way mapping
1692     sub visible_name {
1693     &as_string
1694     }
1695    
1696     # the original (read-only) location
1697     sub load_path {
1698     my ($self) = @_;
1699    
1700 root 1.254 "$MAPDIR/$self->{path}.map"
1701 root 1.166 }
1702    
1703     # the temporary/swap location
1704     sub save_path {
1705     my ($self) = @_;
1706    
1707 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1708 root 1.254 "$TMPDIR/$path.map"
1709 root 1.166 }
1710    
1711     # the unique path, undef == no special unique path
1712     sub uniq_path {
1713     my ($self) = @_;
1714    
1715 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1716 root 1.253 "$UNIQUEDIR/$path"
1717 root 1.166 }
1718    
1719 root 1.110 # and all this just because we cannot iterate over
1720     # all maps in C++...
1721     sub change_all_map_light {
1722     my ($change) = @_;
1723    
1724 root 1.122 $_->change_map_light ($change)
1725     for grep $_->outdoor, values %cf::MAP;
1726 root 1.110 }
1727    
1728 root 1.275 sub decay_objects {
1729     my ($self) = @_;
1730    
1731     return if $self->{deny_reset};
1732    
1733     $self->do_decay_objects;
1734     }
1735    
1736 root 1.166 sub unlink_save {
1737     my ($self) = @_;
1738    
1739     utf8::encode (my $save = $self->save_path);
1740 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1741     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1742 root 1.166 }
1743    
1744     sub load_header_from($) {
1745     my ($self, $path) = @_;
1746 root 1.110
1747     utf8::encode $path;
1748 root 1.200 #aio_open $path, O_RDONLY, 0
1749     # or return;
1750 root 1.110
1751 root 1.166 $self->_load_header ($path)
1752 root 1.110 or return;
1753    
1754 root 1.166 $self->{load_path} = $path;
1755 root 1.135
1756 root 1.166 1
1757     }
1758 root 1.110
1759 root 1.188 sub load_header_orig {
1760 root 1.166 my ($self) = @_;
1761 root 1.110
1762 root 1.166 $self->load_header_from ($self->load_path)
1763 root 1.110 }
1764    
1765 root 1.188 sub load_header_temp {
1766 root 1.166 my ($self) = @_;
1767 root 1.110
1768 root 1.166 $self->load_header_from ($self->save_path)
1769     }
1770 root 1.110
1771 root 1.188 sub prepare_temp {
1772     my ($self) = @_;
1773    
1774     $self->last_access ((delete $self->{last_access})
1775     || $cf::RUNTIME); #d#
1776     # safety
1777     $self->{instantiate_time} = $cf::RUNTIME
1778     if $self->{instantiate_time} > $cf::RUNTIME;
1779     }
1780    
1781     sub prepare_orig {
1782     my ($self) = @_;
1783    
1784     $self->{load_original} = 1;
1785     $self->{instantiate_time} = $cf::RUNTIME;
1786     $self->last_access ($cf::RUNTIME);
1787     $self->instantiate;
1788     }
1789    
1790 root 1.166 sub load_header {
1791     my ($self) = @_;
1792 root 1.110
1793 root 1.188 if ($self->load_header_temp) {
1794     $self->prepare_temp;
1795 root 1.166 } else {
1796 root 1.188 $self->load_header_orig
1797 root 1.166 or return;
1798 root 1.188 $self->prepare_orig;
1799 root 1.166 }
1800 root 1.120
1801 root 1.275 $self->{deny_reset} = 1
1802     if $self->no_reset;
1803    
1804 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1805     unless $self->default_region;
1806    
1807 root 1.166 1
1808     }
1809 root 1.110
1810 root 1.166 sub find;
1811     sub find {
1812     my ($path, $origin) = @_;
1813 root 1.134
1814 root 1.166 $path = normalise $path, $origin && $origin->path;
1815 root 1.110
1816 root 1.166 cf::lock_wait "map_find:$path";
1817 root 1.110
1818 root 1.166 $cf::MAP{$path} || do {
1819     my $guard = cf::lock_acquire "map_find:$path";
1820     my $map = new_from_path cf::map $path
1821     or return;
1822 root 1.110
1823 root 1.116 $map->{last_save} = $cf::RUNTIME;
1824 root 1.110
1825 root 1.166 $map->load_header
1826     or return;
1827 root 1.134
1828 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
1829 root 1.185 # doing this can freeze the server in a sync job, obviously
1830     #$cf::WAIT_FOR_TICK->wait;
1831 root 1.112 $map->reset;
1832 root 1.123 undef $guard;
1833 root 1.192 return find $path;
1834 root 1.112 }
1835 root 1.110
1836 root 1.166 $cf::MAP{$path} = $map
1837 root 1.110 }
1838     }
1839    
1840 root 1.188 sub pre_load { }
1841     sub post_load { }
1842    
1843 root 1.110 sub load {
1844     my ($self) = @_;
1845    
1846 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
1847    
1848 root 1.120 my $path = $self->{path};
1849    
1850 root 1.256 {
1851     my $guard = cf::lock_acquire "map_load:$path";
1852    
1853     return if $self->in_memory != cf::MAP_SWAPPED;
1854 root 1.110
1855 root 1.256 $self->in_memory (cf::MAP_LOADING);
1856 root 1.110
1857 root 1.256 $self->alloc;
1858 root 1.188
1859 root 1.256 $self->pre_load;
1860     Coro::cede;
1861 root 1.188
1862 root 1.256 $self->_load_objects ($self->{load_path}, 1)
1863     or return;
1864 root 1.110
1865 root 1.256 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1866     if delete $self->{load_original};
1867 root 1.111
1868 root 1.256 if (my $uniq = $self->uniq_path) {
1869     utf8::encode $uniq;
1870     if (aio_open $uniq, O_RDONLY, 0) {
1871     $self->clear_unique_items;
1872     $self->_load_objects ($uniq, 0);
1873     }
1874 root 1.110 }
1875    
1876 root 1.166 Coro::cede;
1877 root 1.256 # now do the right thing for maps
1878     $self->link_multipart_objects;
1879 root 1.110 $self->difficulty ($self->estimate_difficulty)
1880     unless $self->difficulty;
1881 root 1.166 Coro::cede;
1882 root 1.256
1883     unless ($self->{deny_activate}) {
1884     $self->decay_objects;
1885     $self->fix_auto_apply;
1886     $self->update_buttons;
1887     Coro::cede;
1888     $self->set_darkness_map;
1889     Coro::cede;
1890     $self->activate;
1891     }
1892    
1893 root 1.325 $self->{last_save} = $cf::RUNTIME;
1894     $self->last_access ($cf::RUNTIME);
1895 root 1.324
1896 root 1.256 $self->in_memory (cf::MAP_IN_MEMORY);
1897 root 1.110 }
1898    
1899 root 1.188 $self->post_load;
1900 root 1.166 }
1901    
1902     sub customise_for {
1903     my ($self, $ob) = @_;
1904    
1905     return find "~" . $ob->name . "/" . $self->{path}
1906     if $self->per_player;
1907 root 1.134
1908 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
1909     # if $self->per_party;
1910    
1911 root 1.166 $self
1912 root 1.110 }
1913    
1914 root 1.157 # find and load all maps in the 3x3 area around a map
1915 root 1.333 sub load_neighbours {
1916 root 1.157 my ($map) = @_;
1917    
1918 root 1.333 my @neigh; # diagonal neighbours
1919 root 1.157
1920     for (0 .. 3) {
1921     my $neigh = $map->tile_path ($_)
1922     or next;
1923     $neigh = find $neigh, $map
1924     or next;
1925     $neigh->load;
1926    
1927 root 1.333 push @neigh,
1928     [$neigh->tile_path (($_ + 3) % 4), $neigh],
1929     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1930 root 1.157 }
1931    
1932 root 1.333 for (grep defined $_->[0], @neigh) {
1933     my ($path, $origin) = @$_;
1934     my $neigh = find $path, $origin
1935 root 1.157 or next;
1936     $neigh->load;
1937     }
1938     }
1939    
1940 root 1.133 sub find_sync {
1941 root 1.110 my ($path, $origin) = @_;
1942    
1943 root 1.157 cf::sync_job { find $path, $origin }
1944 root 1.133 }
1945    
1946     sub do_load_sync {
1947     my ($map) = @_;
1948 root 1.110
1949 root 1.133 cf::sync_job { $map->load };
1950 root 1.110 }
1951    
1952 root 1.157 our %MAP_PREFETCH;
1953 root 1.183 our $MAP_PREFETCHER = undef;
1954 root 1.157
1955     sub find_async {
1956     my ($path, $origin) = @_;
1957    
1958 root 1.166 $path = normalise $path, $origin && $origin->{path};
1959 root 1.157
1960 root 1.166 if (my $map = $cf::MAP{$path}) {
1961 root 1.157 return $map if $map->in_memory == cf::MAP_IN_MEMORY;
1962     }
1963    
1964 root 1.183 undef $MAP_PREFETCH{$path};
1965     $MAP_PREFETCHER ||= cf::async {
1966     while (%MAP_PREFETCH) {
1967     for my $path (keys %MAP_PREFETCH) {
1968 root 1.308 if (my $map = find $path) {
1969     $map->load;
1970     }
1971 root 1.183
1972     delete $MAP_PREFETCH{$path};
1973     }
1974     }
1975     undef $MAP_PREFETCHER;
1976     };
1977 root 1.189 $MAP_PREFETCHER->prio (6);
1978 root 1.157
1979     ()
1980     }
1981    
1982 root 1.110 sub save {
1983     my ($self) = @_;
1984    
1985 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1986    
1987 root 1.110 $self->{last_save} = $cf::RUNTIME;
1988    
1989     return unless $self->dirty;
1990    
1991 root 1.166 my $save = $self->save_path; utf8::encode $save;
1992     my $uniq = $self->uniq_path; utf8::encode $uniq;
1993 root 1.117
1994 root 1.110 $self->{load_path} = $save;
1995    
1996     return if $self->{deny_save};
1997    
1998 root 1.132 local $self->{last_access} = $self->last_access;#d#
1999    
2000 root 1.143 cf::async {
2001     $_->contr->save for $self->players;
2002     };
2003    
2004 root 1.110 if ($uniq) {
2005 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2006     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2007 root 1.110 } else {
2008 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2009 root 1.110 }
2010     }
2011    
2012     sub swap_out {
2013     my ($self) = @_;
2014    
2015 root 1.130 # save first because save cedes
2016     $self->save;
2017    
2018 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
2019    
2020 root 1.110 return if $self->players;
2021     return if $self->in_memory != cf::MAP_IN_MEMORY;
2022     return if $self->{deny_save};
2023    
2024     $self->clear;
2025     $self->in_memory (cf::MAP_SWAPPED);
2026     }
2027    
2028 root 1.112 sub reset_at {
2029     my ($self) = @_;
2030 root 1.110
2031     # TODO: safety, remove and allow resettable per-player maps
2032 root 1.114 return 1e99 if $self->{deny_reset};
2033 root 1.110
2034 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2035 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2036 root 1.110
2037 root 1.112 $time + $to
2038     }
2039    
2040     sub should_reset {
2041     my ($self) = @_;
2042    
2043     $self->reset_at <= $cf::RUNTIME
2044 root 1.111 }
2045    
2046 root 1.110 sub reset {
2047     my ($self) = @_;
2048    
2049 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2050 root 1.137
2051 root 1.110 return if $self->players;
2052    
2053 root 1.274 warn "resetting map ", $self->path;
2054 root 1.110
2055 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2056    
2057     # need to save uniques path
2058     unless ($self->{deny_save}) {
2059     my $uniq = $self->uniq_path; utf8::encode $uniq;
2060    
2061     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2062     if $uniq;
2063     }
2064    
2065 root 1.111 delete $cf::MAP{$self->path};
2066 root 1.110
2067 root 1.167 $self->clear;
2068    
2069 root 1.110 $_->clear_links_to ($self) for values %cf::MAP;
2070    
2071 root 1.166 $self->unlink_save;
2072 root 1.111 $self->destroy;
2073 root 1.110 }
2074    
2075 root 1.114 my $nuke_counter = "aaaa";
2076    
2077     sub nuke {
2078     my ($self) = @_;
2079    
2080 root 1.174 delete $cf::MAP{$self->path};
2081    
2082     $self->unlink_save;
2083    
2084     bless $self, "cf::map";
2085     delete $self->{deny_reset};
2086 root 1.114 $self->{deny_save} = 1;
2087     $self->reset_timeout (1);
2088 root 1.174 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2089    
2090     $cf::MAP{$self->path} = $self;
2091    
2092 root 1.114 $self->reset; # polite request, might not happen
2093     }
2094    
2095 root 1.276 =item $maps = cf::map::tmp_maps
2096    
2097     Returns an arrayref with all map paths of currently instantiated and saved
2098 root 1.277 maps. May block.
2099 root 1.276
2100     =cut
2101    
2102     sub tmp_maps() {
2103     [
2104     map {
2105     utf8::decode $_;
2106 root 1.277 /\.map$/
2107 root 1.276 ? normalise $_
2108     : ()
2109     } @{ aio_readdir $TMPDIR or [] }
2110     ]
2111     }
2112    
2113 root 1.277 =item $maps = cf::map::random_maps
2114    
2115     Returns an arrayref with all map paths of currently instantiated and saved
2116     random maps. May block.
2117    
2118     =cut
2119    
2120     sub random_maps() {
2121     [
2122     map {
2123     utf8::decode $_;
2124     /\.map$/
2125     ? normalise "?random/$_"
2126     : ()
2127     } @{ aio_readdir $RANDOMDIR or [] }
2128     ]
2129     }
2130    
2131 root 1.158 =item cf::map::unique_maps
2132    
2133 root 1.166 Returns an arrayref of paths of all shared maps that have
2134 root 1.158 instantiated unique items. May block.
2135    
2136     =cut
2137    
2138     sub unique_maps() {
2139 root 1.276 [
2140     map {
2141     utf8::decode $_;
2142 root 1.277 /\.map$/
2143 root 1.276 ? normalise $_
2144     : ()
2145     } @{ aio_readdir $UNIQUEDIR or [] }
2146     ]
2147 root 1.158 }
2148    
2149 root 1.155 package cf;
2150    
2151     =back
2152    
2153     =head3 cf::object
2154    
2155     =cut
2156    
2157     package cf::object;
2158    
2159     =over 4
2160    
2161     =item $ob->inv_recursive
2162 root 1.110
2163 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
2164 root 1.110
2165 root 1.155 =cut
2166 root 1.144
2167 root 1.155 sub inv_recursive_;
2168     sub inv_recursive_ {
2169     map { $_, inv_recursive_ $_->inv } @_
2170     }
2171 root 1.110
2172 root 1.155 sub inv_recursive {
2173     inv_recursive_ inv $_[0]
2174 root 1.110 }
2175    
2176     package cf;
2177    
2178     =back
2179    
2180 root 1.95 =head3 cf::object::player
2181    
2182     =over 4
2183    
2184 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2185 root 1.28
2186     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2187     can be C<undef>. Does the right thing when the player is currently in a
2188     dialogue with the given NPC character.
2189    
2190     =cut
2191    
2192 root 1.22 # rough implementation of a future "reply" method that works
2193     # with dialog boxes.
2194 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2195 root 1.23 sub cf::object::player::reply($$$;$) {
2196     my ($self, $npc, $msg, $flags) = @_;
2197    
2198     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2199 root 1.22
2200 root 1.24 if ($self->{record_replies}) {
2201     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2202 elmex 1.282
2203 root 1.24 } else {
2204 elmex 1.282 my $pl = $self->contr;
2205    
2206     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2207 root 1.316 my $dialog = $pl->{npc_dialog};
2208     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2209 elmex 1.282
2210     } else {
2211     $msg = $npc->name . " says: $msg" if $npc;
2212     $self->message ($msg, $flags);
2213     }
2214 root 1.24 }
2215 root 1.22 }
2216    
2217 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2218    
2219     =cut
2220    
2221     sub cf::object::send_msg {
2222     my $pl = shift->contr
2223     or return;
2224     $pl->send_msg (@_);
2225     }
2226    
2227 root 1.79 =item $player_object->may ("access")
2228    
2229     Returns wether the given player is authorized to access resource "access"
2230     (e.g. "command_wizcast").
2231    
2232     =cut
2233    
2234     sub cf::object::player::may {
2235     my ($self, $access) = @_;
2236    
2237     $self->flag (cf::FLAG_WIZ) ||
2238     (ref $cf::CFG{"may_$access"}
2239     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2240     : $cf::CFG{"may_$access"})
2241     }
2242 root 1.70
2243 root 1.115 =item $player_object->enter_link
2244    
2245     Freezes the player and moves him/her to a special map (C<{link}>).
2246    
2247 root 1.166 The player should be reasonably safe there for short amounts of time. You
2248 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
2249    
2250 root 1.166 Will never block.
2251    
2252 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2253    
2254 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2255     map. If the map is not valid (or omitted), the player will be moved back
2256     to the location he/she was before the call to C<enter_link>, or, if that
2257     fails, to the emergency map position.
2258 root 1.115
2259     Might block.
2260    
2261     =cut
2262    
2263 root 1.166 sub link_map {
2264     unless ($LINK_MAP) {
2265     $LINK_MAP = cf::map::find "{link}"
2266 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2267 root 1.166 $LINK_MAP->load;
2268     }
2269    
2270     $LINK_MAP
2271     }
2272    
2273 root 1.110 sub cf::object::player::enter_link {
2274     my ($self) = @_;
2275    
2276 root 1.259 $self->deactivate_recursive;
2277 root 1.258
2278 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2279 root 1.110
2280 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2281 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2282 root 1.110
2283 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
2284 root 1.110 }
2285    
2286     sub cf::object::player::leave_link {
2287     my ($self, $map, $x, $y) = @_;
2288    
2289 root 1.270 return unless $self->contr->active;
2290    
2291 root 1.110 my $link_pos = delete $self->{_link_pos};
2292    
2293     unless ($map) {
2294     # restore original map position
2295     ($map, $x, $y) = @{ $link_pos || [] };
2296 root 1.133 $map = cf::map::find $map;
2297 root 1.110
2298     unless ($map) {
2299     ($map, $x, $y) = @$EMERGENCY_POSITION;
2300 root 1.133 $map = cf::map::find $map
2301 root 1.110 or die "FATAL: cannot load emergency map\n";
2302     }
2303     }
2304    
2305     ($x, $y) = (-1, -1)
2306     unless (defined $x) && (defined $y);
2307    
2308     # use -1 or undef as default coordinates, not 0, 0
2309     ($x, $y) = ($map->enter_x, $map->enter_y)
2310     if $x <=0 && $y <= 0;
2311    
2312     $map->load;
2313 root 1.333 $map->load_neighbours;
2314 root 1.110
2315 root 1.143 return unless $self->contr->active;
2316 root 1.110 $self->activate_recursive;
2317 root 1.215
2318     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2319 root 1.110 $self->enter_map ($map, $x, $y);
2320     }
2321    
2322 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2323 root 1.268
2324     Moves the player to the given map-path and coordinates by first freezing
2325     her, loading and preparing them map, calling the provided $check callback
2326     that has to return the map if sucecssful, and then unfreezes the player on
2327 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2328     be called at the end of this process.
2329 root 1.110
2330     =cut
2331    
2332 root 1.270 our $GOTOGEN;
2333    
2334 root 1.136 sub cf::object::player::goto {
2335 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2336 root 1.268
2337 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2338     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2339    
2340 root 1.110 $self->enter_link;
2341    
2342 root 1.140 (async {
2343 root 1.197 my $map = eval {
2344     my $map = cf::map::find $path;
2345 root 1.268
2346     if ($map) {
2347     $map = $map->customise_for ($self);
2348     $map = $check->($map) if $check && $map;
2349     } else {
2350     $self->message ("The exit to '$path' is closed", cf::NDI_UNIQUE | cf::NDI_RED);
2351     }
2352    
2353 root 1.197 $map
2354 root 1.268 };
2355    
2356     if ($@) {
2357     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2358     LOG llevError | logBacktrace, Carp::longmess $@;
2359     }
2360 root 1.115
2361 root 1.270 if ($gen == $self->{_goto_generation}) {
2362     delete $self->{_goto_generation};
2363     $self->leave_link ($map, $x, $y);
2364     }
2365 root 1.306
2366     $done->() if $done;
2367 root 1.110 })->prio (1);
2368     }
2369    
2370     =item $player_object->enter_exit ($exit_object)
2371    
2372     =cut
2373    
2374     sub parse_random_map_params {
2375     my ($spec) = @_;
2376    
2377     my $rmp = { # defaults
2378 root 1.181 xsize => (cf::rndm 15, 40),
2379     ysize => (cf::rndm 15, 40),
2380     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2381 root 1.182 #layout => string,
2382 root 1.110 };
2383    
2384     for (split /\n/, $spec) {
2385     my ($k, $v) = split /\s+/, $_, 2;
2386    
2387     $rmp->{lc $k} = $v if (length $k) && (length $v);
2388     }
2389    
2390     $rmp
2391     }
2392    
2393     sub prepare_random_map {
2394     my ($exit) = @_;
2395    
2396 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
2397    
2398 root 1.110 # all this does is basically replace the /! path by
2399     # a new random map path (?random/...) with a seed
2400     # that depends on the exit object
2401    
2402     my $rmp = parse_random_map_params $exit->msg;
2403    
2404     if ($exit->map) {
2405 root 1.198 $rmp->{region} = $exit->region->name;
2406 root 1.110 $rmp->{origin_map} = $exit->map->path;
2407     $rmp->{origin_x} = $exit->x;
2408     $rmp->{origin_y} = $exit->y;
2409     }
2410    
2411     $rmp->{random_seed} ||= $exit->random_seed;
2412    
2413     my $data = cf::to_json $rmp;
2414     my $md5 = Digest::MD5::md5_hex $data;
2415 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2416 root 1.110
2417 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2418 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2419 root 1.177 undef $fh;
2420     aio_rename "$meta~", $meta;
2421 root 1.110
2422     $exit->slaying ("?random/$md5");
2423     $exit->msg (undef);
2424     }
2425     }
2426    
2427     sub cf::object::player::enter_exit {
2428     my ($self, $exit) = @_;
2429    
2430     return unless $self->type == cf::PLAYER;
2431    
2432 root 1.195 if ($exit->slaying eq "/!") {
2433     #TODO: this should de-fi-ni-te-ly not be a sync-job
2434 root 1.233 # the problem is that $exit might not survive long enough
2435     # so it needs to be done right now, right here
2436 root 1.195 cf::sync_job { prepare_random_map $exit };
2437     }
2438    
2439     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2440     my $hp = $exit->stats->hp;
2441     my $sp = $exit->stats->sp;
2442    
2443 root 1.110 $self->enter_link;
2444    
2445 root 1.296 # if exit is damned, update players death & WoR home-position
2446     $self->contr->savebed ($slaying, $hp, $sp)
2447     if $exit->flag (FLAG_DAMNED);
2448    
2449 root 1.140 (async {
2450 root 1.133 $self->deactivate_recursive; # just to be sure
2451 root 1.110 unless (eval {
2452 root 1.195 $self->goto ($slaying, $hp, $sp);
2453 root 1.110
2454     1;
2455     }) {
2456     $self->message ("Something went wrong deep within the crossfire server. "
2457 root 1.233 . "I'll try to bring you back to the map you were before. "
2458     . "Please report this to the dungeon master!",
2459     cf::NDI_UNIQUE | cf::NDI_RED);
2460 root 1.110
2461     warn "ERROR in enter_exit: $@";
2462     $self->leave_link;
2463     }
2464     })->prio (1);
2465     }
2466    
2467 root 1.95 =head3 cf::client
2468    
2469     =over 4
2470    
2471     =item $client->send_drawinfo ($text, $flags)
2472    
2473     Sends a drawinfo packet to the client. Circumvents output buffering so
2474     should not be used under normal circumstances.
2475    
2476 root 1.70 =cut
2477    
2478 root 1.95 sub cf::client::send_drawinfo {
2479     my ($self, $text, $flags) = @_;
2480    
2481     utf8::encode $text;
2482 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2483 root 1.95 }
2484    
2485 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2486 root 1.283
2487     Send a drawinfo or msg packet to the client, formatting the msg for the
2488     client if neccessary. C<$type> should be a string identifying the type of
2489     the message, with C<log> being the default. If C<$color> is negative, suppress
2490     the message unless the client supports the msg packet.
2491    
2492     =cut
2493    
2494     sub cf::client::send_msg {
2495 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
2496 root 1.283
2497     $msg = $self->pl->expand_cfpod ($msg);
2498    
2499 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
2500 root 1.311
2501     if (ref $channel) {
2502     # send meta info to client, if not yet sent
2503     unless (exists $self->{channel}{$channel->{id}}) {
2504     $self->{channel}{$channel->{id}} = $channel;
2505 root 1.319 $self->ext_msg (channel_info => $channel);
2506 root 1.311 }
2507    
2508     $channel = $channel->{id};
2509     }
2510    
2511 root 1.313 return unless @extra || length $msg;
2512    
2513 root 1.283 if ($self->can_msg) {
2514 root 1.323 # default colour, mask it out
2515     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2516     if $color & cf::NDI_DEF;
2517    
2518     $self->send_packet ("msg " . $self->{json_coder}->encode (
2519     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]));
2520 root 1.283 } else {
2521 root 1.323 if ($color >= 0) {
2522     # replace some tags by gcfclient-compatible ones
2523     for ($msg) {
2524     1 while
2525     s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2526     || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2527     || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2528     || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2529     || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2530     }
2531    
2532     $color &= cf::NDI_COLOR_MASK;
2533 root 1.283
2534 root 1.327 utf8::encode $msg;
2535    
2536 root 1.284 if (0 && $msg =~ /\[/) {
2537 root 1.331 # COMMAND/INFO
2538     $self->send_packet ("drawextinfo $color 10 8 $msg")
2539 root 1.283 } else {
2540 root 1.286 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2541 root 1.283 $self->send_packet ("drawinfo $color $msg")
2542     }
2543     }
2544     }
2545     }
2546    
2547 root 1.316 =item $client->ext_msg ($type, @msg)
2548 root 1.232
2549 root 1.287 Sends an ext event to the client.
2550 root 1.232
2551     =cut
2552    
2553 root 1.316 sub cf::client::ext_msg($$@) {
2554     my ($self, $type, @msg) = @_;
2555 root 1.232
2556 root 1.316 my $extcmd = $self->extcmd;
2557 root 1.305
2558 root 1.316 if ($extcmd == 2) {
2559     $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2560     } elsif ($extcmd == 1) { # TODO: remove
2561     push @msg, msgtype => "event_$type";
2562     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2563     }
2564 root 1.232 }
2565 root 1.95
2566 root 1.336 =item $client->ext_reply ($msgid, @msg)
2567    
2568     Sends an ext reply to the client.
2569    
2570     =cut
2571    
2572     sub cf::client::ext_reply($$@) {
2573     my ($self, $id, @msg) = @_;
2574    
2575     if ($self->extcmd == 2) {
2576     $self->send_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
2577     } elsif ($self->ns->extcmd == 1) {
2578     #TODO: version 1, remove
2579     unshift @msg, msgtype => "reply", msgid => $id;
2580     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2581     }
2582     }
2583    
2584 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
2585    
2586     Queues a query to the client, calling the given callback with
2587     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2588     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2589    
2590 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
2591     become reliable at some point in the future.
2592 root 1.95
2593     =cut
2594    
2595     sub cf::client::query {
2596     my ($self, $flags, $text, $cb) = @_;
2597    
2598     return unless $self->state == ST_PLAYING
2599     || $self->state == ST_SETUP
2600     || $self->state == ST_CUSTOM;
2601    
2602     $self->state (ST_CUSTOM);
2603    
2604     utf8::encode $text;
2605     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2606    
2607     $self->send_packet ($self->{query_queue}[0][0])
2608     if @{ $self->{query_queue} } == 1;
2609 root 1.287
2610     1
2611 root 1.95 }
2612    
2613     cf::client->attach (
2614 root 1.290 on_connect => sub {
2615     my ($ns) = @_;
2616    
2617     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2618     },
2619 root 1.95 on_reply => sub {
2620     my ($ns, $msg) = @_;
2621    
2622     # this weird shuffling is so that direct followup queries
2623     # get handled first
2624 root 1.128 my $queue = delete $ns->{query_queue}
2625 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2626 root 1.95
2627     (shift @$queue)->[1]->($msg);
2628 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2629 root 1.95
2630     push @{ $ns->{query_queue} }, @$queue;
2631    
2632     if (@{ $ns->{query_queue} } == @$queue) {
2633     if (@$queue) {
2634     $ns->send_packet ($ns->{query_queue}[0][0]);
2635     } else {
2636 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2637 root 1.95 }
2638     }
2639     },
2640 root 1.287 on_exticmd => sub {
2641     my ($ns, $buf) = @_;
2642    
2643 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2644 root 1.287
2645     if (ref $msg) {
2646 root 1.316 my ($type, $reply, @payload) =
2647     "ARRAY" eq ref $msg
2648     ? @$msg
2649     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2650    
2651     if (my $cb = $EXTICMD{$type}) {
2652     my @reply = $cb->($ns, @payload);
2653    
2654     $ns->ext_reply ($reply, @reply)
2655     if $reply;
2656 root 1.287 }
2657     } else {
2658     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2659     }
2660    
2661     cf::override;
2662     },
2663 root 1.95 );
2664    
2665 root 1.140 =item $client->async (\&cb)
2666 root 1.96
2667     Create a new coroutine, running the specified callback. The coroutine will
2668     be automatically cancelled when the client gets destroyed (e.g. on logout,
2669     or loss of connection).
2670    
2671     =cut
2672    
2673 root 1.140 sub cf::client::async {
2674 root 1.96 my ($self, $cb) = @_;
2675    
2676 root 1.140 my $coro = &Coro::async ($cb);
2677 root 1.103
2678     $coro->on_destroy (sub {
2679 root 1.96 delete $self->{_coro}{$coro+0};
2680 root 1.103 });
2681 root 1.96
2682     $self->{_coro}{$coro+0} = $coro;
2683 root 1.103
2684     $coro
2685 root 1.96 }
2686    
2687     cf::client->attach (
2688     on_destroy => sub {
2689     my ($ns) = @_;
2690    
2691 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2692 root 1.96 },
2693     );
2694    
2695 root 1.95 =back
2696    
2697 root 1.70
2698     =head2 SAFE SCRIPTING
2699    
2700     Functions that provide a safe environment to compile and execute
2701     snippets of perl code without them endangering the safety of the server
2702     itself. Looping constructs, I/O operators and other built-in functionality
2703     is not available in the safe scripting environment, and the number of
2704 root 1.79 functions and methods that can be called is greatly reduced.
2705 root 1.70
2706     =cut
2707 root 1.23
2708 root 1.42 our $safe = new Safe "safe";
2709 root 1.23 our $safe_hole = new Safe::Hole;
2710    
2711     $SIG{FPE} = 'IGNORE';
2712    
2713 root 1.328 $safe->permit_only (Opcode::opset qw(
2714     :base_core :base_mem :base_orig :base_math
2715     grepstart grepwhile mapstart mapwhile
2716     sort time
2717     ));
2718 root 1.23
2719 root 1.25 # here we export the classes and methods available to script code
2720    
2721 root 1.70 =pod
2722    
2723 root 1.228 The following functions and methods are available within a safe environment:
2724 root 1.70
2725 root 1.297 cf::object
2726     contr pay_amount pay_player map x y force_find force_add
2727 elmex 1.330 insert remove name archname title slaying race
2728 root 1.297
2729     cf::object::player
2730     player
2731    
2732     cf::player
2733     peaceful
2734    
2735     cf::map
2736     trigger
2737 root 1.70
2738     =cut
2739    
2740 root 1.25 for (
2741 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2742 elmex 1.330 insert remove inv name archname title slaying race)],
2743 root 1.25 ["cf::object::player" => qw(player)],
2744     ["cf::player" => qw(peaceful)],
2745 elmex 1.91 ["cf::map" => qw(trigger)],
2746 root 1.25 ) {
2747     no strict 'refs';
2748     my ($pkg, @funs) = @$_;
2749 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2750 root 1.25 for @funs;
2751     }
2752 root 1.23
2753 root 1.70 =over 4
2754    
2755     =item @retval = safe_eval $code, [var => value, ...]
2756    
2757     Compiled and executes the given perl code snippet. additional var/value
2758     pairs result in temporary local (my) scalar variables of the given name
2759     that are available in the code snippet. Example:
2760    
2761     my $five = safe_eval '$first + $second', first => 1, second => 4;
2762    
2763     =cut
2764    
2765 root 1.23 sub safe_eval($;@) {
2766     my ($code, %vars) = @_;
2767    
2768     my $qcode = $code;
2769     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2770     $qcode =~ s/\n/\\n/g;
2771    
2772     local $_;
2773 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2774 root 1.23
2775 root 1.42 my $eval =
2776 root 1.23 "do {\n"
2777     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2778     . "#line 0 \"{$qcode}\"\n"
2779     . $code
2780     . "\n}"
2781 root 1.25 ;
2782    
2783     sub_generation_inc;
2784 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2785 root 1.25 sub_generation_inc;
2786    
2787 root 1.42 if ($@) {
2788     warn "$@";
2789     warn "while executing safe code '$code'\n";
2790     warn "with arguments " . (join " ", %vars) . "\n";
2791     }
2792    
2793 root 1.25 wantarray ? @res : $res[0]
2794 root 1.23 }
2795    
2796 root 1.69 =item cf::register_script_function $function => $cb
2797    
2798     Register a function that can be called from within map/npc scripts. The
2799     function should be reasonably secure and should be put into a package name
2800     like the extension.
2801    
2802     Example: register a function that gets called whenever a map script calls
2803     C<rent::overview>, as used by the C<rent> extension.
2804    
2805     cf::register_script_function "rent::overview" => sub {
2806     ...
2807     };
2808    
2809     =cut
2810    
2811 root 1.23 sub register_script_function {
2812     my ($fun, $cb) = @_;
2813    
2814     no strict 'refs';
2815 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2816 root 1.23 }
2817    
2818 root 1.70 =back
2819    
2820 root 1.71 =cut
2821    
2822 root 1.23 #############################################################################
2823 root 1.203 # the server's init and main functions
2824    
2825 root 1.246 sub load_facedata($) {
2826     my ($path) = @_;
2827 root 1.223
2828 root 1.334 my $enc = JSON::XS->new->utf8->canonical;
2829    
2830 root 1.229 warn "loading facedata from $path\n";
2831 root 1.223
2832 root 1.236 my $facedata;
2833     0 < aio_load $path, $facedata
2834 root 1.223 or die "$path: $!";
2835    
2836 root 1.237 $facedata = Coro::Storable::thaw $facedata;
2837 root 1.223
2838 root 1.236 $facedata->{version} == 2
2839 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
2840    
2841 root 1.334 # patch in the exptable
2842     $facedata->{resource}{"res/exp_table"} = {
2843     type => FT_RSRC,
2844     data => $enc->encode ([map cf::level_to_min_exp $_, 0 .. cf::settings->max_level]),
2845     };
2846     cf::cede_to_tick;
2847    
2848 root 1.236 {
2849     my $faces = $facedata->{faceinfo};
2850    
2851     while (my ($face, $info) = each %$faces) {
2852     my $idx = (cf::face::find $face) || cf::face::alloc $face;
2853 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
2854     cf::face::set_magicmap $idx, $info->{magicmap};
2855 root 1.334 cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32};
2856     cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64};
2857 root 1.302
2858     cf::cede_to_tick;
2859 root 1.236 }
2860    
2861     while (my ($face, $info) = each %$faces) {
2862     next unless $info->{smooth};
2863     my $idx = cf::face::find $face
2864     or next;
2865     if (my $smooth = cf::face::find $info->{smooth}) {
2866 root 1.302 cf::face::set_smooth $idx, $smooth;
2867     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
2868 root 1.236 } else {
2869     warn "smooth face '$info->{smooth}' not found for face '$face'";
2870     }
2871 root 1.302
2872     cf::cede_to_tick;
2873 root 1.236 }
2874 root 1.223 }
2875    
2876 root 1.236 {
2877     my $anims = $facedata->{animinfo};
2878    
2879     while (my ($anim, $info) = each %$anims) {
2880     cf::anim::set $anim, $info->{frames}, $info->{facings};
2881 root 1.302 cf::cede_to_tick;
2882 root 1.225 }
2883 root 1.236
2884     cf::anim::invalidate_all; # d'oh
2885 root 1.225 }
2886    
2887 root 1.302 {
2888     # TODO: for gcfclient pleasure, we should give resources
2889     # that gcfclient doesn't grok a >10000 face index.
2890     my $res = $facedata->{resource};
2891    
2892 root 1.321 my $soundconf = delete $res->{"res/sound.conf"};
2893 root 1.320
2894 root 1.302 while (my ($name, $info) = each %$res) {
2895     my $idx = (cf::face::find $name) || cf::face::alloc $name;
2896 root 1.334 my $data;
2897 root 1.307
2898 root 1.318 if ($info->{type} & 1) {
2899     # prepend meta info
2900    
2901 root 1.334 my $meta = $enc->encode ({
2902     name => $name,
2903     %{ $info->{meta} || {} },
2904     });
2905 root 1.307
2906 root 1.334 $data = pack "(w/a*)*", $meta, $info->{data};
2907 root 1.307 }
2908 root 1.302
2909 root 1.334 cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data;
2910 root 1.318 cf::face::set_type $idx, $info->{type};
2911    
2912 root 1.302 cf::cede_to_tick;
2913     }
2914 root 1.321
2915     if ($soundconf) {
2916     $soundconf = $enc->decode (delete $soundconf->{data});
2917    
2918     for (0 .. SOUND_CAST_SPELL_0 - 1) {
2919     my $sound = $soundconf->{compat}[$_]
2920     or next;
2921    
2922     my $face = cf::face::find "sound/$sound->[1]";
2923     cf::sound::set $sound->[0] => $face;
2924     cf::sound::old_sound_index $_, $face; # gcfclient-compat
2925     }
2926    
2927 root 1.326 while (my ($k, $v) = each %{$soundconf->{event}}) {
2928     my $face = cf::face::find "sound/$v";
2929     cf::sound::set $k => $face;
2930     }
2931 root 1.321 }
2932 root 1.302 }
2933    
2934 root 1.223 1
2935     }
2936    
2937 root 1.318 register_exticmd fx_want => sub {
2938     my ($ns, $want) = @_;
2939    
2940     while (my ($k, $v) = each %$want) {
2941     $ns->fx_want ($k, $v);
2942     }
2943     };
2944    
2945 root 1.253 sub reload_regions {
2946     load_resource_file "$MAPDIR/regions"
2947     or die "unable to load regions file\n";
2948 root 1.304
2949     for (cf::region::list) {
2950     $_->{match} = qr/$_->{match}/
2951     if exists $_->{match};
2952     }
2953 root 1.253 }
2954    
2955 root 1.246 sub reload_facedata {
2956 root 1.253 load_facedata "$DATADIR/facedata"
2957 root 1.246 or die "unable to load facedata\n";
2958     }
2959    
2960     sub reload_archetypes {
2961 root 1.253 load_resource_file "$DATADIR/archetypes"
2962 root 1.246 or die "unable to load archetypes\n";
2963 root 1.289 #d# NEED to laod twice to resolve forward references
2964     # this really needs to be done in an extra post-pass
2965     # (which needs to be synchronous, so solve it differently)
2966     load_resource_file "$DATADIR/archetypes"
2967     or die "unable to load archetypes\n";
2968 root 1.241 }
2969    
2970 root 1.246 sub reload_treasures {
2971 root 1.253 load_resource_file "$DATADIR/treasures"
2972 root 1.246 or die "unable to load treasurelists\n";
2973 root 1.241 }
2974    
2975 root 1.223 sub reload_resources {
2976 root 1.245 warn "reloading resource files...\n";
2977    
2978 root 1.246 reload_regions;
2979     reload_facedata;
2980 root 1.274 #reload_archetypes;#d#
2981 root 1.246 reload_archetypes;
2982     reload_treasures;
2983 root 1.245
2984     warn "finished reloading resource files\n";
2985 root 1.223 }
2986    
2987     sub init {
2988     reload_resources;
2989 root 1.203 }
2990 root 1.34
2991 root 1.73 sub cfg_load {
2992 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
2993 root 1.72 or return;
2994    
2995     local $/;
2996     *CFG = YAML::Syck::Load <$fh>;
2997 root 1.131
2998     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
2999    
3000 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3001     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3002    
3003 root 1.131 if (exists $CFG{mlockall}) {
3004     eval {
3005 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3006 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3007     };
3008     warn $@ if $@;
3009     }
3010 root 1.72 }
3011    
3012 root 1.39 sub main {
3013 root 1.108 # we must not ever block the main coroutine
3014     local $Coro::idle = sub {
3015 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3016 root 1.175 (async {
3017     Event::one_event;
3018     })->prio (Coro::PRIO_MAX);
3019 root 1.108 };
3020    
3021 root 1.73 cfg_load;
3022 root 1.210 db_init;
3023 root 1.61 load_extensions;
3024 root 1.183
3025     $TICK_WATCHER->start;
3026 root 1.34 Event::loop;
3027     }
3028    
3029     #############################################################################
3030 root 1.155 # initialisation and cleanup
3031    
3032     # install some emergency cleanup handlers
3033     BEGIN {
3034     for my $signal (qw(INT HUP TERM)) {
3035     Event->signal (
3036 root 1.189 reentrant => 0,
3037     data => WF_AUTOCANCEL,
3038     signal => $signal,
3039 root 1.191 prio => 0,
3040 root 1.189 cb => sub {
3041 root 1.155 cf::cleanup "SIG$signal";
3042     },
3043     );
3044     }
3045     }
3046    
3047 root 1.281 sub write_runtime {
3048     my $runtime = "$LOCALDIR/runtime";
3049    
3050     # first touch the runtime file to show we are still running:
3051     # the fsync below can take a very very long time.
3052    
3053     IO::AIO::aio_utime $runtime, undef, undef;
3054    
3055     my $guard = cf::lock_acquire "write_runtime";
3056    
3057     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
3058     or return;
3059    
3060     my $value = $cf::RUNTIME + 90 + 10;
3061     # 10 is the runtime save interval, for a monotonic clock
3062     # 60 allows for the watchdog to kill the server.
3063    
3064     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3065     and return;
3066    
3067     # always fsync - this file is important
3068     aio_fsync $fh
3069     and return;
3070    
3071     # touch it again to show we are up-to-date
3072     aio_utime $fh, undef, undef;
3073    
3074     close $fh
3075     or return;
3076    
3077     aio_rename "$runtime~", $runtime
3078     and return;
3079    
3080     warn "runtime file written.\n";
3081    
3082     1
3083     }
3084    
3085 root 1.156 sub emergency_save() {
3086 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3087    
3088     warn "enter emergency perl save\n";
3089    
3090     cf::sync_job {
3091     # use a peculiar iteration method to avoid tripping on perl
3092     # refcount bugs in for. also avoids problems with players
3093 root 1.167 # and maps saved/destroyed asynchronously.
3094 root 1.155 warn "begin emergency player save\n";
3095     for my $login (keys %cf::PLAYER) {
3096     my $pl = $cf::PLAYER{$login} or next;
3097     $pl->valid or next;
3098     $pl->save;
3099     }
3100     warn "end emergency player save\n";
3101    
3102     warn "begin emergency map save\n";
3103     for my $path (keys %cf::MAP) {
3104     my $map = $cf::MAP{$path} or next;
3105     $map->valid or next;
3106     $map->save;
3107     }
3108     warn "end emergency map save\n";
3109 root 1.208
3110     warn "begin emergency database checkpoint\n";
3111     BDB::db_env_txn_checkpoint $DB_ENV;
3112     warn "end emergency database checkpoint\n";
3113 root 1.155 };
3114    
3115     warn "leave emergency perl save\n";
3116     }
3117 root 1.22
3118 root 1.211 sub post_cleanup {
3119     my ($make_core) = @_;
3120    
3121     warn Carp::longmess "post_cleanup backtrace"
3122     if $make_core;
3123     }
3124    
3125 root 1.246 sub do_reload_perl() {
3126 root 1.106 # can/must only be called in main
3127     if ($Coro::current != $Coro::main) {
3128 root 1.183 warn "can only reload from main coroutine";
3129 root 1.106 return;
3130     }
3131    
3132 root 1.103 warn "reloading...";
3133    
3134 root 1.212 warn "entering sync_job";
3135    
3136 root 1.213 cf::sync_job {
3137 root 1.214 cf::write_runtime; # external watchdog should not bark
3138 root 1.212 cf::emergency_save;
3139 root 1.214 cf::write_runtime; # external watchdog should not bark
3140 root 1.183
3141 root 1.212 warn "syncing database to disk";
3142     BDB::db_env_txn_checkpoint $DB_ENV;
3143 root 1.106
3144     # if anything goes wrong in here, we should simply crash as we already saved
3145 root 1.65
3146 root 1.183 warn "cancelling all WF_AUTOCANCEL watchers";
3147 root 1.87 for (Event::all_watchers) {
3148     $_->cancel if $_->data & WF_AUTOCANCEL;
3149     }
3150 root 1.65
3151 root 1.183 warn "flushing outstanding aio requests";
3152     for (;;) {
3153 root 1.208 BDB::flush;
3154 root 1.183 IO::AIO::flush;
3155     Coro::cede;
3156 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
3157 root 1.183 warn "iterate...";
3158     }
3159    
3160 root 1.223 ++$RELOAD;
3161    
3162 root 1.183 warn "cancelling all extension coros";
3163 root 1.103 $_->cancel for values %EXT_CORO;
3164     %EXT_CORO = ();
3165    
3166 root 1.183 warn "removing commands";
3167 root 1.159 %COMMAND = ();
3168    
3169 root 1.287 warn "removing ext/exti commands";
3170     %EXTCMD = ();
3171     %EXTICMD = ();
3172 root 1.159
3173 root 1.183 warn "unloading/nuking all extensions";
3174 root 1.159 for my $pkg (@EXTS) {
3175 root 1.160 warn "... unloading $pkg";
3176 root 1.159
3177     if (my $cb = $pkg->can ("unload")) {
3178     eval {
3179     $cb->($pkg);
3180     1
3181     } or warn "$pkg unloaded, but with errors: $@";
3182     }
3183    
3184 root 1.160 warn "... nuking $pkg";
3185 root 1.159 Symbol::delete_package $pkg;
3186 root 1.65 }
3187    
3188 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
3189 root 1.65 while (my ($k, $v) = each %INC) {
3190     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3191    
3192 root 1.183 warn "... unloading $k";
3193 root 1.65 delete $INC{$k};
3194    
3195     $k =~ s/\.pm$//;
3196     $k =~ s/\//::/g;
3197    
3198     if (my $cb = $k->can ("unload_module")) {
3199     $cb->();
3200     }
3201    
3202     Symbol::delete_package $k;
3203     }
3204    
3205 root 1.183 warn "getting rid of safe::, as good as possible";
3206 root 1.65 Symbol::delete_package "safe::$_"
3207 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3208 root 1.65
3209 root 1.183 warn "unloading cf.pm \"a bit\"";
3210 root 1.65 delete $INC{"cf.pm"};
3211 root 1.252 delete $INC{"cf/pod.pm"};
3212 root 1.65
3213     # don't, removes xs symbols, too,
3214     # and global variables created in xs
3215     #Symbol::delete_package __PACKAGE__;
3216    
3217 root 1.183 warn "unload completed, starting to reload now";
3218    
3219 root 1.103 warn "reloading cf.pm";
3220 root 1.65 require cf;
3221 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3222    
3223 root 1.183 warn "loading config and database again";
3224 root 1.73 cf::cfg_load;
3225 root 1.65
3226 root 1.183 warn "loading extensions";
3227 root 1.65 cf::load_extensions;
3228    
3229 root 1.183 warn "reattaching attachments to objects/players";
3230 root 1.222 _global_reattach; # objects, sockets
3231 root 1.183 warn "reattaching attachments to maps";
3232 root 1.144 reattach $_ for values %MAP;
3233 root 1.222 warn "reattaching attachments to players";
3234     reattach $_ for values %PLAYER;
3235 root 1.183
3236 root 1.212 warn "leaving sync_job";
3237 root 1.183
3238 root 1.212 1
3239     } or do {
3240 root 1.106 warn $@;
3241     warn "error while reloading, exiting.";
3242     exit 1;
3243 root 1.212 };
3244 root 1.106
3245 root 1.159 warn "reloaded";
3246 root 1.65 };
3247    
3248 root 1.175 our $RELOAD_WATCHER; # used only during reload
3249    
3250 root 1.246 sub reload_perl() {
3251     # doing reload synchronously and two reloads happen back-to-back,
3252     # coro crashes during coro_state_free->destroy here.
3253    
3254     $RELOAD_WATCHER ||= Event->timer (
3255     reentrant => 0,
3256     after => 0,
3257     data => WF_AUTOCANCEL,
3258     cb => sub {
3259     do_reload_perl;
3260     undef $RELOAD_WATCHER;
3261     },
3262     );
3263     }
3264    
3265 root 1.111 register_command "reload" => sub {
3266 root 1.65 my ($who, $arg) = @_;
3267    
3268     if ($who->flag (FLAG_WIZ)) {
3269 root 1.175 $who->message ("reloading server.");
3270 root 1.246 async { reload_perl };
3271 root 1.65 }
3272     };
3273    
3274 root 1.27 unshift @INC, $LIBDIR;
3275 root 1.17
3276 root 1.183 my $bug_warning = 0;
3277    
3278 root 1.239 our @WAIT_FOR_TICK;
3279     our @WAIT_FOR_TICK_BEGIN;
3280    
3281     sub wait_for_tick {
3282 root 1.240 return unless $TICK_WATCHER->is_active;
3283 root 1.241 return if $Coro::current == $Coro::main;
3284    
3285 root 1.239 my $signal = new Coro::Signal;
3286     push @WAIT_FOR_TICK, $signal;
3287     $signal->wait;
3288     }
3289    
3290     sub wait_for_tick_begin {
3291 root 1.240 return unless $TICK_WATCHER->is_active;
3292 root 1.241 return if $Coro::current == $Coro::main;
3293    
3294 root 1.239 my $signal = new Coro::Signal;
3295     push @WAIT_FOR_TICK_BEGIN, $signal;
3296     $signal->wait;
3297     }
3298    
3299 root 1.268 my $min = 1e6;#d#
3300     my $avg = 10;
3301 root 1.35 $TICK_WATCHER = Event->timer (
3302 root 1.104 reentrant => 0,
3303 root 1.183 parked => 1,
3304 root 1.191 prio => 0,
3305 root 1.104 at => $NEXT_TICK || $TICK,
3306     data => WF_AUTOCANCEL,
3307     cb => sub {
3308 root 1.183 if ($Coro::current != $Coro::main) {
3309     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3310     unless ++$bug_warning > 10;
3311     return;
3312     }
3313    
3314 root 1.265 $NOW = $tick_start = Event::time;
3315 root 1.163
3316 root 1.133 cf::server_tick; # one server iteration
3317 root 1.245
3318 root 1.268 0 && sync_job {#d#
3319     for(1..10) {
3320     my $t = Event::time;
3321     my $map = my $map = new_from_path cf::map "/tmp/x.map"
3322     or die;
3323    
3324     $map->width (50);
3325     $map->height (50);
3326     $map->alloc;
3327     $map->_load_objects ("/tmp/x.map", 1);
3328     my $t = Event::time - $t;
3329    
3330     #next unless $t < 0.0013;#d#
3331     if ($t < $min) {
3332     $min = $t;
3333     }
3334     $avg = $avg * 0.99 + $t * 0.01;
3335     }
3336     warn "XXXXXXXXXXXXXXXXXX min $min avg $avg\n";#d#
3337     exit 0;
3338     # 2007-05-22 02:33:04.569 min 0.00112509727478027 avg 0.0012259249572477
3339     };
3340    
3341 root 1.133 $RUNTIME += $TICK;
3342 root 1.35 $NEXT_TICK += $TICK;
3343    
3344 root 1.214 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3345     $NEXT_RUNTIME_WRITE = $NOW + 10;
3346     Coro::async_pool {
3347     write_runtime
3348     or warn "ERROR: unable to write runtime file: $!";
3349     };
3350     }
3351    
3352 root 1.191 # my $AFTER = Event::time;
3353     # warn $AFTER - $NOW;#d#
3354 root 1.190
3355 root 1.245 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3356     $sig->send;
3357     }
3358     while (my $sig = shift @WAIT_FOR_TICK) {
3359     $sig->send;
3360     }
3361    
3362 root 1.265 $NOW = Event::time;
3363    
3364     # if we are delayed by four ticks or more, skip them all
3365     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
3366    
3367     $TICK_WATCHER->at ($NEXT_TICK);
3368     $TICK_WATCHER->start;
3369    
3370     $LOAD = ($NOW - $tick_start) / $TICK;
3371     $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3372    
3373 root 1.245 _post_tick;
3374 root 1.265
3375    
3376 root 1.35 },
3377     );
3378    
3379 root 1.206 {
3380     BDB::max_poll_time $TICK * 0.1;
3381     $BDB_POLL_WATCHER = Event->io (
3382     reentrant => 0,
3383     fd => BDB::poll_fileno,
3384     poll => 'r',
3385     prio => 0,
3386     data => WF_AUTOCANCEL,
3387     cb => \&BDB::poll_cb,
3388     );
3389     BDB::min_parallel 8;
3390    
3391     BDB::set_sync_prepare {
3392     my $status;
3393     my $current = $Coro::current;
3394     (
3395     sub {
3396     $status = $!;
3397     $current->ready; undef $current;
3398     },
3399     sub {
3400     Coro::schedule while defined $current;
3401     $! = $status;
3402     },
3403     )
3404     };
3405 root 1.77
3406 root 1.206 unless ($DB_ENV) {
3407     $DB_ENV = BDB::db_env_create;
3408    
3409     cf::sync_job {
3410 root 1.208 eval {
3411     BDB::db_env_open
3412     $DB_ENV,
3413 root 1.253 $BDBDIR,
3414 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3415     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3416     0666;
3417    
3418 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3419 root 1.208
3420     $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC, 1);
3421     $DB_ENV->set_lk_detect;
3422     };
3423    
3424     cf::cleanup "db_env_open(db): $@" if $@;
3425 root 1.206 };
3426     }
3427     }
3428    
3429     {
3430     IO::AIO::min_parallel 8;
3431    
3432     undef $Coro::AIO::WATCHER;
3433     IO::AIO::max_poll_time $TICK * 0.1;
3434     $AIO_POLL_WATCHER = Event->io (
3435     reentrant => 0,
3436 root 1.214 data => WF_AUTOCANCEL,
3437 root 1.206 fd => IO::AIO::poll_fileno,
3438     poll => 'r',
3439     prio => 6,
3440     cb => \&IO::AIO::poll_cb,
3441     );
3442     }
3443 root 1.108
3444 root 1.262 my $_log_backtrace;
3445    
3446 root 1.260 sub _log_backtrace {
3447     my ($msg, @addr) = @_;
3448    
3449 root 1.262 $msg =~ s/\n//;
3450 root 1.260
3451 root 1.262 # limit the # of concurrent backtraces
3452     if ($_log_backtrace < 2) {
3453     ++$_log_backtrace;
3454     async {
3455     my @bt = fork_call {
3456     @addr = map { sprintf "%x", $_ } @addr;
3457     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3458     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3459     or die "addr2line: $!";
3460    
3461     my @funcs;
3462     my @res = <$fh>;
3463     chomp for @res;
3464     while (@res) {
3465     my ($func, $line) = splice @res, 0, 2, ();
3466     push @funcs, "[$func] $line";
3467     }
3468 root 1.260
3469 root 1.262 @funcs
3470     };
3471 root 1.260
3472 root 1.262 LOG llevInfo, "[ABT] $msg\n";
3473     LOG llevInfo, "[ABT] $_\n" for @bt;
3474     --$_log_backtrace;
3475     };
3476     } else {
3477 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3478 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3479     }
3480 root 1.260 }
3481    
3482 root 1.249 # load additional modules
3483     use cf::pod;
3484    
3485 root 1.125 END { cf::emergency_save }
3486    
3487 root 1.1 1
3488