ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.508
Committed: Sun Mar 28 22:29:50 2010 UTC (14 years, 1 month ago) by root
Branch: MAIN
Changes since 1.507: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

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