ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.458
Committed: Sat Dec 13 20:34:37 2008 UTC (15 years, 5 months ago) by root
Branch: MAIN
Changes since 1.457: +4 -3 lines
Log Message:
take advantage of Guard module

File Contents

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