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