ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.479
Committed: Thu Oct 8 05:04:27 2009 UTC (14 years, 7 months ago) by root
Branch: MAIN
Changes since 1.478: +16 -0 lines
Log Message:
*** empty log message ***

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