ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.475
Committed: Tue Jul 21 06:17:00 2009 UTC (14 years, 10 months ago) by root
Branch: MAIN
CVS Tags: rel-2_79
Changes since 1.474: +1 -0 lines
Log Message:
*** empty log message ***

File Contents

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