ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.487
Committed: Fri Oct 16 01:56:41 2009 UTC (14 years, 7 months ago) by root
Branch: MAIN
Changes since 1.486: +6 -0 lines
Log Message:
*** empty log message ***

File Contents

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