ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.501
Committed: Sat Jan 16 23:27:21 2010 UTC (14 years, 4 months ago) by root
Branch: MAIN
Changes since 1.500: +2 -2 lines
Log Message:
safe woes...

File Contents

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