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