ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.502
Committed: Sat Jan 23 20:24:50 2010 UTC (14 years, 4 months ago) by root
Branch: MAIN
Changes since 1.501: +18 -9 lines
Log Message:
get_slot fix

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