ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.345
Committed: Tue Aug 28 19:30:11 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
Changes since 1.344: +7 -6 lines
Log Message:
lock map_data in addition to map_load when loading a map, possibly avoids the crash seen today

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.344 use JSON::XS ();
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 root 1.338 my @reply;
1155    
1156 root 1.316 if (my $cb = $EXTCMD{$type}) {
1157 root 1.338 @reply = $cb->($pl, @payload);
1158     }
1159    
1160     $pl->ext_reply ($reply, @reply)
1161     if $reply;
1162 root 1.316
1163 root 1.85 } else {
1164     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1165     }
1166    
1167     cf::override;
1168     },
1169 root 1.93 );
1170 root 1.85
1171 root 1.278 sub load_extensions {
1172     cf::sync_job {
1173     my %todo;
1174    
1175     for my $path (<$LIBDIR/*.ext>) {
1176     next unless -r $path;
1177    
1178     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
1179     my $base = $1;
1180     my $pkg = $1;
1181     $pkg =~ s/[^[:word:]]/_/g;
1182     $pkg = "ext::$pkg";
1183    
1184     open my $fh, "<:utf8", $path
1185     or die "$path: $!";
1186    
1187     my $source = do { local $/; <$fh> };
1188 root 1.1
1189 root 1.278 my %ext = (
1190     path => $path,
1191     base => $base,
1192     pkg => $pkg,
1193     );
1194 root 1.1
1195 root 1.279 $ext{meta} = { map { (split /=/, $_, 2)[0, 1] } split /\s+/, $1 }
1196     if $source =~ /\A#!.*?perl.*?#\s*(.*)$/m;
1197 root 1.1
1198 root 1.278 $ext{source} =
1199     "package $pkg; use strict; use utf8;\n"
1200     . "#line 1 \"$path\"\n{\n"
1201     . $source
1202     . "\n};\n1";
1203 root 1.1
1204 root 1.278 $todo{$base} = \%ext;
1205 root 1.166 }
1206 root 1.1
1207 root 1.278 my %done;
1208     while (%todo) {
1209     my $progress;
1210    
1211     while (my ($k, $v) = each %todo) {
1212 root 1.279 for (split /,\s*/, $v->{meta}{depends}) {
1213 root 1.278 goto skip
1214     unless exists $done{$_};
1215     }
1216    
1217     warn "... loading '$k' into '$v->{pkg}'\n";
1218    
1219     unless (eval $v->{source}) {
1220     my $msg = $@ ? "$v->{path}: $@\n"
1221 root 1.279 : "$v->{base}: extension inactive.\n";
1222 root 1.278
1223     if (exists $v->{meta}{mandatory}) {
1224     warn $msg;
1225     warn "mandatory extension failed to load, exiting.\n";
1226     exit 1;
1227     }
1228    
1229 root 1.279 warn $msg;
1230 root 1.278 }
1231    
1232     $done{$k} = delete $todo{$k};
1233     push @EXTS, $v->{pkg};
1234 root 1.279 $progress = 1;
1235 root 1.278 }
1236 root 1.1
1237 root 1.278 skip:
1238     die "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n"
1239     unless $progress;
1240     }
1241     };
1242 root 1.1 }
1243    
1244 root 1.8 #############################################################################
1245 root 1.70
1246 root 1.281 =back
1247    
1248 root 1.70 =head2 CORE EXTENSIONS
1249    
1250     Functions and methods that extend core crossfire objects.
1251    
1252 root 1.143 =cut
1253    
1254     package cf::player;
1255    
1256 root 1.154 use Coro::AIO;
1257    
1258 root 1.95 =head3 cf::player
1259    
1260 root 1.70 =over 4
1261 root 1.22
1262 root 1.143 =item cf::player::find $login
1263 root 1.23
1264 root 1.143 Returns the given player object, loading it if necessary (might block).
1265 root 1.23
1266     =cut
1267    
1268 root 1.145 sub playerdir($) {
1269 root 1.253 "$PLAYERDIR/"
1270 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1271     }
1272    
1273 root 1.143 sub path($) {
1274 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1275    
1276 root 1.234 (playerdir $login) . "/playerdata"
1277 root 1.143 }
1278    
1279     sub find_active($) {
1280     $cf::PLAYER{$_[0]}
1281     and $cf::PLAYER{$_[0]}->active
1282     and $cf::PLAYER{$_[0]}
1283     }
1284    
1285     sub exists($) {
1286     my ($login) = @_;
1287    
1288     $cf::PLAYER{$login}
1289 root 1.180 or cf::sync_job { !aio_stat path $login }
1290 root 1.143 }
1291    
1292     sub find($) {
1293     return $cf::PLAYER{$_[0]} || do {
1294     my $login = $_[0];
1295    
1296     my $guard = cf::lock_acquire "user_find:$login";
1297    
1298 root 1.151 $cf::PLAYER{$_[0]} || do {
1299 root 1.234 # rename old playerfiles to new ones
1300     #TODO: remove when no longer required
1301     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1302     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1303     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1304     aio_unlink +(playerdir $login) . "/$login.pl";
1305    
1306 root 1.151 my $pl = load_pl path $login
1307     or return;
1308     $cf::PLAYER{$login} = $pl
1309     }
1310     }
1311 root 1.143 }
1312    
1313     sub save($) {
1314     my ($pl) = @_;
1315    
1316     return if $pl->{deny_save};
1317    
1318     my $path = path $pl;
1319     my $guard = cf::lock_acquire "user_save:$path";
1320    
1321     return if $pl->{deny_save};
1322 root 1.146
1323 root 1.154 aio_mkdir playerdir $pl, 0770;
1324 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1325    
1326     $pl->save_pl ($path);
1327     Coro::cede;
1328     }
1329    
1330     sub new($) {
1331     my ($login) = @_;
1332    
1333     my $self = create;
1334    
1335     $self->ob->name ($login);
1336     $self->{deny_save} = 1;
1337    
1338     $cf::PLAYER{$login} = $self;
1339    
1340     $self
1341 root 1.23 }
1342    
1343 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1344    
1345     =cut
1346    
1347     sub send_msg {
1348     my $ns = shift->ns
1349     or return;
1350     $ns->send_msg (@_);
1351     }
1352    
1353 root 1.154 =item $pl->quit_character
1354    
1355     Nukes the player without looking back. If logged in, the connection will
1356     be destroyed. May block for a long time.
1357    
1358     =cut
1359    
1360 root 1.145 sub quit_character {
1361     my ($pl) = @_;
1362    
1363 root 1.220 my $name = $pl->ob->name;
1364    
1365 root 1.145 $pl->{deny_save} = 1;
1366     $pl->password ("*"); # this should lock out the player until we nuked the dir
1367    
1368     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1369     $pl->deactivate;
1370     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1371     $pl->ns->destroy if $pl->ns;
1372    
1373     my $path = playerdir $pl;
1374     my $temp = "$path~$cf::RUNTIME~deleting~";
1375 root 1.154 aio_rename $path, $temp;
1376 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1377     $pl->destroy;
1378 root 1.220
1379     my $prefix = qr<^~\Q$name\E/>;
1380    
1381     # nuke player maps
1382     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1383    
1384 root 1.150 IO::AIO::aio_rmtree $temp;
1385 root 1.145 }
1386    
1387 pippijn 1.221 =item $pl->kick
1388    
1389     Kicks a player out of the game. This destroys the connection.
1390    
1391     =cut
1392    
1393     sub kick {
1394     my ($pl, $kicker) = @_;
1395    
1396     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1397     $pl->killer ("kicked");
1398     $pl->ns->destroy;
1399     }
1400    
1401 root 1.154 =item cf::player::list_logins
1402    
1403     Returns am arrayref of all valid playernames in the system, can take a
1404     while and may block, so not sync_job-capable, ever.
1405    
1406     =cut
1407    
1408     sub list_logins {
1409 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1410 root 1.154 or return [];
1411    
1412     my @logins;
1413    
1414     for my $login (@$dirs) {
1415     my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next;
1416     aio_read $fh, 0, 512, my $buf, 0 or next;
1417 root 1.155 $buf !~ /^password -------------$/m or next; # official not-valid tag
1418 root 1.154
1419     utf8::decode $login;
1420     push @logins, $login;
1421     }
1422    
1423     \@logins
1424     }
1425    
1426     =item $player->maps
1427    
1428 root 1.166 Returns an arrayref of map paths that are private for this
1429 root 1.154 player. May block.
1430    
1431     =cut
1432    
1433     sub maps($) {
1434     my ($pl) = @_;
1435    
1436 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1437    
1438 root 1.154 my $files = aio_readdir playerdir $pl
1439     or return;
1440    
1441     my @paths;
1442    
1443     for (@$files) {
1444     utf8::decode $_;
1445     next if /\.(?:pl|pst)$/;
1446 root 1.158 next unless /^$PATH_SEP/o;
1447 root 1.154
1448 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1449 root 1.154 }
1450    
1451     \@paths
1452     }
1453    
1454 root 1.283 =item $protocol_xml = $player->expand_cfpod ($crossfire_pod)
1455    
1456     Expand crossfire pod fragments into protocol xml.
1457    
1458     =cut
1459    
1460     sub expand_cfpod {
1461     ((my $self), (local $_)) = @_;
1462    
1463     # escape & and <
1464     s/&/&amp;/g;
1465     s/(?<![BIUGH])</&lt;/g;
1466    
1467     # this is buggy, it needs to properly take care of nested <'s
1468    
1469     1 while
1470     # replace B<>, I<>, U<> etc.
1471     s/B<([^\>]*)>/<b>$1<\/b>/
1472     || s/I<([^\>]*)>/<i>$1<\/i>/
1473     || s/U<([^\>]*)>/<u>$1<\/u>/
1474     # replace G<male|female> tags
1475     || s{G<([^>|]*)\|([^>]*)>}{
1476     $self->gender ? $2 : $1
1477     }ge
1478     # replace H<hint text>
1479 root 1.291 || s{H<([^\>]*)>}
1480     {
1481     ("<fg name=\"lightblue\">[$1 (Use hintmode to suppress hints)]</fg>",
1482     "<fg name=\"lightblue\">[Hint suppressed, see hintmode]</fg>",
1483     "")
1484     [$self->{hintmode}]
1485     }ge;
1486 root 1.283
1487     # create single paragraphs (very hackish)
1488     s/(?<=\S)\n(?=\w)/ /g;
1489    
1490 root 1.291 # compress some whitespace
1491 root 1.295 s/\s+\n/\n/g; # ws line-ends
1492     s/\n\n+/\n/g; # double lines
1493     s/^\n+//; # beginning lines
1494     s/\n+$//; # ending lines
1495 root 1.293
1496 root 1.283 $_
1497     }
1498    
1499 root 1.291 sub hintmode {
1500     $_[0]{hintmode} = $_[1] if @_ > 1;
1501     $_[0]{hintmode}
1502     }
1503    
1504 root 1.316 =item $player->ext_reply ($msgid, @msg)
1505 root 1.95
1506     Sends an ext reply to the player.
1507    
1508     =cut
1509    
1510 root 1.316 sub ext_reply($$@) {
1511     my ($self, $id, @msg) = @_;
1512 root 1.95
1513 root 1.336 $self->ns->ext_reply ($id, @msg)
1514 root 1.95 }
1515    
1516 root 1.316 =item $player->ext_msg ($type, @msg)
1517 root 1.231
1518     Sends an ext event to the client.
1519    
1520     =cut
1521    
1522 root 1.316 sub ext_msg($$@) {
1523     my ($self, $type, @msg) = @_;
1524 root 1.231
1525 root 1.316 $self->ns->ext_msg ($type, @msg);
1526 root 1.231 }
1527    
1528 root 1.238 =head3 cf::region
1529    
1530     =over 4
1531    
1532     =cut
1533    
1534     package cf::region;
1535    
1536     =item cf::region::find_by_path $path
1537    
1538 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1539 root 1.238
1540     =cut
1541    
1542     sub find_by_path($) {
1543     my ($path) = @_;
1544    
1545     my ($match, $specificity);
1546    
1547     for my $region (list) {
1548 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1549 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1550     if $region->specificity > $specificity;
1551     }
1552     }
1553    
1554     $match
1555     }
1556 root 1.143
1557 root 1.95 =back
1558    
1559 root 1.110 =head3 cf::map
1560    
1561     =over 4
1562    
1563     =cut
1564    
1565     package cf::map;
1566    
1567     use Fcntl;
1568     use Coro::AIO;
1569    
1570 root 1.166 use overload
1571 root 1.173 '""' => \&as_string,
1572     fallback => 1;
1573 root 1.166
1574 root 1.133 our $MAX_RESET = 3600;
1575     our $DEFAULT_RESET = 3000;
1576 root 1.110
1577     sub generate_random_map {
1578 root 1.166 my ($self, $rmp) = @_;
1579 root 1.110 # mit "rum" bekleckern, nicht
1580 root 1.166 $self->_create_random_map (
1581 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1582     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1583     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1584     $rmp->{exit_on_final_map},
1585     $rmp->{xsize}, $rmp->{ysize},
1586     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1587     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1588     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1589     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1590     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1591 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1592     )
1593 root 1.110 }
1594    
1595 root 1.187 =item cf::map->register ($regex, $prio)
1596    
1597     Register a handler for the map path matching the given regex at the
1598     givne priority (higher is better, built-in handlers have priority 0, the
1599     default).
1600    
1601     =cut
1602    
1603 root 1.166 sub register {
1604 root 1.187 my (undef, $regex, $prio) = @_;
1605 root 1.166 my $pkg = caller;
1606    
1607     no strict;
1608     push @{"$pkg\::ISA"}, __PACKAGE__;
1609    
1610 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1611 root 1.166 }
1612    
1613     # also paths starting with '/'
1614 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1615 root 1.166
1616 root 1.170 sub thawer_merge {
1617 root 1.172 my ($self, $merge) = @_;
1618    
1619 root 1.170 # we have to keep some variables in memory intact
1620 root 1.172 local $self->{path};
1621     local $self->{load_path};
1622 root 1.170
1623 root 1.172 $self->SUPER::thawer_merge ($merge);
1624 root 1.170 }
1625    
1626 root 1.166 sub normalise {
1627     my ($path, $base) = @_;
1628    
1629 root 1.192 $path = "$path"; # make sure its a string
1630    
1631 root 1.199 $path =~ s/\.map$//;
1632    
1633 root 1.166 # map plan:
1634     #
1635     # /! non-realised random map exit (special hack!)
1636     # {... are special paths that are not being touched
1637     # ?xxx/... are special absolute paths
1638     # ?random/... random maps
1639     # /... normal maps
1640     # ~user/... per-player map of a specific user
1641    
1642     $path =~ s/$PATH_SEP/\//go;
1643    
1644     # treat it as relative path if it starts with
1645     # something that looks reasonable
1646     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1647     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1648    
1649     $base =~ s{[^/]+/?$}{};
1650     $path = "$base/$path";
1651     }
1652    
1653     for ($path) {
1654     redo if s{//}{/};
1655     redo if s{/\.?/}{/};
1656     redo if s{/[^/]+/\.\./}{/};
1657     }
1658    
1659     $path
1660     }
1661    
1662     sub new_from_path {
1663     my (undef, $path, $base) = @_;
1664    
1665     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1666    
1667     $path = normalise $path, $base;
1668    
1669 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1670     if ($path =~ $EXT_MAP{$pkg}[1]) {
1671 root 1.166 my $self = bless cf::map::new, $pkg;
1672     $self->{path} = $path; $self->path ($path);
1673     $self->init; # pass $1 etc.
1674     return $self;
1675     }
1676     }
1677    
1678 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1679 root 1.166 ()
1680     }
1681    
1682     sub init {
1683     my ($self) = @_;
1684    
1685     $self
1686     }
1687    
1688     sub as_string {
1689     my ($self) = @_;
1690    
1691     "$self->{path}"
1692     }
1693    
1694     # the displayed name, this is a one way mapping
1695     sub visible_name {
1696     &as_string
1697     }
1698    
1699     # the original (read-only) location
1700     sub load_path {
1701     my ($self) = @_;
1702    
1703 root 1.254 "$MAPDIR/$self->{path}.map"
1704 root 1.166 }
1705    
1706     # the temporary/swap location
1707     sub save_path {
1708     my ($self) = @_;
1709    
1710 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1711 root 1.254 "$TMPDIR/$path.map"
1712 root 1.166 }
1713    
1714     # the unique path, undef == no special unique path
1715     sub uniq_path {
1716     my ($self) = @_;
1717    
1718 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1719 root 1.253 "$UNIQUEDIR/$path"
1720 root 1.166 }
1721    
1722 root 1.110 # and all this just because we cannot iterate over
1723     # all maps in C++...
1724     sub change_all_map_light {
1725     my ($change) = @_;
1726    
1727 root 1.122 $_->change_map_light ($change)
1728     for grep $_->outdoor, values %cf::MAP;
1729 root 1.110 }
1730    
1731 root 1.275 sub decay_objects {
1732     my ($self) = @_;
1733    
1734     return if $self->{deny_reset};
1735    
1736     $self->do_decay_objects;
1737     }
1738    
1739 root 1.166 sub unlink_save {
1740     my ($self) = @_;
1741    
1742     utf8::encode (my $save = $self->save_path);
1743 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1744     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1745 root 1.166 }
1746    
1747     sub load_header_from($) {
1748     my ($self, $path) = @_;
1749 root 1.110
1750     utf8::encode $path;
1751 root 1.200 #aio_open $path, O_RDONLY, 0
1752     # or return;
1753 root 1.110
1754 root 1.166 $self->_load_header ($path)
1755 root 1.110 or return;
1756    
1757 root 1.166 $self->{load_path} = $path;
1758 root 1.135
1759 root 1.166 1
1760     }
1761 root 1.110
1762 root 1.188 sub load_header_orig {
1763 root 1.166 my ($self) = @_;
1764 root 1.110
1765 root 1.166 $self->load_header_from ($self->load_path)
1766 root 1.110 }
1767    
1768 root 1.188 sub load_header_temp {
1769 root 1.166 my ($self) = @_;
1770 root 1.110
1771 root 1.166 $self->load_header_from ($self->save_path)
1772     }
1773 root 1.110
1774 root 1.188 sub prepare_temp {
1775     my ($self) = @_;
1776    
1777     $self->last_access ((delete $self->{last_access})
1778     || $cf::RUNTIME); #d#
1779     # safety
1780     $self->{instantiate_time} = $cf::RUNTIME
1781     if $self->{instantiate_time} > $cf::RUNTIME;
1782     }
1783    
1784     sub prepare_orig {
1785     my ($self) = @_;
1786    
1787     $self->{load_original} = 1;
1788     $self->{instantiate_time} = $cf::RUNTIME;
1789     $self->last_access ($cf::RUNTIME);
1790     $self->instantiate;
1791     }
1792    
1793 root 1.166 sub load_header {
1794     my ($self) = @_;
1795 root 1.110
1796 root 1.188 if ($self->load_header_temp) {
1797     $self->prepare_temp;
1798 root 1.166 } else {
1799 root 1.188 $self->load_header_orig
1800 root 1.166 or return;
1801 root 1.188 $self->prepare_orig;
1802 root 1.166 }
1803 root 1.120
1804 root 1.275 $self->{deny_reset} = 1
1805     if $self->no_reset;
1806    
1807 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1808     unless $self->default_region;
1809    
1810 root 1.166 1
1811     }
1812 root 1.110
1813 root 1.166 sub find;
1814     sub find {
1815     my ($path, $origin) = @_;
1816 root 1.134
1817 root 1.166 $path = normalise $path, $origin && $origin->path;
1818 root 1.110
1819 root 1.166 cf::lock_wait "map_find:$path";
1820 root 1.110
1821 root 1.166 $cf::MAP{$path} || do {
1822     my $guard = cf::lock_acquire "map_find:$path";
1823     my $map = new_from_path cf::map $path
1824     or return;
1825 root 1.110
1826 root 1.116 $map->{last_save} = $cf::RUNTIME;
1827 root 1.110
1828 root 1.166 $map->load_header
1829     or return;
1830 root 1.134
1831 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
1832 root 1.185 # doing this can freeze the server in a sync job, obviously
1833     #$cf::WAIT_FOR_TICK->wait;
1834 root 1.112 $map->reset;
1835 root 1.123 undef $guard;
1836 root 1.192 return find $path;
1837 root 1.112 }
1838 root 1.110
1839 root 1.166 $cf::MAP{$path} = $map
1840 root 1.110 }
1841     }
1842    
1843 root 1.188 sub pre_load { }
1844     sub post_load { }
1845    
1846 root 1.110 sub load {
1847     my ($self) = @_;
1848    
1849 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
1850    
1851 root 1.120 my $path = $self->{path};
1852    
1853 root 1.256 {
1854 root 1.345 my $guard1 = cf::lock_acquire "map_data:$path";
1855     my $guard2 = cf::lock_acquire "map_load:$path";
1856 root 1.256
1857     return if $self->in_memory != cf::MAP_SWAPPED;
1858 root 1.110
1859 root 1.256 $self->in_memory (cf::MAP_LOADING);
1860 root 1.110
1861 root 1.256 $self->alloc;
1862 root 1.188
1863 root 1.256 $self->pre_load;
1864     Coro::cede;
1865 root 1.188
1866 root 1.256 $self->_load_objects ($self->{load_path}, 1)
1867     or return;
1868 root 1.110
1869 root 1.256 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1870     if delete $self->{load_original};
1871 root 1.111
1872 root 1.256 if (my $uniq = $self->uniq_path) {
1873     utf8::encode $uniq;
1874     if (aio_open $uniq, O_RDONLY, 0) {
1875     $self->clear_unique_items;
1876     $self->_load_objects ($uniq, 0);
1877     }
1878 root 1.110 }
1879    
1880 root 1.166 Coro::cede;
1881 root 1.256 # now do the right thing for maps
1882     $self->link_multipart_objects;
1883 root 1.110 $self->difficulty ($self->estimate_difficulty)
1884     unless $self->difficulty;
1885 root 1.166 Coro::cede;
1886 root 1.256
1887     unless ($self->{deny_activate}) {
1888     $self->decay_objects;
1889     $self->fix_auto_apply;
1890     $self->update_buttons;
1891     Coro::cede;
1892     $self->set_darkness_map;
1893     Coro::cede;
1894     $self->activate;
1895     }
1896    
1897 root 1.325 $self->{last_save} = $cf::RUNTIME;
1898     $self->last_access ($cf::RUNTIME);
1899 root 1.324
1900 root 1.256 $self->in_memory (cf::MAP_IN_MEMORY);
1901 root 1.110 }
1902    
1903 root 1.188 $self->post_load;
1904 root 1.166 }
1905    
1906     sub customise_for {
1907     my ($self, $ob) = @_;
1908    
1909     return find "~" . $ob->name . "/" . $self->{path}
1910     if $self->per_player;
1911 root 1.134
1912 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
1913     # if $self->per_party;
1914    
1915 root 1.166 $self
1916 root 1.110 }
1917    
1918 root 1.157 # find and load all maps in the 3x3 area around a map
1919 root 1.333 sub load_neighbours {
1920 root 1.157 my ($map) = @_;
1921    
1922 root 1.333 my @neigh; # diagonal neighbours
1923 root 1.157
1924     for (0 .. 3) {
1925     my $neigh = $map->tile_path ($_)
1926     or next;
1927     $neigh = find $neigh, $map
1928     or next;
1929     $neigh->load;
1930    
1931 root 1.333 push @neigh,
1932     [$neigh->tile_path (($_ + 3) % 4), $neigh],
1933     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1934 root 1.157 }
1935    
1936 root 1.333 for (grep defined $_->[0], @neigh) {
1937     my ($path, $origin) = @$_;
1938     my $neigh = find $path, $origin
1939 root 1.157 or next;
1940     $neigh->load;
1941     }
1942     }
1943    
1944 root 1.133 sub find_sync {
1945 root 1.110 my ($path, $origin) = @_;
1946    
1947 root 1.157 cf::sync_job { find $path, $origin }
1948 root 1.133 }
1949    
1950     sub do_load_sync {
1951     my ($map) = @_;
1952 root 1.110
1953 root 1.339 cf::LOG cf::llevDebug | cf::logBacktrace, "do_load_sync"
1954 root 1.342 if $Coro::current == $Coro::main;
1955 root 1.339
1956 root 1.133 cf::sync_job { $map->load };
1957 root 1.110 }
1958    
1959 root 1.157 our %MAP_PREFETCH;
1960 root 1.183 our $MAP_PREFETCHER = undef;
1961 root 1.157
1962     sub find_async {
1963 root 1.339 my ($path, $origin, $load) = @_;
1964 root 1.157
1965 root 1.166 $path = normalise $path, $origin && $origin->{path};
1966 root 1.157
1967 root 1.166 if (my $map = $cf::MAP{$path}) {
1968 root 1.340 return $map if !$load || $map->in_memory == cf::MAP_IN_MEMORY;
1969 root 1.157 }
1970    
1971 root 1.339 $MAP_PREFETCH{$path} |= $load;
1972    
1973 root 1.183 $MAP_PREFETCHER ||= cf::async {
1974     while (%MAP_PREFETCH) {
1975 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
1976     if (my $map = find $k) {
1977     $map->load if $v;
1978 root 1.308 }
1979 root 1.183
1980 root 1.339 delete $MAP_PREFETCH{$k};
1981 root 1.183 }
1982     }
1983     undef $MAP_PREFETCHER;
1984     };
1985 root 1.189 $MAP_PREFETCHER->prio (6);
1986 root 1.157
1987     ()
1988     }
1989    
1990 root 1.110 sub save {
1991     my ($self) = @_;
1992    
1993 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
1994 root 1.137
1995 root 1.110 $self->{last_save} = $cf::RUNTIME;
1996    
1997     return unless $self->dirty;
1998    
1999 root 1.166 my $save = $self->save_path; utf8::encode $save;
2000     my $uniq = $self->uniq_path; utf8::encode $uniq;
2001 root 1.117
2002 root 1.110 $self->{load_path} = $save;
2003    
2004     return if $self->{deny_save};
2005    
2006 root 1.132 local $self->{last_access} = $self->last_access;#d#
2007    
2008 root 1.143 cf::async {
2009     $_->contr->save for $self->players;
2010     };
2011    
2012 root 1.110 if ($uniq) {
2013 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2014     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2015 root 1.110 } else {
2016 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2017 root 1.110 }
2018     }
2019    
2020     sub swap_out {
2021     my ($self) = @_;
2022    
2023 root 1.130 # save first because save cedes
2024     $self->save;
2025    
2026 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2027 root 1.137
2028 root 1.110 return if $self->players;
2029     return if $self->in_memory != cf::MAP_IN_MEMORY;
2030     return if $self->{deny_save};
2031    
2032     $self->clear;
2033     $self->in_memory (cf::MAP_SWAPPED);
2034     }
2035    
2036 root 1.112 sub reset_at {
2037     my ($self) = @_;
2038 root 1.110
2039     # TODO: safety, remove and allow resettable per-player maps
2040 root 1.114 return 1e99 if $self->{deny_reset};
2041 root 1.110
2042 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2043 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2044 root 1.110
2045 root 1.112 $time + $to
2046     }
2047    
2048     sub should_reset {
2049     my ($self) = @_;
2050    
2051     $self->reset_at <= $cf::RUNTIME
2052 root 1.111 }
2053    
2054 root 1.110 sub reset {
2055     my ($self) = @_;
2056    
2057 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2058 root 1.137
2059 root 1.110 return if $self->players;
2060    
2061 root 1.274 warn "resetting map ", $self->path;
2062 root 1.110
2063 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2064    
2065     # need to save uniques path
2066     unless ($self->{deny_save}) {
2067     my $uniq = $self->uniq_path; utf8::encode $uniq;
2068    
2069     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2070     if $uniq;
2071     }
2072    
2073 root 1.111 delete $cf::MAP{$self->path};
2074 root 1.110
2075 root 1.167 $self->clear;
2076    
2077 root 1.110 $_->clear_links_to ($self) for values %cf::MAP;
2078    
2079 root 1.166 $self->unlink_save;
2080 root 1.111 $self->destroy;
2081 root 1.110 }
2082    
2083 root 1.114 my $nuke_counter = "aaaa";
2084    
2085     sub nuke {
2086     my ($self) = @_;
2087    
2088 root 1.174 delete $cf::MAP{$self->path};
2089    
2090     $self->unlink_save;
2091    
2092     bless $self, "cf::map";
2093     delete $self->{deny_reset};
2094 root 1.114 $self->{deny_save} = 1;
2095     $self->reset_timeout (1);
2096 root 1.174 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2097    
2098     $cf::MAP{$self->path} = $self;
2099    
2100 root 1.114 $self->reset; # polite request, might not happen
2101     }
2102    
2103 root 1.276 =item $maps = cf::map::tmp_maps
2104    
2105     Returns an arrayref with all map paths of currently instantiated and saved
2106 root 1.277 maps. May block.
2107 root 1.276
2108     =cut
2109    
2110     sub tmp_maps() {
2111     [
2112     map {
2113     utf8::decode $_;
2114 root 1.277 /\.map$/
2115 root 1.276 ? normalise $_
2116     : ()
2117     } @{ aio_readdir $TMPDIR or [] }
2118     ]
2119     }
2120    
2121 root 1.277 =item $maps = cf::map::random_maps
2122    
2123     Returns an arrayref with all map paths of currently instantiated and saved
2124     random maps. May block.
2125    
2126     =cut
2127    
2128     sub random_maps() {
2129     [
2130     map {
2131     utf8::decode $_;
2132     /\.map$/
2133     ? normalise "?random/$_"
2134     : ()
2135     } @{ aio_readdir $RANDOMDIR or [] }
2136     ]
2137     }
2138    
2139 root 1.158 =item cf::map::unique_maps
2140    
2141 root 1.166 Returns an arrayref of paths of all shared maps that have
2142 root 1.158 instantiated unique items. May block.
2143    
2144     =cut
2145    
2146     sub unique_maps() {
2147 root 1.276 [
2148     map {
2149     utf8::decode $_;
2150 root 1.277 /\.map$/
2151 root 1.276 ? normalise $_
2152     : ()
2153     } @{ aio_readdir $UNIQUEDIR or [] }
2154     ]
2155 root 1.158 }
2156    
2157 root 1.155 package cf;
2158    
2159     =back
2160    
2161     =head3 cf::object
2162    
2163     =cut
2164    
2165     package cf::object;
2166    
2167     =over 4
2168    
2169     =item $ob->inv_recursive
2170 root 1.110
2171 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
2172 root 1.110
2173 root 1.155 =cut
2174 root 1.144
2175 root 1.155 sub inv_recursive_;
2176     sub inv_recursive_ {
2177     map { $_, inv_recursive_ $_->inv } @_
2178     }
2179 root 1.110
2180 root 1.155 sub inv_recursive {
2181     inv_recursive_ inv $_[0]
2182 root 1.110 }
2183    
2184     package cf;
2185    
2186     =back
2187    
2188 root 1.95 =head3 cf::object::player
2189    
2190     =over 4
2191    
2192 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2193 root 1.28
2194     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2195     can be C<undef>. Does the right thing when the player is currently in a
2196     dialogue with the given NPC character.
2197    
2198     =cut
2199    
2200 root 1.22 # rough implementation of a future "reply" method that works
2201     # with dialog boxes.
2202 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2203 root 1.23 sub cf::object::player::reply($$$;$) {
2204     my ($self, $npc, $msg, $flags) = @_;
2205    
2206     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2207 root 1.22
2208 root 1.24 if ($self->{record_replies}) {
2209     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2210 elmex 1.282
2211 root 1.24 } else {
2212 elmex 1.282 my $pl = $self->contr;
2213    
2214     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2215 root 1.316 my $dialog = $pl->{npc_dialog};
2216     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2217 elmex 1.282
2218     } else {
2219     $msg = $npc->name . " says: $msg" if $npc;
2220     $self->message ($msg, $flags);
2221     }
2222 root 1.24 }
2223 root 1.22 }
2224    
2225 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2226    
2227     =cut
2228    
2229     sub cf::object::send_msg {
2230     my $pl = shift->contr
2231     or return;
2232     $pl->send_msg (@_);
2233     }
2234    
2235 root 1.79 =item $player_object->may ("access")
2236    
2237     Returns wether the given player is authorized to access resource "access"
2238     (e.g. "command_wizcast").
2239    
2240     =cut
2241    
2242     sub cf::object::player::may {
2243     my ($self, $access) = @_;
2244    
2245     $self->flag (cf::FLAG_WIZ) ||
2246     (ref $cf::CFG{"may_$access"}
2247     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2248     : $cf::CFG{"may_$access"})
2249     }
2250 root 1.70
2251 root 1.115 =item $player_object->enter_link
2252    
2253     Freezes the player and moves him/her to a special map (C<{link}>).
2254    
2255 root 1.166 The player should be reasonably safe there for short amounts of time. You
2256 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
2257    
2258 root 1.166 Will never block.
2259    
2260 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2261    
2262 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2263     map. If the map is not valid (or omitted), the player will be moved back
2264     to the location he/she was before the call to C<enter_link>, or, if that
2265     fails, to the emergency map position.
2266 root 1.115
2267     Might block.
2268    
2269     =cut
2270    
2271 root 1.166 sub link_map {
2272     unless ($LINK_MAP) {
2273     $LINK_MAP = cf::map::find "{link}"
2274 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2275 root 1.166 $LINK_MAP->load;
2276     }
2277    
2278     $LINK_MAP
2279     }
2280    
2281 root 1.110 sub cf::object::player::enter_link {
2282     my ($self) = @_;
2283    
2284 root 1.259 $self->deactivate_recursive;
2285 root 1.258
2286 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2287 root 1.110
2288 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2289 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2290 root 1.110
2291 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
2292 root 1.110 }
2293    
2294     sub cf::object::player::leave_link {
2295     my ($self, $map, $x, $y) = @_;
2296    
2297 root 1.270 return unless $self->contr->active;
2298    
2299 root 1.110 my $link_pos = delete $self->{_link_pos};
2300    
2301     unless ($map) {
2302     # restore original map position
2303     ($map, $x, $y) = @{ $link_pos || [] };
2304 root 1.133 $map = cf::map::find $map;
2305 root 1.110
2306     unless ($map) {
2307     ($map, $x, $y) = @$EMERGENCY_POSITION;
2308 root 1.133 $map = cf::map::find $map
2309 root 1.110 or die "FATAL: cannot load emergency map\n";
2310     }
2311     }
2312    
2313     ($x, $y) = (-1, -1)
2314     unless (defined $x) && (defined $y);
2315    
2316     # use -1 or undef as default coordinates, not 0, 0
2317     ($x, $y) = ($map->enter_x, $map->enter_y)
2318     if $x <=0 && $y <= 0;
2319    
2320     $map->load;
2321 root 1.333 $map->load_neighbours;
2322 root 1.110
2323 root 1.143 return unless $self->contr->active;
2324 root 1.110 $self->activate_recursive;
2325 root 1.215
2326     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2327 root 1.110 $self->enter_map ($map, $x, $y);
2328     }
2329    
2330 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2331 root 1.268
2332     Moves the player to the given map-path and coordinates by first freezing
2333     her, loading and preparing them map, calling the provided $check callback
2334     that has to return the map if sucecssful, and then unfreezes the player on
2335 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2336     be called at the end of this process.
2337 root 1.110
2338     =cut
2339    
2340 root 1.270 our $GOTOGEN;
2341    
2342 root 1.136 sub cf::object::player::goto {
2343 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2344 root 1.268
2345 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2346     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2347    
2348 root 1.110 $self->enter_link;
2349    
2350 root 1.140 (async {
2351 root 1.197 my $map = eval {
2352     my $map = cf::map::find $path;
2353 root 1.268
2354     if ($map) {
2355     $map = $map->customise_for ($self);
2356     $map = $check->($map) if $check && $map;
2357     } else {
2358     $self->message ("The exit to '$path' is closed", cf::NDI_UNIQUE | cf::NDI_RED);
2359     }
2360    
2361 root 1.197 $map
2362 root 1.268 };
2363    
2364     if ($@) {
2365     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2366     LOG llevError | logBacktrace, Carp::longmess $@;
2367     }
2368 root 1.115
2369 root 1.270 if ($gen == $self->{_goto_generation}) {
2370     delete $self->{_goto_generation};
2371     $self->leave_link ($map, $x, $y);
2372     }
2373 root 1.306
2374     $done->() if $done;
2375 root 1.110 })->prio (1);
2376     }
2377    
2378     =item $player_object->enter_exit ($exit_object)
2379    
2380     =cut
2381    
2382     sub parse_random_map_params {
2383     my ($spec) = @_;
2384    
2385     my $rmp = { # defaults
2386 root 1.181 xsize => (cf::rndm 15, 40),
2387     ysize => (cf::rndm 15, 40),
2388     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2389 root 1.182 #layout => string,
2390 root 1.110 };
2391    
2392     for (split /\n/, $spec) {
2393     my ($k, $v) = split /\s+/, $_, 2;
2394    
2395     $rmp->{lc $k} = $v if (length $k) && (length $v);
2396     }
2397    
2398     $rmp
2399     }
2400    
2401     sub prepare_random_map {
2402     my ($exit) = @_;
2403    
2404 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
2405    
2406 root 1.110 # all this does is basically replace the /! path by
2407     # a new random map path (?random/...) with a seed
2408     # that depends on the exit object
2409    
2410     my $rmp = parse_random_map_params $exit->msg;
2411    
2412     if ($exit->map) {
2413 root 1.198 $rmp->{region} = $exit->region->name;
2414 root 1.110 $rmp->{origin_map} = $exit->map->path;
2415     $rmp->{origin_x} = $exit->x;
2416     $rmp->{origin_y} = $exit->y;
2417     }
2418    
2419     $rmp->{random_seed} ||= $exit->random_seed;
2420    
2421     my $data = cf::to_json $rmp;
2422     my $md5 = Digest::MD5::md5_hex $data;
2423 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2424 root 1.110
2425 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2426 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2427 root 1.177 undef $fh;
2428     aio_rename "$meta~", $meta;
2429 root 1.110
2430     $exit->slaying ("?random/$md5");
2431     $exit->msg (undef);
2432     }
2433     }
2434    
2435     sub cf::object::player::enter_exit {
2436     my ($self, $exit) = @_;
2437    
2438     return unless $self->type == cf::PLAYER;
2439    
2440 root 1.195 if ($exit->slaying eq "/!") {
2441     #TODO: this should de-fi-ni-te-ly not be a sync-job
2442 root 1.233 # the problem is that $exit might not survive long enough
2443     # so it needs to be done right now, right here
2444 root 1.195 cf::sync_job { prepare_random_map $exit };
2445     }
2446    
2447     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2448     my $hp = $exit->stats->hp;
2449     my $sp = $exit->stats->sp;
2450    
2451 root 1.110 $self->enter_link;
2452    
2453 root 1.296 # if exit is damned, update players death & WoR home-position
2454     $self->contr->savebed ($slaying, $hp, $sp)
2455     if $exit->flag (FLAG_DAMNED);
2456    
2457 root 1.140 (async {
2458 root 1.133 $self->deactivate_recursive; # just to be sure
2459 root 1.110 unless (eval {
2460 root 1.195 $self->goto ($slaying, $hp, $sp);
2461 root 1.110
2462     1;
2463     }) {
2464     $self->message ("Something went wrong deep within the crossfire server. "
2465 root 1.233 . "I'll try to bring you back to the map you were before. "
2466     . "Please report this to the dungeon master!",
2467     cf::NDI_UNIQUE | cf::NDI_RED);
2468 root 1.110
2469     warn "ERROR in enter_exit: $@";
2470     $self->leave_link;
2471     }
2472     })->prio (1);
2473     }
2474    
2475 root 1.95 =head3 cf::client
2476    
2477     =over 4
2478    
2479     =item $client->send_drawinfo ($text, $flags)
2480    
2481     Sends a drawinfo packet to the client. Circumvents output buffering so
2482     should not be used under normal circumstances.
2483    
2484 root 1.70 =cut
2485    
2486 root 1.95 sub cf::client::send_drawinfo {
2487     my ($self, $text, $flags) = @_;
2488    
2489     utf8::encode $text;
2490 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2491 root 1.95 }
2492    
2493 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2494 root 1.283
2495     Send a drawinfo or msg packet to the client, formatting the msg for the
2496     client if neccessary. C<$type> should be a string identifying the type of
2497     the message, with C<log> being the default. If C<$color> is negative, suppress
2498     the message unless the client supports the msg packet.
2499    
2500     =cut
2501    
2502     sub cf::client::send_msg {
2503 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
2504 root 1.283
2505     $msg = $self->pl->expand_cfpod ($msg);
2506    
2507 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
2508 root 1.311
2509     if (ref $channel) {
2510     # send meta info to client, if not yet sent
2511     unless (exists $self->{channel}{$channel->{id}}) {
2512     $self->{channel}{$channel->{id}} = $channel;
2513 root 1.319 $self->ext_msg (channel_info => $channel);
2514 root 1.311 }
2515    
2516     $channel = $channel->{id};
2517     }
2518    
2519 root 1.313 return unless @extra || length $msg;
2520    
2521 root 1.283 if ($self->can_msg) {
2522 root 1.323 # default colour, mask it out
2523     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2524     if $color & cf::NDI_DEF;
2525    
2526     $self->send_packet ("msg " . $self->{json_coder}->encode (
2527     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]));
2528 root 1.283 } else {
2529 root 1.323 if ($color >= 0) {
2530     # replace some tags by gcfclient-compatible ones
2531     for ($msg) {
2532     1 while
2533     s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2534     || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2535     || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2536     || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2537     || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2538     }
2539    
2540     $color &= cf::NDI_COLOR_MASK;
2541 root 1.283
2542 root 1.327 utf8::encode $msg;
2543    
2544 root 1.284 if (0 && $msg =~ /\[/) {
2545 root 1.331 # COMMAND/INFO
2546     $self->send_packet ("drawextinfo $color 10 8 $msg")
2547 root 1.283 } else {
2548 root 1.286 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2549 root 1.283 $self->send_packet ("drawinfo $color $msg")
2550     }
2551     }
2552     }
2553     }
2554    
2555 root 1.316 =item $client->ext_msg ($type, @msg)
2556 root 1.232
2557 root 1.287 Sends an ext event to the client.
2558 root 1.232
2559     =cut
2560    
2561 root 1.316 sub cf::client::ext_msg($$@) {
2562     my ($self, $type, @msg) = @_;
2563 root 1.232
2564 root 1.343 if ($self->extcmd == 2) {
2565 root 1.316 $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2566 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
2567 root 1.316 push @msg, msgtype => "event_$type";
2568     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2569     }
2570 root 1.232 }
2571 root 1.95
2572 root 1.336 =item $client->ext_reply ($msgid, @msg)
2573    
2574     Sends an ext reply to the client.
2575    
2576     =cut
2577    
2578     sub cf::client::ext_reply($$@) {
2579     my ($self, $id, @msg) = @_;
2580    
2581     if ($self->extcmd == 2) {
2582     $self->send_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
2583 root 1.343 } elsif ($self->extcmd == 1) {
2584 root 1.336 #TODO: version 1, remove
2585     unshift @msg, msgtype => "reply", msgid => $id;
2586     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2587     }
2588     }
2589    
2590 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
2591    
2592     Queues a query to the client, calling the given callback with
2593     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2594     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2595    
2596 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
2597     become reliable at some point in the future.
2598 root 1.95
2599     =cut
2600    
2601     sub cf::client::query {
2602     my ($self, $flags, $text, $cb) = @_;
2603    
2604     return unless $self->state == ST_PLAYING
2605     || $self->state == ST_SETUP
2606     || $self->state == ST_CUSTOM;
2607    
2608     $self->state (ST_CUSTOM);
2609    
2610     utf8::encode $text;
2611     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2612    
2613     $self->send_packet ($self->{query_queue}[0][0])
2614     if @{ $self->{query_queue} } == 1;
2615 root 1.287
2616     1
2617 root 1.95 }
2618    
2619     cf::client->attach (
2620 root 1.290 on_connect => sub {
2621     my ($ns) = @_;
2622    
2623     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2624     },
2625 root 1.95 on_reply => sub {
2626     my ($ns, $msg) = @_;
2627    
2628     # this weird shuffling is so that direct followup queries
2629     # get handled first
2630 root 1.128 my $queue = delete $ns->{query_queue}
2631 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2632 root 1.95
2633     (shift @$queue)->[1]->($msg);
2634 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2635 root 1.95
2636     push @{ $ns->{query_queue} }, @$queue;
2637    
2638     if (@{ $ns->{query_queue} } == @$queue) {
2639     if (@$queue) {
2640     $ns->send_packet ($ns->{query_queue}[0][0]);
2641     } else {
2642 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2643 root 1.95 }
2644     }
2645     },
2646 root 1.287 on_exticmd => sub {
2647     my ($ns, $buf) = @_;
2648    
2649 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2650 root 1.287
2651     if (ref $msg) {
2652 root 1.316 my ($type, $reply, @payload) =
2653     "ARRAY" eq ref $msg
2654     ? @$msg
2655     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2656    
2657 root 1.338 my @reply;
2658    
2659 root 1.316 if (my $cb = $EXTICMD{$type}) {
2660 root 1.338 @reply = $cb->($ns, @payload);
2661     }
2662    
2663     $ns->ext_reply ($reply, @reply)
2664     if $reply;
2665 root 1.316
2666 root 1.287 } else {
2667     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2668     }
2669    
2670     cf::override;
2671     },
2672 root 1.95 );
2673    
2674 root 1.140 =item $client->async (\&cb)
2675 root 1.96
2676     Create a new coroutine, running the specified callback. The coroutine will
2677     be automatically cancelled when the client gets destroyed (e.g. on logout,
2678     or loss of connection).
2679    
2680     =cut
2681    
2682 root 1.140 sub cf::client::async {
2683 root 1.96 my ($self, $cb) = @_;
2684    
2685 root 1.140 my $coro = &Coro::async ($cb);
2686 root 1.103
2687     $coro->on_destroy (sub {
2688 root 1.96 delete $self->{_coro}{$coro+0};
2689 root 1.103 });
2690 root 1.96
2691     $self->{_coro}{$coro+0} = $coro;
2692 root 1.103
2693     $coro
2694 root 1.96 }
2695    
2696     cf::client->attach (
2697     on_destroy => sub {
2698     my ($ns) = @_;
2699    
2700 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2701 root 1.96 },
2702     );
2703    
2704 root 1.95 =back
2705    
2706 root 1.70
2707     =head2 SAFE SCRIPTING
2708    
2709     Functions that provide a safe environment to compile and execute
2710     snippets of perl code without them endangering the safety of the server
2711     itself. Looping constructs, I/O operators and other built-in functionality
2712     is not available in the safe scripting environment, and the number of
2713 root 1.79 functions and methods that can be called is greatly reduced.
2714 root 1.70
2715     =cut
2716 root 1.23
2717 root 1.42 our $safe = new Safe "safe";
2718 root 1.23 our $safe_hole = new Safe::Hole;
2719    
2720     $SIG{FPE} = 'IGNORE';
2721    
2722 root 1.328 $safe->permit_only (Opcode::opset qw(
2723     :base_core :base_mem :base_orig :base_math
2724     grepstart grepwhile mapstart mapwhile
2725     sort time
2726     ));
2727 root 1.23
2728 root 1.25 # here we export the classes and methods available to script code
2729    
2730 root 1.70 =pod
2731    
2732 root 1.228 The following functions and methods are available within a safe environment:
2733 root 1.70
2734 root 1.297 cf::object
2735     contr pay_amount pay_player map x y force_find force_add
2736 elmex 1.341 insert remove name archname title slaying race decrease_ob_nr
2737 root 1.297
2738     cf::object::player
2739     player
2740    
2741     cf::player
2742     peaceful
2743    
2744     cf::map
2745     trigger
2746 root 1.70
2747     =cut
2748    
2749 root 1.25 for (
2750 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2751 elmex 1.341 insert remove inv name archname title slaying race
2752     decrease_ob_nr)],
2753 root 1.25 ["cf::object::player" => qw(player)],
2754     ["cf::player" => qw(peaceful)],
2755 elmex 1.91 ["cf::map" => qw(trigger)],
2756 root 1.25 ) {
2757     no strict 'refs';
2758     my ($pkg, @funs) = @$_;
2759 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2760 root 1.25 for @funs;
2761     }
2762 root 1.23
2763 root 1.70 =over 4
2764    
2765     =item @retval = safe_eval $code, [var => value, ...]
2766    
2767     Compiled and executes the given perl code snippet. additional var/value
2768     pairs result in temporary local (my) scalar variables of the given name
2769     that are available in the code snippet. Example:
2770    
2771     my $five = safe_eval '$first + $second', first => 1, second => 4;
2772    
2773     =cut
2774    
2775 root 1.23 sub safe_eval($;@) {
2776     my ($code, %vars) = @_;
2777    
2778     my $qcode = $code;
2779     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2780     $qcode =~ s/\n/\\n/g;
2781    
2782     local $_;
2783 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2784 root 1.23
2785 root 1.42 my $eval =
2786 root 1.23 "do {\n"
2787     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2788     . "#line 0 \"{$qcode}\"\n"
2789     . $code
2790     . "\n}"
2791 root 1.25 ;
2792    
2793     sub_generation_inc;
2794 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2795 root 1.25 sub_generation_inc;
2796    
2797 root 1.42 if ($@) {
2798     warn "$@";
2799     warn "while executing safe code '$code'\n";
2800     warn "with arguments " . (join " ", %vars) . "\n";
2801     }
2802    
2803 root 1.25 wantarray ? @res : $res[0]
2804 root 1.23 }
2805    
2806 root 1.69 =item cf::register_script_function $function => $cb
2807    
2808     Register a function that can be called from within map/npc scripts. The
2809     function should be reasonably secure and should be put into a package name
2810     like the extension.
2811    
2812     Example: register a function that gets called whenever a map script calls
2813     C<rent::overview>, as used by the C<rent> extension.
2814    
2815     cf::register_script_function "rent::overview" => sub {
2816     ...
2817     };
2818    
2819     =cut
2820    
2821 root 1.23 sub register_script_function {
2822     my ($fun, $cb) = @_;
2823    
2824     no strict 'refs';
2825 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2826 root 1.23 }
2827    
2828 root 1.70 =back
2829    
2830 root 1.71 =cut
2831    
2832 root 1.23 #############################################################################
2833 root 1.203 # the server's init and main functions
2834    
2835 root 1.246 sub load_facedata($) {
2836     my ($path) = @_;
2837 root 1.223
2838 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
2839 root 1.334
2840 root 1.229 warn "loading facedata from $path\n";
2841 root 1.223
2842 root 1.236 my $facedata;
2843     0 < aio_load $path, $facedata
2844 root 1.223 or die "$path: $!";
2845    
2846 root 1.237 $facedata = Coro::Storable::thaw $facedata;
2847 root 1.223
2848 root 1.236 $facedata->{version} == 2
2849 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
2850    
2851 root 1.334 # patch in the exptable
2852     $facedata->{resource}{"res/exp_table"} = {
2853     type => FT_RSRC,
2854 root 1.337 data => $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]),
2855 root 1.334 };
2856     cf::cede_to_tick;
2857    
2858 root 1.236 {
2859     my $faces = $facedata->{faceinfo};
2860    
2861     while (my ($face, $info) = each %$faces) {
2862     my $idx = (cf::face::find $face) || cf::face::alloc $face;
2863 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
2864     cf::face::set_magicmap $idx, $info->{magicmap};
2865 root 1.334 cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32};
2866     cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64};
2867 root 1.302
2868     cf::cede_to_tick;
2869 root 1.236 }
2870    
2871     while (my ($face, $info) = each %$faces) {
2872     next unless $info->{smooth};
2873     my $idx = cf::face::find $face
2874     or next;
2875     if (my $smooth = cf::face::find $info->{smooth}) {
2876 root 1.302 cf::face::set_smooth $idx, $smooth;
2877     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
2878 root 1.236 } else {
2879     warn "smooth face '$info->{smooth}' not found for face '$face'";
2880     }
2881 root 1.302
2882     cf::cede_to_tick;
2883 root 1.236 }
2884 root 1.223 }
2885    
2886 root 1.236 {
2887     my $anims = $facedata->{animinfo};
2888    
2889     while (my ($anim, $info) = each %$anims) {
2890     cf::anim::set $anim, $info->{frames}, $info->{facings};
2891 root 1.302 cf::cede_to_tick;
2892 root 1.225 }
2893 root 1.236
2894     cf::anim::invalidate_all; # d'oh
2895 root 1.225 }
2896    
2897 root 1.302 {
2898     # TODO: for gcfclient pleasure, we should give resources
2899     # that gcfclient doesn't grok a >10000 face index.
2900     my $res = $facedata->{resource};
2901    
2902 root 1.321 my $soundconf = delete $res->{"res/sound.conf"};
2903 root 1.320
2904 root 1.302 while (my ($name, $info) = each %$res) {
2905     my $idx = (cf::face::find $name) || cf::face::alloc $name;
2906 root 1.334 my $data;
2907 root 1.307
2908 root 1.318 if ($info->{type} & 1) {
2909     # prepend meta info
2910    
2911 root 1.334 my $meta = $enc->encode ({
2912     name => $name,
2913     %{ $info->{meta} || {} },
2914     });
2915 root 1.307
2916 root 1.334 $data = pack "(w/a*)*", $meta, $info->{data};
2917 root 1.337 } else {
2918     $data = $info->{data};
2919 root 1.307 }
2920 root 1.302
2921 root 1.334 cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data;
2922 root 1.318 cf::face::set_type $idx, $info->{type};
2923    
2924 root 1.302 cf::cede_to_tick;
2925     }
2926 root 1.321
2927     if ($soundconf) {
2928     $soundconf = $enc->decode (delete $soundconf->{data});
2929    
2930     for (0 .. SOUND_CAST_SPELL_0 - 1) {
2931     my $sound = $soundconf->{compat}[$_]
2932     or next;
2933    
2934     my $face = cf::face::find "sound/$sound->[1]";
2935     cf::sound::set $sound->[0] => $face;
2936     cf::sound::old_sound_index $_, $face; # gcfclient-compat
2937     }
2938    
2939 root 1.326 while (my ($k, $v) = each %{$soundconf->{event}}) {
2940     my $face = cf::face::find "sound/$v";
2941     cf::sound::set $k => $face;
2942     }
2943 root 1.321 }
2944 root 1.302 }
2945    
2946 root 1.223 1
2947     }
2948    
2949 root 1.318 register_exticmd fx_want => sub {
2950     my ($ns, $want) = @_;
2951    
2952     while (my ($k, $v) = each %$want) {
2953     $ns->fx_want ($k, $v);
2954     }
2955     };
2956    
2957 root 1.253 sub reload_regions {
2958     load_resource_file "$MAPDIR/regions"
2959     or die "unable to load regions file\n";
2960 root 1.304
2961     for (cf::region::list) {
2962     $_->{match} = qr/$_->{match}/
2963     if exists $_->{match};
2964     }
2965 root 1.253 }
2966    
2967 root 1.246 sub reload_facedata {
2968 root 1.253 load_facedata "$DATADIR/facedata"
2969 root 1.246 or die "unable to load facedata\n";
2970     }
2971    
2972     sub reload_archetypes {
2973 root 1.253 load_resource_file "$DATADIR/archetypes"
2974 root 1.246 or die "unable to load archetypes\n";
2975 root 1.289 #d# NEED to laod twice to resolve forward references
2976     # this really needs to be done in an extra post-pass
2977     # (which needs to be synchronous, so solve it differently)
2978     load_resource_file "$DATADIR/archetypes"
2979     or die "unable to load archetypes\n";
2980 root 1.241 }
2981    
2982 root 1.246 sub reload_treasures {
2983 root 1.253 load_resource_file "$DATADIR/treasures"
2984 root 1.246 or die "unable to load treasurelists\n";
2985 root 1.241 }
2986    
2987 root 1.223 sub reload_resources {
2988 root 1.245 warn "reloading resource files...\n";
2989    
2990 root 1.246 reload_regions;
2991     reload_facedata;
2992 root 1.274 #reload_archetypes;#d#
2993 root 1.246 reload_archetypes;
2994     reload_treasures;
2995 root 1.245
2996     warn "finished reloading resource files\n";
2997 root 1.223 }
2998    
2999     sub init {
3000     reload_resources;
3001 root 1.203 }
3002 root 1.34
3003 root 1.345 sub reload_config {
3004 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3005 root 1.72 or return;
3006    
3007     local $/;
3008     *CFG = YAML::Syck::Load <$fh>;
3009 root 1.131
3010     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
3011    
3012 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3013     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3014    
3015 root 1.131 if (exists $CFG{mlockall}) {
3016     eval {
3017 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3018 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3019     };
3020     warn $@ if $@;
3021     }
3022 root 1.72 }
3023    
3024 root 1.39 sub main {
3025 root 1.108 # we must not ever block the main coroutine
3026     local $Coro::idle = sub {
3027 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3028 root 1.175 (async {
3029     Event::one_event;
3030     })->prio (Coro::PRIO_MAX);
3031 root 1.108 };
3032    
3033 root 1.345 reload_config;
3034 root 1.210 db_init;
3035 root 1.61 load_extensions;
3036 root 1.183
3037     $TICK_WATCHER->start;
3038 root 1.34 Event::loop;
3039     }
3040    
3041     #############################################################################
3042 root 1.155 # initialisation and cleanup
3043    
3044     # install some emergency cleanup handlers
3045     BEGIN {
3046     for my $signal (qw(INT HUP TERM)) {
3047     Event->signal (
3048 root 1.189 reentrant => 0,
3049     data => WF_AUTOCANCEL,
3050     signal => $signal,
3051 root 1.191 prio => 0,
3052 root 1.189 cb => sub {
3053 root 1.155 cf::cleanup "SIG$signal";
3054     },
3055     );
3056     }
3057     }
3058    
3059 root 1.281 sub write_runtime {
3060     my $runtime = "$LOCALDIR/runtime";
3061    
3062     # first touch the runtime file to show we are still running:
3063     # the fsync below can take a very very long time.
3064    
3065     IO::AIO::aio_utime $runtime, undef, undef;
3066    
3067     my $guard = cf::lock_acquire "write_runtime";
3068    
3069     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
3070     or return;
3071    
3072     my $value = $cf::RUNTIME + 90 + 10;
3073     # 10 is the runtime save interval, for a monotonic clock
3074     # 60 allows for the watchdog to kill the server.
3075    
3076     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3077     and return;
3078    
3079     # always fsync - this file is important
3080     aio_fsync $fh
3081     and return;
3082    
3083     # touch it again to show we are up-to-date
3084     aio_utime $fh, undef, undef;
3085    
3086     close $fh
3087     or return;
3088    
3089     aio_rename "$runtime~", $runtime
3090     and return;
3091    
3092     warn "runtime file written.\n";
3093    
3094     1
3095     }
3096    
3097 root 1.156 sub emergency_save() {
3098 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3099    
3100     warn "enter emergency perl save\n";
3101    
3102     cf::sync_job {
3103     # use a peculiar iteration method to avoid tripping on perl
3104     # refcount bugs in for. also avoids problems with players
3105 root 1.167 # and maps saved/destroyed asynchronously.
3106 root 1.155 warn "begin emergency player save\n";
3107     for my $login (keys %cf::PLAYER) {
3108     my $pl = $cf::PLAYER{$login} or next;
3109     $pl->valid or next;
3110     $pl->save;
3111     }
3112     warn "end emergency player save\n";
3113    
3114     warn "begin emergency map save\n";
3115     for my $path (keys %cf::MAP) {
3116     my $map = $cf::MAP{$path} or next;
3117     $map->valid or next;
3118     $map->save;
3119     }
3120     warn "end emergency map save\n";
3121 root 1.208
3122     warn "begin emergency database checkpoint\n";
3123     BDB::db_env_txn_checkpoint $DB_ENV;
3124     warn "end emergency database checkpoint\n";
3125 root 1.155 };
3126    
3127     warn "leave emergency perl save\n";
3128     }
3129 root 1.22
3130 root 1.211 sub post_cleanup {
3131     my ($make_core) = @_;
3132    
3133     warn Carp::longmess "post_cleanup backtrace"
3134     if $make_core;
3135     }
3136    
3137 root 1.246 sub do_reload_perl() {
3138 root 1.106 # can/must only be called in main
3139     if ($Coro::current != $Coro::main) {
3140 root 1.183 warn "can only reload from main coroutine";
3141 root 1.106 return;
3142     }
3143    
3144 root 1.103 warn "reloading...";
3145    
3146 root 1.212 warn "entering sync_job";
3147    
3148 root 1.213 cf::sync_job {
3149 root 1.214 cf::write_runtime; # external watchdog should not bark
3150 root 1.212 cf::emergency_save;
3151 root 1.214 cf::write_runtime; # external watchdog should not bark
3152 root 1.183
3153 root 1.212 warn "syncing database to disk";
3154     BDB::db_env_txn_checkpoint $DB_ENV;
3155 root 1.106
3156     # if anything goes wrong in here, we should simply crash as we already saved
3157 root 1.65
3158 root 1.183 warn "cancelling all WF_AUTOCANCEL watchers";
3159 root 1.87 for (Event::all_watchers) {
3160     $_->cancel if $_->data & WF_AUTOCANCEL;
3161     }
3162 root 1.65
3163 root 1.183 warn "flushing outstanding aio requests";
3164     for (;;) {
3165 root 1.208 BDB::flush;
3166 root 1.183 IO::AIO::flush;
3167     Coro::cede;
3168 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
3169 root 1.183 warn "iterate...";
3170     }
3171    
3172 root 1.223 ++$RELOAD;
3173    
3174 root 1.183 warn "cancelling all extension coros";
3175 root 1.103 $_->cancel for values %EXT_CORO;
3176     %EXT_CORO = ();
3177    
3178 root 1.183 warn "removing commands";
3179 root 1.159 %COMMAND = ();
3180    
3181 root 1.287 warn "removing ext/exti commands";
3182     %EXTCMD = ();
3183     %EXTICMD = ();
3184 root 1.159
3185 root 1.183 warn "unloading/nuking all extensions";
3186 root 1.159 for my $pkg (@EXTS) {
3187 root 1.160 warn "... unloading $pkg";
3188 root 1.159
3189     if (my $cb = $pkg->can ("unload")) {
3190     eval {
3191     $cb->($pkg);
3192     1
3193     } or warn "$pkg unloaded, but with errors: $@";
3194     }
3195    
3196 root 1.160 warn "... nuking $pkg";
3197 root 1.159 Symbol::delete_package $pkg;
3198 root 1.65 }
3199    
3200 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
3201 root 1.65 while (my ($k, $v) = each %INC) {
3202     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3203    
3204 root 1.183 warn "... unloading $k";
3205 root 1.65 delete $INC{$k};
3206    
3207     $k =~ s/\.pm$//;
3208     $k =~ s/\//::/g;
3209    
3210     if (my $cb = $k->can ("unload_module")) {
3211     $cb->();
3212     }
3213    
3214     Symbol::delete_package $k;
3215     }
3216    
3217 root 1.183 warn "getting rid of safe::, as good as possible";
3218 root 1.65 Symbol::delete_package "safe::$_"
3219 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3220 root 1.65
3221 root 1.183 warn "unloading cf.pm \"a bit\"";
3222 root 1.65 delete $INC{"cf.pm"};
3223 root 1.252 delete $INC{"cf/pod.pm"};
3224 root 1.65
3225     # don't, removes xs symbols, too,
3226     # and global variables created in xs
3227     #Symbol::delete_package __PACKAGE__;
3228    
3229 root 1.183 warn "unload completed, starting to reload now";
3230    
3231 root 1.103 warn "reloading cf.pm";
3232 root 1.65 require cf;
3233 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3234    
3235 root 1.183 warn "loading config and database again";
3236 root 1.345 cf::reload_config;
3237 root 1.65
3238 root 1.183 warn "loading extensions";
3239 root 1.65 cf::load_extensions;
3240    
3241 root 1.183 warn "reattaching attachments to objects/players";
3242 root 1.222 _global_reattach; # objects, sockets
3243 root 1.183 warn "reattaching attachments to maps";
3244 root 1.144 reattach $_ for values %MAP;
3245 root 1.222 warn "reattaching attachments to players";
3246     reattach $_ for values %PLAYER;
3247 root 1.183
3248 root 1.212 warn "leaving sync_job";
3249 root 1.183
3250 root 1.212 1
3251     } or do {
3252 root 1.106 warn $@;
3253     warn "error while reloading, exiting.";
3254     exit 1;
3255 root 1.212 };
3256 root 1.106
3257 root 1.159 warn "reloaded";
3258 root 1.65 };
3259    
3260 root 1.175 our $RELOAD_WATCHER; # used only during reload
3261    
3262 root 1.246 sub reload_perl() {
3263     # doing reload synchronously and two reloads happen back-to-back,
3264     # coro crashes during coro_state_free->destroy here.
3265    
3266     $RELOAD_WATCHER ||= Event->timer (
3267     reentrant => 0,
3268     after => 0,
3269     data => WF_AUTOCANCEL,
3270     cb => sub {
3271     do_reload_perl;
3272     undef $RELOAD_WATCHER;
3273     },
3274     );
3275     }
3276    
3277 root 1.111 register_command "reload" => sub {
3278 root 1.65 my ($who, $arg) = @_;
3279    
3280     if ($who->flag (FLAG_WIZ)) {
3281 root 1.175 $who->message ("reloading server.");
3282 root 1.246 async { reload_perl };
3283 root 1.65 }
3284     };
3285    
3286 root 1.27 unshift @INC, $LIBDIR;
3287 root 1.17
3288 root 1.183 my $bug_warning = 0;
3289    
3290 root 1.239 our @WAIT_FOR_TICK;
3291     our @WAIT_FOR_TICK_BEGIN;
3292    
3293     sub wait_for_tick {
3294 root 1.240 return unless $TICK_WATCHER->is_active;
3295 root 1.241 return if $Coro::current == $Coro::main;
3296    
3297 root 1.239 my $signal = new Coro::Signal;
3298     push @WAIT_FOR_TICK, $signal;
3299     $signal->wait;
3300     }
3301    
3302     sub wait_for_tick_begin {
3303 root 1.240 return unless $TICK_WATCHER->is_active;
3304 root 1.241 return if $Coro::current == $Coro::main;
3305    
3306 root 1.239 my $signal = new Coro::Signal;
3307     push @WAIT_FOR_TICK_BEGIN, $signal;
3308     $signal->wait;
3309     }
3310    
3311 root 1.268 my $min = 1e6;#d#
3312     my $avg = 10;
3313 root 1.35 $TICK_WATCHER = Event->timer (
3314 root 1.104 reentrant => 0,
3315 root 1.183 parked => 1,
3316 root 1.191 prio => 0,
3317 root 1.104 at => $NEXT_TICK || $TICK,
3318     data => WF_AUTOCANCEL,
3319     cb => sub {
3320 root 1.183 if ($Coro::current != $Coro::main) {
3321     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3322     unless ++$bug_warning > 10;
3323     return;
3324     }
3325    
3326 root 1.265 $NOW = $tick_start = Event::time;
3327 root 1.163
3328 root 1.133 cf::server_tick; # one server iteration
3329 root 1.245
3330 root 1.268 0 && sync_job {#d#
3331     for(1..10) {
3332     my $t = Event::time;
3333     my $map = my $map = new_from_path cf::map "/tmp/x.map"
3334     or die;
3335    
3336     $map->width (50);
3337     $map->height (50);
3338     $map->alloc;
3339     $map->_load_objects ("/tmp/x.map", 1);
3340     my $t = Event::time - $t;
3341    
3342     #next unless $t < 0.0013;#d#
3343     if ($t < $min) {
3344     $min = $t;
3345     }
3346     $avg = $avg * 0.99 + $t * 0.01;
3347     }
3348     warn "XXXXXXXXXXXXXXXXXX min $min avg $avg\n";#d#
3349     exit 0;
3350     # 2007-05-22 02:33:04.569 min 0.00112509727478027 avg 0.0012259249572477
3351     };
3352    
3353 root 1.133 $RUNTIME += $TICK;
3354 root 1.35 $NEXT_TICK += $TICK;
3355    
3356 root 1.214 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3357     $NEXT_RUNTIME_WRITE = $NOW + 10;
3358     Coro::async_pool {
3359     write_runtime
3360     or warn "ERROR: unable to write runtime file: $!";
3361     };
3362     }
3363    
3364 root 1.191 # my $AFTER = Event::time;
3365     # warn $AFTER - $NOW;#d#
3366 root 1.190
3367 root 1.245 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3368     $sig->send;
3369     }
3370     while (my $sig = shift @WAIT_FOR_TICK) {
3371     $sig->send;
3372     }
3373    
3374 root 1.265 $NOW = Event::time;
3375    
3376     # if we are delayed by four ticks or more, skip them all
3377     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
3378    
3379     $TICK_WATCHER->at ($NEXT_TICK);
3380     $TICK_WATCHER->start;
3381    
3382     $LOAD = ($NOW - $tick_start) / $TICK;
3383     $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3384    
3385 root 1.245 _post_tick;
3386 root 1.265
3387    
3388 root 1.35 },
3389     );
3390    
3391 root 1.206 {
3392     BDB::max_poll_time $TICK * 0.1;
3393     $BDB_POLL_WATCHER = Event->io (
3394     reentrant => 0,
3395     fd => BDB::poll_fileno,
3396     poll => 'r',
3397     prio => 0,
3398     data => WF_AUTOCANCEL,
3399     cb => \&BDB::poll_cb,
3400     );
3401     BDB::min_parallel 8;
3402    
3403     BDB::set_sync_prepare {
3404     my $status;
3405     my $current = $Coro::current;
3406     (
3407     sub {
3408     $status = $!;
3409     $current->ready; undef $current;
3410     },
3411     sub {
3412     Coro::schedule while defined $current;
3413     $! = $status;
3414     },
3415     )
3416     };
3417 root 1.77
3418 root 1.206 unless ($DB_ENV) {
3419     $DB_ENV = BDB::db_env_create;
3420    
3421     cf::sync_job {
3422 root 1.208 eval {
3423     BDB::db_env_open
3424     $DB_ENV,
3425 root 1.253 $BDBDIR,
3426 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3427     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3428     0666;
3429    
3430 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3431 root 1.208
3432     $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC, 1);
3433     $DB_ENV->set_lk_detect;
3434     };
3435    
3436     cf::cleanup "db_env_open(db): $@" if $@;
3437 root 1.206 };
3438     }
3439     }
3440    
3441     {
3442     IO::AIO::min_parallel 8;
3443    
3444     undef $Coro::AIO::WATCHER;
3445     IO::AIO::max_poll_time $TICK * 0.1;
3446     $AIO_POLL_WATCHER = Event->io (
3447     reentrant => 0,
3448 root 1.214 data => WF_AUTOCANCEL,
3449 root 1.206 fd => IO::AIO::poll_fileno,
3450     poll => 'r',
3451     prio => 6,
3452     cb => \&IO::AIO::poll_cb,
3453     );
3454     }
3455 root 1.108
3456 root 1.262 my $_log_backtrace;
3457    
3458 root 1.260 sub _log_backtrace {
3459     my ($msg, @addr) = @_;
3460    
3461 root 1.262 $msg =~ s/\n//;
3462 root 1.260
3463 root 1.262 # limit the # of concurrent backtraces
3464     if ($_log_backtrace < 2) {
3465     ++$_log_backtrace;
3466     async {
3467     my @bt = fork_call {
3468     @addr = map { sprintf "%x", $_ } @addr;
3469     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3470     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3471     or die "addr2line: $!";
3472    
3473     my @funcs;
3474     my @res = <$fh>;
3475     chomp for @res;
3476     while (@res) {
3477     my ($func, $line) = splice @res, 0, 2, ();
3478     push @funcs, "[$func] $line";
3479     }
3480 root 1.260
3481 root 1.262 @funcs
3482     };
3483 root 1.260
3484 root 1.262 LOG llevInfo, "[ABT] $msg\n";
3485     LOG llevInfo, "[ABT] $_\n" for @bt;
3486     --$_log_backtrace;
3487     };
3488     } else {
3489 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3490 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3491     }
3492 root 1.260 }
3493    
3494 root 1.249 # load additional modules
3495     use cf::pod;
3496    
3497 root 1.125 END { cf::emergency_save }
3498    
3499 root 1.1 1
3500