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