ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.315
Committed: Mon Jul 23 17:53:55 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.314: +10 -4 lines
Log Message:
much better, still label duplication

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