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