ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.314
Committed: Mon Jul 23 16:53:15 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.313: +50 -0 lines
Log Message:
implement yet another et-cpu-time primitive, this time its called get_slot... might replace most uses of wait_for_tick

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