ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.512
Committed: Sun Apr 11 04:52:07 2010 UTC (14 years, 1 month ago) by root
Branch: MAIN
Changes since 1.511: +9 -9 lines
Log Message:
prefer AE functions over EV functions#

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.512 my $time = AE::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.512 my $time = AE::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.509 on_unknown_command => sub {
1316 root 1.85 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 root 1.511 cf::player->attach (
1539     on_load => sub {
1540     my ($pl, $path) = @_;
1541    
1542     # restore slots saved in save, below
1543     my $slots = delete $pl->{_slots};
1544    
1545     $pl->ob->current_weapon ($slots->[0]);
1546     $pl->combat_ob ($slots->[1]);
1547     $pl->ranged_ob ($slots->[2]);
1548     },
1549     );
1550    
1551 root 1.143 sub save($) {
1552     my ($pl) = @_;
1553    
1554     return if $pl->{deny_save};
1555    
1556     my $path = path $pl;
1557     my $guard = cf::lock_acquire "user_save:$path";
1558    
1559     return if $pl->{deny_save};
1560 root 1.146
1561 root 1.154 aio_mkdir playerdir $pl, 0770;
1562 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1563    
1564 root 1.420 cf::get_slot 0.01;
1565    
1566 root 1.511 # save slots, to be restored later
1567     local $pl->{_slots} = [$pl->ob->current_weapon, $pl->combat_ob, $pl->ranged_ob];
1568    
1569 root 1.143 $pl->save_pl ($path);
1570 root 1.346 cf::cede_to_tick;
1571 root 1.143 }
1572    
1573     sub new($) {
1574     my ($login) = @_;
1575    
1576     my $self = create;
1577    
1578     $self->ob->name ($login);
1579     $self->{deny_save} = 1;
1580    
1581     $cf::PLAYER{$login} = $self;
1582    
1583     $self
1584 root 1.23 }
1585    
1586 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1587    
1588     =cut
1589    
1590     sub send_msg {
1591     my $ns = shift->ns
1592     or return;
1593     $ns->send_msg (@_);
1594     }
1595    
1596 root 1.154 =item $pl->quit_character
1597    
1598     Nukes the player without looking back. If logged in, the connection will
1599     be destroyed. May block for a long time.
1600    
1601     =cut
1602    
1603 root 1.145 sub quit_character {
1604     my ($pl) = @_;
1605    
1606 root 1.220 my $name = $pl->ob->name;
1607    
1608 root 1.145 $pl->{deny_save} = 1;
1609 root 1.443 $pl->password ("*"); # this should lock out the player until we have nuked the dir
1610 root 1.145
1611     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1612     $pl->deactivate;
1613 root 1.432 my $killer = cf::arch::get "killer_quit"; $pl->killer ($killer); $killer->destroy;
1614     $pl->ob->check_score;
1615 root 1.145 $pl->invoke (cf::EVENT_PLAYER_QUIT);
1616     $pl->ns->destroy if $pl->ns;
1617    
1618     my $path = playerdir $pl;
1619     my $temp = "$path~$cf::RUNTIME~deleting~";
1620 root 1.154 aio_rename $path, $temp;
1621 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1622     $pl->destroy;
1623 root 1.220
1624     my $prefix = qr<^~\Q$name\E/>;
1625    
1626     # nuke player maps
1627     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1628    
1629 root 1.150 IO::AIO::aio_rmtree $temp;
1630 root 1.145 }
1631    
1632 pippijn 1.221 =item $pl->kick
1633    
1634     Kicks a player out of the game. This destroys the connection.
1635    
1636     =cut
1637    
1638     sub kick {
1639     my ($pl, $kicker) = @_;
1640    
1641     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1642     $pl->killer ("kicked");
1643     $pl->ns->destroy;
1644     }
1645    
1646 root 1.154 =item cf::player::list_logins
1647    
1648     Returns am arrayref of all valid playernames in the system, can take a
1649     while and may block, so not sync_job-capable, ever.
1650    
1651     =cut
1652    
1653     sub list_logins {
1654 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1655 root 1.154 or return [];
1656    
1657     my @logins;
1658    
1659     for my $login (@$dirs) {
1660 root 1.354 my $path = path $login;
1661    
1662     # a .pst is a dead give-away for a valid player
1663 root 1.427 # if no pst file found, open and chekc for blocked users
1664     if (aio_stat "$path.pst") {
1665 root 1.354 my $fh = aio_open $path, Fcntl::O_RDONLY, 0 or next;
1666     aio_read $fh, 0, 512, my $buf, 0 or next;
1667     $buf !~ /^password -------------$/m or next; # official not-valid tag
1668     }
1669 root 1.154
1670     utf8::decode $login;
1671     push @logins, $login;
1672     }
1673    
1674     \@logins
1675     }
1676    
1677     =item $player->maps
1678    
1679 root 1.166 Returns an arrayref of map paths that are private for this
1680 root 1.154 player. May block.
1681    
1682     =cut
1683    
1684     sub maps($) {
1685     my ($pl) = @_;
1686    
1687 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1688    
1689 root 1.154 my $files = aio_readdir playerdir $pl
1690     or return;
1691    
1692     my @paths;
1693    
1694     for (@$files) {
1695     utf8::decode $_;
1696     next if /\.(?:pl|pst)$/;
1697 root 1.158 next unless /^$PATH_SEP/o;
1698 root 1.154
1699 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1700 root 1.154 }
1701    
1702     \@paths
1703     }
1704    
1705 root 1.447 =item $protocol_xml = $player->expand_cfpod ($cfpod)
1706 root 1.283
1707 root 1.447 Expand deliantra pod fragments into protocol xml.
1708 root 1.283
1709 root 1.316 =item $player->ext_reply ($msgid, @msg)
1710 root 1.95
1711     Sends an ext reply to the player.
1712    
1713     =cut
1714    
1715 root 1.316 sub ext_reply($$@) {
1716     my ($self, $id, @msg) = @_;
1717 root 1.95
1718 root 1.336 $self->ns->ext_reply ($id, @msg)
1719 root 1.95 }
1720    
1721 root 1.316 =item $player->ext_msg ($type, @msg)
1722 root 1.231
1723     Sends an ext event to the client.
1724    
1725     =cut
1726    
1727 root 1.316 sub ext_msg($$@) {
1728     my ($self, $type, @msg) = @_;
1729 root 1.231
1730 root 1.316 $self->ns->ext_msg ($type, @msg);
1731 root 1.231 }
1732    
1733 root 1.238 =head3 cf::region
1734    
1735     =over 4
1736    
1737     =cut
1738    
1739     package cf::region;
1740    
1741     =item cf::region::find_by_path $path
1742    
1743 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1744 root 1.238
1745     =cut
1746    
1747     sub find_by_path($) {
1748     my ($path) = @_;
1749    
1750     my ($match, $specificity);
1751    
1752     for my $region (list) {
1753 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1754 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1755     if $region->specificity > $specificity;
1756     }
1757     }
1758    
1759     $match
1760     }
1761 root 1.143
1762 root 1.95 =back
1763    
1764 root 1.110 =head3 cf::map
1765    
1766     =over 4
1767    
1768     =cut
1769    
1770     package cf::map;
1771    
1772     use Fcntl;
1773     use Coro::AIO;
1774    
1775 root 1.166 use overload
1776 root 1.173 '""' => \&as_string,
1777     fallback => 1;
1778 root 1.166
1779 root 1.133 our $MAX_RESET = 3600;
1780     our $DEFAULT_RESET = 3000;
1781 root 1.110
1782     sub generate_random_map {
1783 root 1.166 my ($self, $rmp) = @_;
1784 root 1.418
1785     my $lock = cf::lock_acquire "generate_random_map"; # the random map generator is NOT reentrant ATM
1786    
1787 root 1.110 # mit "rum" bekleckern, nicht
1788 root 1.166 $self->_create_random_map (
1789 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1790 root 1.508 $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle}, $rmp->{miningstyle},
1791 root 1.110 $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1792     $rmp->{exit_on_final_map},
1793     $rmp->{xsize}, $rmp->{ysize},
1794     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1795     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1796     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1797     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1798     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1799 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1800     )
1801 root 1.110 }
1802    
1803 root 1.187 =item cf::map->register ($regex, $prio)
1804    
1805     Register a handler for the map path matching the given regex at the
1806     givne priority (higher is better, built-in handlers have priority 0, the
1807     default).
1808    
1809     =cut
1810    
1811 root 1.166 sub register {
1812 root 1.187 my (undef, $regex, $prio) = @_;
1813 root 1.166 my $pkg = caller;
1814    
1815     no strict;
1816     push @{"$pkg\::ISA"}, __PACKAGE__;
1817    
1818 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1819 root 1.166 }
1820    
1821     # also paths starting with '/'
1822 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1823 root 1.166
1824 root 1.170 sub thawer_merge {
1825 root 1.172 my ($self, $merge) = @_;
1826    
1827 root 1.170 # we have to keep some variables in memory intact
1828 root 1.172 local $self->{path};
1829     local $self->{load_path};
1830 root 1.170
1831 root 1.172 $self->SUPER::thawer_merge ($merge);
1832 root 1.170 }
1833    
1834 root 1.166 sub normalise {
1835     my ($path, $base) = @_;
1836    
1837 root 1.192 $path = "$path"; # make sure its a string
1838    
1839 root 1.199 $path =~ s/\.map$//;
1840    
1841 root 1.166 # map plan:
1842     #
1843     # /! non-realised random map exit (special hack!)
1844     # {... are special paths that are not being touched
1845     # ?xxx/... are special absolute paths
1846     # ?random/... random maps
1847     # /... normal maps
1848     # ~user/... per-player map of a specific user
1849    
1850     $path =~ s/$PATH_SEP/\//go;
1851    
1852     # treat it as relative path if it starts with
1853     # something that looks reasonable
1854     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1855     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1856    
1857     $base =~ s{[^/]+/?$}{};
1858     $path = "$base/$path";
1859     }
1860    
1861     for ($path) {
1862     redo if s{//}{/};
1863     redo if s{/\.?/}{/};
1864     redo if s{/[^/]+/\.\./}{/};
1865     }
1866    
1867     $path
1868     }
1869    
1870     sub new_from_path {
1871     my (undef, $path, $base) = @_;
1872    
1873     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1874    
1875     $path = normalise $path, $base;
1876    
1877 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1878     if ($path =~ $EXT_MAP{$pkg}[1]) {
1879 root 1.166 my $self = bless cf::map::new, $pkg;
1880     $self->{path} = $path; $self->path ($path);
1881     $self->init; # pass $1 etc.
1882     return $self;
1883     }
1884     }
1885    
1886 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1887 root 1.166 ()
1888     }
1889    
1890     sub init {
1891     my ($self) = @_;
1892    
1893     $self
1894     }
1895    
1896     sub as_string {
1897     my ($self) = @_;
1898    
1899     "$self->{path}"
1900     }
1901    
1902     # the displayed name, this is a one way mapping
1903     sub visible_name {
1904     &as_string
1905     }
1906    
1907     # the original (read-only) location
1908     sub load_path {
1909     my ($self) = @_;
1910    
1911 root 1.254 "$MAPDIR/$self->{path}.map"
1912 root 1.166 }
1913    
1914     # the temporary/swap location
1915     sub save_path {
1916     my ($self) = @_;
1917    
1918 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
1919 root 1.254 "$TMPDIR/$path.map"
1920 root 1.166 }
1921    
1922     # the unique path, undef == no special unique path
1923     sub uniq_path {
1924     my ($self) = @_;
1925    
1926 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
1927 root 1.253 "$UNIQUEDIR/$path"
1928 root 1.166 }
1929    
1930 root 1.275 sub decay_objects {
1931     my ($self) = @_;
1932    
1933     return if $self->{deny_reset};
1934    
1935     $self->do_decay_objects;
1936     }
1937    
1938 root 1.166 sub unlink_save {
1939     my ($self) = @_;
1940    
1941     utf8::encode (my $save = $self->save_path);
1942 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1943     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1944 root 1.166 }
1945    
1946     sub load_header_from($) {
1947     my ($self, $path) = @_;
1948 root 1.110
1949     utf8::encode $path;
1950 root 1.356 my $f = new_from_file cf::object::thawer $path
1951     or return;
1952 root 1.110
1953 root 1.356 $self->_load_header ($f)
1954 root 1.110 or return;
1955    
1956 root 1.356 local $MAP_LOADING{$self->{path}} = $self;
1957     $f->resolve_delayed_derefs;
1958    
1959 root 1.166 $self->{load_path} = $path;
1960 root 1.135
1961 root 1.166 1
1962     }
1963 root 1.110
1964 root 1.188 sub load_header_orig {
1965 root 1.166 my ($self) = @_;
1966 root 1.110
1967 root 1.166 $self->load_header_from ($self->load_path)
1968 root 1.110 }
1969    
1970 root 1.188 sub load_header_temp {
1971 root 1.166 my ($self) = @_;
1972 root 1.110
1973 root 1.166 $self->load_header_from ($self->save_path)
1974     }
1975 root 1.110
1976 root 1.188 sub prepare_temp {
1977     my ($self) = @_;
1978    
1979     $self->last_access ((delete $self->{last_access})
1980     || $cf::RUNTIME); #d#
1981     # safety
1982     $self->{instantiate_time} = $cf::RUNTIME
1983     if $self->{instantiate_time} > $cf::RUNTIME;
1984     }
1985    
1986     sub prepare_orig {
1987     my ($self) = @_;
1988    
1989     $self->{load_original} = 1;
1990     $self->{instantiate_time} = $cf::RUNTIME;
1991     $self->last_access ($cf::RUNTIME);
1992     $self->instantiate;
1993     }
1994    
1995 root 1.166 sub load_header {
1996     my ($self) = @_;
1997 root 1.110
1998 root 1.188 if ($self->load_header_temp) {
1999     $self->prepare_temp;
2000 root 1.166 } else {
2001 root 1.188 $self->load_header_orig
2002 root 1.166 or return;
2003 root 1.188 $self->prepare_orig;
2004 root 1.166 }
2005 root 1.120
2006 root 1.275 $self->{deny_reset} = 1
2007     if $self->no_reset;
2008    
2009 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
2010     unless $self->default_region;
2011    
2012 root 1.166 1
2013     }
2014 root 1.110
2015 root 1.166 sub find;
2016     sub find {
2017     my ($path, $origin) = @_;
2018 root 1.134
2019 root 1.166 $path = normalise $path, $origin && $origin->path;
2020 root 1.110
2021 root 1.459 my $guard1 = cf::lock_acquire "map_data:$path";#d#remove
2022     my $guard2 = cf::lock_acquire "map_find:$path";
2023 root 1.110
2024 root 1.166 $cf::MAP{$path} || do {
2025     my $map = new_from_path cf::map $path
2026     or return;
2027 root 1.110
2028 root 1.116 $map->{last_save} = $cf::RUNTIME;
2029 root 1.110
2030 root 1.166 $map->load_header
2031     or return;
2032 root 1.134
2033 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
2034 root 1.185 # doing this can freeze the server in a sync job, obviously
2035     #$cf::WAIT_FOR_TICK->wait;
2036 root 1.429 undef $guard2;
2037 root 1.358 undef $guard1;
2038 root 1.112 $map->reset;
2039 root 1.192 return find $path;
2040 root 1.112 }
2041 root 1.110
2042 root 1.166 $cf::MAP{$path} = $map
2043 root 1.110 }
2044     }
2045    
2046 root 1.500 sub pre_load { }
2047     #sub post_load { } # XS
2048 root 1.188
2049 root 1.110 sub load {
2050     my ($self) = @_;
2051    
2052 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
2053    
2054 root 1.120 my $path = $self->{path};
2055    
2056 root 1.256 {
2057 root 1.357 my $guard = cf::lock_acquire "map_data:$path";
2058 root 1.256
2059 root 1.357 return unless $self->valid;
2060 root 1.360 return unless $self->in_memory == cf::MAP_SWAPPED;
2061 root 1.110
2062 root 1.256 $self->in_memory (cf::MAP_LOADING);
2063 root 1.110
2064 root 1.256 $self->alloc;
2065 root 1.188
2066 root 1.256 $self->pre_load;
2067 root 1.346 cf::cede_to_tick;
2068 root 1.188
2069 root 1.356 my $f = new_from_file cf::object::thawer $self->{load_path};
2070     $f->skip_block;
2071     $self->_load_objects ($f)
2072 root 1.256 or return;
2073 root 1.110
2074 root 1.439 $self->post_load_original
2075 root 1.256 if delete $self->{load_original};
2076 root 1.111
2077 root 1.256 if (my $uniq = $self->uniq_path) {
2078     utf8::encode $uniq;
2079 root 1.356 unless (aio_stat $uniq) {
2080     if (my $f = new_from_file cf::object::thawer $uniq) {
2081     $self->clear_unique_items;
2082     $self->_load_objects ($f);
2083     $f->resolve_delayed_derefs;
2084     }
2085 root 1.256 }
2086 root 1.110 }
2087    
2088 root 1.356 $f->resolve_delayed_derefs;
2089    
2090 root 1.346 cf::cede_to_tick;
2091 root 1.256 # now do the right thing for maps
2092     $self->link_multipart_objects;
2093 root 1.110 $self->difficulty ($self->estimate_difficulty)
2094     unless $self->difficulty;
2095 root 1.346 cf::cede_to_tick;
2096 root 1.256
2097     unless ($self->{deny_activate}) {
2098     $self->decay_objects;
2099     $self->fix_auto_apply;
2100     $self->update_buttons;
2101 root 1.346 cf::cede_to_tick;
2102 root 1.256 $self->activate;
2103     }
2104    
2105 root 1.325 $self->{last_save} = $cf::RUNTIME;
2106     $self->last_access ($cf::RUNTIME);
2107 root 1.324
2108 root 1.420 $self->in_memory (cf::MAP_ACTIVE);
2109 root 1.110 }
2110    
2111 root 1.188 $self->post_load;
2112 root 1.166 }
2113    
2114 root 1.507 # customize the map for a given player, i.e.
2115     # return the _real_ map. used by e.g. per-player
2116     # maps to change the path to ~playername/mappath
2117 root 1.166 sub customise_for {
2118     my ($self, $ob) = @_;
2119    
2120     return find "~" . $ob->name . "/" . $self->{path}
2121     if $self->per_player;
2122 root 1.134
2123 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
2124     # if $self->per_party;
2125    
2126 root 1.166 $self
2127 root 1.110 }
2128    
2129 root 1.157 # find and load all maps in the 3x3 area around a map
2130 root 1.333 sub load_neighbours {
2131 root 1.157 my ($map) = @_;
2132    
2133 root 1.333 my @neigh; # diagonal neighbours
2134 root 1.157
2135     for (0 .. 3) {
2136     my $neigh = $map->tile_path ($_)
2137     or next;
2138     $neigh = find $neigh, $map
2139     or next;
2140     $neigh->load;
2141    
2142 root 1.333 push @neigh,
2143     [$neigh->tile_path (($_ + 3) % 4), $neigh],
2144     [$neigh->tile_path (($_ + 1) % 4), $neigh];
2145 root 1.157 }
2146    
2147 root 1.333 for (grep defined $_->[0], @neigh) {
2148     my ($path, $origin) = @$_;
2149     my $neigh = find $path, $origin
2150 root 1.157 or next;
2151     $neigh->load;
2152     }
2153     }
2154    
2155 root 1.133 sub find_sync {
2156 root 1.110 my ($path, $origin) = @_;
2157    
2158 root 1.157 cf::sync_job { find $path, $origin }
2159 root 1.133 }
2160    
2161     sub do_load_sync {
2162     my ($map) = @_;
2163 root 1.110
2164 root 1.339 cf::LOG cf::llevDebug | cf::logBacktrace, "do_load_sync"
2165 root 1.342 if $Coro::current == $Coro::main;
2166 root 1.339
2167 root 1.133 cf::sync_job { $map->load };
2168 root 1.110 }
2169    
2170 root 1.157 our %MAP_PREFETCH;
2171 root 1.183 our $MAP_PREFETCHER = undef;
2172 root 1.157
2173     sub find_async {
2174 root 1.339 my ($path, $origin, $load) = @_;
2175 root 1.157
2176 root 1.166 $path = normalise $path, $origin && $origin->{path};
2177 root 1.157
2178 root 1.166 if (my $map = $cf::MAP{$path}) {
2179 root 1.420 return $map if !$load || $map->in_memory == cf::MAP_ACTIVE;
2180 root 1.157 }
2181    
2182 root 1.339 $MAP_PREFETCH{$path} |= $load;
2183    
2184 root 1.183 $MAP_PREFETCHER ||= cf::async {
2185 root 1.374 $Coro::current->{desc} = "map prefetcher";
2186    
2187 root 1.183 while (%MAP_PREFETCH) {
2188 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
2189     if (my $map = find $k) {
2190     $map->load if $v;
2191 root 1.308 }
2192 root 1.183
2193 root 1.339 delete $MAP_PREFETCH{$k};
2194 root 1.183 }
2195     }
2196     undef $MAP_PREFETCHER;
2197     };
2198 root 1.189 $MAP_PREFETCHER->prio (6);
2199 root 1.157
2200     ()
2201     }
2202    
2203 root 1.110 sub save {
2204     my ($self) = @_;
2205    
2206 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2207 root 1.137
2208 root 1.110 $self->{last_save} = $cf::RUNTIME;
2209    
2210     return unless $self->dirty;
2211    
2212 root 1.166 my $save = $self->save_path; utf8::encode $save;
2213     my $uniq = $self->uniq_path; utf8::encode $uniq;
2214 root 1.117
2215 root 1.110 $self->{load_path} = $save;
2216    
2217     return if $self->{deny_save};
2218    
2219 root 1.132 local $self->{last_access} = $self->last_access;#d#
2220    
2221 root 1.143 cf::async {
2222 root 1.374 $Coro::current->{desc} = "map player save";
2223 root 1.143 $_->contr->save for $self->players;
2224     };
2225    
2226 root 1.420 cf::get_slot 0.02;
2227    
2228 root 1.110 if ($uniq) {
2229 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2230     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2231 root 1.110 } else {
2232 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2233 root 1.110 }
2234     }
2235    
2236     sub swap_out {
2237     my ($self) = @_;
2238    
2239 root 1.130 # save first because save cedes
2240     $self->save;
2241    
2242 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2243 root 1.137
2244 root 1.110 return if $self->players;
2245 root 1.420 return if $self->in_memory != cf::MAP_ACTIVE;
2246 root 1.110 return if $self->{deny_save};
2247    
2248 root 1.359 $self->in_memory (cf::MAP_SWAPPED);
2249    
2250 root 1.358 $self->deactivate;
2251 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2252 root 1.110 $self->clear;
2253     }
2254    
2255 root 1.112 sub reset_at {
2256     my ($self) = @_;
2257 root 1.110
2258     # TODO: safety, remove and allow resettable per-player maps
2259 root 1.114 return 1e99 if $self->{deny_reset};
2260 root 1.110
2261 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2262 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2263 root 1.110
2264 root 1.112 $time + $to
2265     }
2266    
2267     sub should_reset {
2268     my ($self) = @_;
2269    
2270     $self->reset_at <= $cf::RUNTIME
2271 root 1.111 }
2272    
2273 root 1.110 sub reset {
2274     my ($self) = @_;
2275    
2276 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2277 root 1.137
2278 root 1.110 return if $self->players;
2279    
2280 root 1.478 warn "resetting map ", $self->path, "\n";
2281 root 1.110
2282 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2283    
2284     # need to save uniques path
2285     unless ($self->{deny_save}) {
2286     my $uniq = $self->uniq_path; utf8::encode $uniq;
2287    
2288     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2289     if $uniq;
2290     }
2291    
2292 root 1.111 delete $cf::MAP{$self->path};
2293 root 1.110
2294 root 1.358 $self->deactivate;
2295 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2296 root 1.167 $self->clear;
2297    
2298 root 1.166 $self->unlink_save;
2299 root 1.111 $self->destroy;
2300 root 1.110 }
2301    
2302 root 1.114 my $nuke_counter = "aaaa";
2303    
2304     sub nuke {
2305     my ($self) = @_;
2306    
2307 root 1.349 {
2308     my $lock = cf::lock_acquire "map_data:$self->{path}";
2309    
2310     delete $cf::MAP{$self->path};
2311 root 1.174
2312 root 1.351 $self->unlink_save;
2313    
2314 root 1.349 bless $self, "cf::map";
2315     delete $self->{deny_reset};
2316     $self->{deny_save} = 1;
2317     $self->reset_timeout (1);
2318     $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2319 root 1.174
2320 root 1.349 $cf::MAP{$self->path} = $self;
2321     }
2322 root 1.174
2323 root 1.114 $self->reset; # polite request, might not happen
2324     }
2325    
2326 root 1.276 =item $maps = cf::map::tmp_maps
2327    
2328     Returns an arrayref with all map paths of currently instantiated and saved
2329 root 1.277 maps. May block.
2330 root 1.276
2331     =cut
2332    
2333     sub tmp_maps() {
2334     [
2335     map {
2336     utf8::decode $_;
2337 root 1.277 /\.map$/
2338 root 1.276 ? normalise $_
2339     : ()
2340     } @{ aio_readdir $TMPDIR or [] }
2341     ]
2342     }
2343    
2344 root 1.277 =item $maps = cf::map::random_maps
2345    
2346     Returns an arrayref with all map paths of currently instantiated and saved
2347     random maps. May block.
2348    
2349     =cut
2350    
2351     sub random_maps() {
2352     [
2353     map {
2354     utf8::decode $_;
2355     /\.map$/
2356     ? normalise "?random/$_"
2357     : ()
2358     } @{ aio_readdir $RANDOMDIR or [] }
2359     ]
2360     }
2361    
2362 root 1.158 =item cf::map::unique_maps
2363    
2364 root 1.166 Returns an arrayref of paths of all shared maps that have
2365 root 1.158 instantiated unique items. May block.
2366    
2367     =cut
2368    
2369     sub unique_maps() {
2370 root 1.276 [
2371     map {
2372     utf8::decode $_;
2373 root 1.419 s/\.map$//; # TODO future compatibility hack
2374     /\.pst$/ || !/^$PATH_SEP/o # TODO unique maps apparebntly lack the .map suffix :/
2375     ? ()
2376     : normalise $_
2377 root 1.276 } @{ aio_readdir $UNIQUEDIR or [] }
2378     ]
2379 root 1.158 }
2380    
2381 root 1.489 =item cf::map::static_maps
2382    
2383     Returns an arrayref if paths of all static maps (all preinstalled F<.map>
2384 root 1.491 file in the shared directory excluding F</styles> and F</editor>). May
2385     block.
2386 root 1.489
2387     =cut
2388    
2389     sub static_maps() {
2390     my @dirs = "";
2391     my @maps;
2392    
2393     while (@dirs) {
2394     my $dir = shift @dirs;
2395    
2396 root 1.491 next if $dir eq "/styles" || $dir eq "/editor";
2397 root 1.490
2398 root 1.489 my ($dirs, $files) = Coro::AIO::aio_scandir "$MAPDIR$dir", 2
2399     or return;
2400    
2401     for (@$files) {
2402     s/\.map$// or next;
2403     utf8::decode $_;
2404     push @maps, "$dir/$_";
2405     }
2406    
2407     push @dirs, map "$dir/$_", @$dirs;
2408     }
2409    
2410     \@maps
2411     }
2412    
2413 root 1.155 =back
2414    
2415     =head3 cf::object
2416    
2417     =cut
2418    
2419     package cf::object;
2420    
2421     =over 4
2422    
2423     =item $ob->inv_recursive
2424 root 1.110
2425 root 1.419 Returns the inventory of the object I<and> their inventories, recursively,
2426     but I<not> the object itself.
2427 root 1.110
2428 root 1.155 =cut
2429 root 1.144
2430 root 1.155 sub inv_recursive_;
2431     sub inv_recursive_ {
2432     map { $_, inv_recursive_ $_->inv } @_
2433     }
2434 root 1.110
2435 root 1.155 sub inv_recursive {
2436     inv_recursive_ inv $_[0]
2437 root 1.110 }
2438    
2439 root 1.356 =item $ref = $ob->ref
2440    
2441 root 1.419 Creates and returns a persistent reference to an object that can be stored as a string.
2442 root 1.356
2443     =item $ob = cf::object::deref ($refstring)
2444    
2445     returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2446     even if the object actually exists. May block.
2447    
2448     =cut
2449    
2450     sub deref {
2451     my ($ref) = @_;
2452    
2453 root 1.377 if ($ref =~ m{^player\/(<1\.[0-9a-f]+>)/(.*)$}) {
2454 root 1.356 my ($uuid, $name) = ($1, $2);
2455     my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2456     or return;
2457     $pl->ob->uuid eq $uuid
2458     or return;
2459    
2460     $pl->ob
2461     } else {
2462     warn "$ref: cannot resolve object reference\n";
2463     undef
2464     }
2465     }
2466    
2467 root 1.110 package cf;
2468    
2469     =back
2470    
2471 root 1.95 =head3 cf::object::player
2472    
2473     =over 4
2474    
2475 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2476 root 1.28
2477     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2478     can be C<undef>. Does the right thing when the player is currently in a
2479     dialogue with the given NPC character.
2480    
2481     =cut
2482    
2483 root 1.428 our $SAY_CHANNEL = {
2484     id => "say",
2485     title => "Map",
2486     reply => "say ",
2487 root 1.468 tooltip => "Things said to and replied from NPCs near you and other players on the same map only.",
2488 root 1.428 };
2489    
2490     our $CHAT_CHANNEL = {
2491     id => "chat",
2492     title => "Chat",
2493     reply => "chat ",
2494     tooltip => "Player chat and shouts, global to the server.",
2495     };
2496    
2497 root 1.22 # rough implementation of a future "reply" method that works
2498     # with dialog boxes.
2499 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2500 root 1.23 sub cf::object::player::reply($$$;$) {
2501     my ($self, $npc, $msg, $flags) = @_;
2502    
2503     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2504 root 1.22
2505 root 1.24 if ($self->{record_replies}) {
2506     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2507 elmex 1.282
2508 root 1.24 } else {
2509 elmex 1.282 my $pl = $self->contr;
2510    
2511     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2512 root 1.316 my $dialog = $pl->{npc_dialog};
2513     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2514 elmex 1.282
2515     } else {
2516     $msg = $npc->name . " says: $msg" if $npc;
2517 root 1.428 $self->send_msg ($SAY_CHANNEL => $msg, $flags);
2518 elmex 1.282 }
2519 root 1.24 }
2520 root 1.22 }
2521    
2522 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2523    
2524     =cut
2525    
2526     sub cf::object::send_msg {
2527     my $pl = shift->contr
2528     or return;
2529     $pl->send_msg (@_);
2530     }
2531    
2532 root 1.79 =item $player_object->may ("access")
2533    
2534     Returns wether the given player is authorized to access resource "access"
2535     (e.g. "command_wizcast").
2536    
2537     =cut
2538    
2539     sub cf::object::player::may {
2540     my ($self, $access) = @_;
2541    
2542     $self->flag (cf::FLAG_WIZ) ||
2543     (ref $cf::CFG{"may_$access"}
2544     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2545     : $cf::CFG{"may_$access"})
2546     }
2547 root 1.70
2548 root 1.115 =item $player_object->enter_link
2549    
2550     Freezes the player and moves him/her to a special map (C<{link}>).
2551    
2552 root 1.446 The player should be reasonably safe there for short amounts of time (e.g.
2553     for loading a map). You I<MUST> call C<leave_link> as soon as possible,
2554     though, as the palyer cannot control the character while it is on the link
2555     map.
2556 root 1.115
2557 root 1.166 Will never block.
2558    
2559 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2560    
2561 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2562     map. If the map is not valid (or omitted), the player will be moved back
2563     to the location he/she was before the call to C<enter_link>, or, if that
2564     fails, to the emergency map position.
2565 root 1.115
2566     Might block.
2567    
2568     =cut
2569    
2570 root 1.166 sub link_map {
2571     unless ($LINK_MAP) {
2572     $LINK_MAP = cf::map::find "{link}"
2573 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2574 root 1.166 $LINK_MAP->load;
2575     }
2576    
2577     $LINK_MAP
2578     }
2579    
2580 root 1.110 sub cf::object::player::enter_link {
2581     my ($self) = @_;
2582    
2583 root 1.259 $self->deactivate_recursive;
2584 root 1.258
2585 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2586 root 1.110
2587 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2588 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2589 root 1.110
2590 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
2591 root 1.110 }
2592    
2593     sub cf::object::player::leave_link {
2594     my ($self, $map, $x, $y) = @_;
2595    
2596 root 1.270 return unless $self->contr->active;
2597    
2598 root 1.110 my $link_pos = delete $self->{_link_pos};
2599    
2600     unless ($map) {
2601     # restore original map position
2602     ($map, $x, $y) = @{ $link_pos || [] };
2603 root 1.133 $map = cf::map::find $map;
2604 root 1.110
2605     unless ($map) {
2606     ($map, $x, $y) = @$EMERGENCY_POSITION;
2607 root 1.133 $map = cf::map::find $map
2608 root 1.110 or die "FATAL: cannot load emergency map\n";
2609     }
2610     }
2611    
2612     ($x, $y) = (-1, -1)
2613     unless (defined $x) && (defined $y);
2614    
2615     # use -1 or undef as default coordinates, not 0, 0
2616     ($x, $y) = ($map->enter_x, $map->enter_y)
2617 root 1.492 if $x <= 0 && $y <= 0;
2618 root 1.110
2619     $map->load;
2620 root 1.333 $map->load_neighbours;
2621 root 1.110
2622 root 1.143 return unless $self->contr->active;
2623 root 1.215
2624     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2625 root 1.110 $self->enter_map ($map, $x, $y);
2626 root 1.476
2627     # only activate afterwards, to support waiting in hooks
2628     $self->activate_recursive;
2629 root 1.110 }
2630    
2631 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2632 root 1.268
2633     Moves the player to the given map-path and coordinates by first freezing
2634     her, loading and preparing them map, calling the provided $check callback
2635     that has to return the map if sucecssful, and then unfreezes the player on
2636 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2637     be called at the end of this process.
2638 root 1.110
2639 root 1.436 Note that $check will be called with a potentially non-loaded map, so if
2640     it needs a loaded map it has to call C<< ->load >>.
2641    
2642 root 1.110 =cut
2643    
2644 root 1.270 our $GOTOGEN;
2645    
2646 root 1.136 sub cf::object::player::goto {
2647 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2648 root 1.268
2649 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2650     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2651    
2652 root 1.110 $self->enter_link;
2653    
2654 root 1.140 (async {
2655 root 1.374 $Coro::current->{desc} = "player::goto $path $x $y";
2656    
2657 root 1.365 # *tag paths override both path and x|y
2658     if ($path =~ /^\*(.*)$/) {
2659     if (my @obs = grep $_->map, ext::map_tags::find $1) {
2660     my $ob = $obs[rand @obs];
2661 root 1.366
2662 root 1.367 # see if we actually can go there
2663 root 1.368 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2664     $ob = $obs[rand @obs];
2665 root 1.369 } else {
2666     $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2667 root 1.368 }
2668 root 1.369 # else put us there anyways for now #d#
2669 root 1.366
2670 root 1.365 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2671 root 1.369 } else {
2672     ($path, $x, $y) = (undef, undef, undef);
2673 root 1.365 }
2674     }
2675    
2676 root 1.197 my $map = eval {
2677 root 1.369 my $map = defined $path ? cf::map::find $path : undef;
2678 root 1.268
2679     if ($map) {
2680     $map = $map->customise_for ($self);
2681     $map = $check->($map) if $check && $map;
2682     } else {
2683 root 1.369 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2684 root 1.268 }
2685    
2686 root 1.197 $map
2687 root 1.268 };
2688    
2689     if ($@) {
2690     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2691     LOG llevError | logBacktrace, Carp::longmess $@;
2692     }
2693 root 1.115
2694 root 1.270 if ($gen == $self->{_goto_generation}) {
2695     delete $self->{_goto_generation};
2696     $self->leave_link ($map, $x, $y);
2697     }
2698 root 1.306
2699     $done->() if $done;
2700 root 1.110 })->prio (1);
2701     }
2702    
2703     =item $player_object->enter_exit ($exit_object)
2704    
2705     =cut
2706    
2707     sub parse_random_map_params {
2708     my ($spec) = @_;
2709    
2710     my $rmp = { # defaults
2711 root 1.181 xsize => (cf::rndm 15, 40),
2712     ysize => (cf::rndm 15, 40),
2713     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2714 root 1.182 #layout => string,
2715 root 1.110 };
2716    
2717     for (split /\n/, $spec) {
2718     my ($k, $v) = split /\s+/, $_, 2;
2719    
2720     $rmp->{lc $k} = $v if (length $k) && (length $v);
2721     }
2722    
2723     $rmp
2724     }
2725    
2726     sub prepare_random_map {
2727     my ($exit) = @_;
2728    
2729     # all this does is basically replace the /! path by
2730     # a new random map path (?random/...) with a seed
2731     # that depends on the exit object
2732    
2733     my $rmp = parse_random_map_params $exit->msg;
2734    
2735     if ($exit->map) {
2736 root 1.198 $rmp->{region} = $exit->region->name;
2737 root 1.110 $rmp->{origin_map} = $exit->map->path;
2738     $rmp->{origin_x} = $exit->x;
2739     $rmp->{origin_y} = $exit->y;
2740 root 1.430
2741     $exit->map->touch;
2742 root 1.110 }
2743    
2744     $rmp->{random_seed} ||= $exit->random_seed;
2745    
2746 root 1.422 my $data = JSON::XS->new->utf8->pretty->canonical->encode ($rmp);
2747 root 1.110 my $md5 = Digest::MD5::md5_hex $data;
2748 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2749 root 1.110
2750 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2751 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2752 root 1.177 undef $fh;
2753     aio_rename "$meta~", $meta;
2754 root 1.110
2755 root 1.430 my $slaying = "?random/$md5";
2756    
2757     if ($exit->valid) {
2758     $exit->slaying ("?random/$md5");
2759     $exit->msg (undef);
2760     }
2761 root 1.110 }
2762     }
2763    
2764     sub cf::object::player::enter_exit {
2765     my ($self, $exit) = @_;
2766    
2767     return unless $self->type == cf::PLAYER;
2768    
2769 root 1.430 $self->enter_link;
2770    
2771     (async {
2772     $Coro::current->{desc} = "enter_exit";
2773    
2774     unless (eval {
2775     $self->deactivate_recursive; # just to be sure
2776 root 1.195
2777 root 1.430 # random map handling
2778     {
2779     my $guard = cf::lock_acquire "exit_prepare:$exit";
2780 root 1.195
2781 root 1.430 prepare_random_map $exit
2782     if $exit->slaying eq "/!";
2783     }
2784 root 1.110
2785 root 1.430 my $map = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2786     my $x = $exit->stats->hp;
2787     my $y = $exit->stats->sp;
2788 root 1.296
2789 root 1.430 $self->goto ($map, $x, $y);
2790 root 1.374
2791 root 1.430 # if exit is damned, update players death & WoR home-position
2792     $self->contr->savebed ($map, $x, $y)
2793     if $exit->flag (cf::FLAG_DAMNED);
2794 root 1.110
2795 root 1.430 1
2796 root 1.110 }) {
2797 root 1.447 $self->message ("Something went wrong deep within the deliantra server. "
2798 root 1.233 . "I'll try to bring you back to the map you were before. "
2799     . "Please report this to the dungeon master!",
2800     cf::NDI_UNIQUE | cf::NDI_RED);
2801 root 1.110
2802     warn "ERROR in enter_exit: $@";
2803     $self->leave_link;
2804     }
2805     })->prio (1);
2806     }
2807    
2808 root 1.95 =head3 cf::client
2809    
2810     =over 4
2811    
2812     =item $client->send_drawinfo ($text, $flags)
2813    
2814     Sends a drawinfo packet to the client. Circumvents output buffering so
2815     should not be used under normal circumstances.
2816    
2817 root 1.70 =cut
2818    
2819 root 1.95 sub cf::client::send_drawinfo {
2820     my ($self, $text, $flags) = @_;
2821    
2822     utf8::encode $text;
2823 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2824 root 1.95 }
2825    
2826 root 1.494 =item $client->send_big_packet ($pkt)
2827    
2828     Like C<send_packet>, but tries to compress large packets, and fragments
2829     them as required.
2830    
2831     =cut
2832    
2833     our $MAXFRAGSIZE = cf::MAXSOCKBUF - 64;
2834    
2835     sub cf::client::send_big_packet {
2836     my ($self, $pkt) = @_;
2837    
2838     # try lzf for large packets
2839     $pkt = "lzf " . Compress::LZF::compress $pkt
2840     if 1024 <= length $pkt and $self->{can_lzf};
2841    
2842     # split very large packets
2843     if ($MAXFRAGSIZE < length $pkt and $self->{can_lzf}) {
2844     $self->send_packet ("frag $_") for unpack "(a$MAXFRAGSIZE)*", $pkt;
2845     $pkt = "frag";
2846     }
2847    
2848     $self->send_packet ($pkt);
2849     }
2850    
2851 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2852 root 1.283
2853     Send a drawinfo or msg packet to the client, formatting the msg for the
2854     client if neccessary. C<$type> should be a string identifying the type of
2855     the message, with C<log> being the default. If C<$color> is negative, suppress
2856     the message unless the client supports the msg packet.
2857    
2858     =cut
2859    
2860 root 1.391 # non-persistent channels (usually the info channel)
2861 root 1.350 our %CHANNEL = (
2862 root 1.486 "c/motd" => {
2863     id => "infobox",
2864     title => "MOTD",
2865     reply => undef,
2866     tooltip => "The message of the day",
2867     },
2868 root 1.350 "c/identify" => {
2869 root 1.375 id => "infobox",
2870 root 1.350 title => "Identify",
2871     reply => undef,
2872     tooltip => "Items recently identified",
2873     },
2874 root 1.352 "c/examine" => {
2875 root 1.375 id => "infobox",
2876 root 1.352 title => "Examine",
2877     reply => undef,
2878     tooltip => "Signs and other items you examined",
2879     },
2880 root 1.487 "c/shopinfo" => {
2881     id => "infobox",
2882     title => "Shop Info",
2883     reply => undef,
2884     tooltip => "What your bargaining skill tells you about the shop",
2885     },
2886 root 1.389 "c/book" => {
2887     id => "infobox",
2888     title => "Book",
2889     reply => undef,
2890     tooltip => "The contents of a note or book",
2891     },
2892 root 1.375 "c/lookat" => {
2893     id => "infobox",
2894     title => "Look",
2895     reply => undef,
2896     tooltip => "What you saw there",
2897     },
2898 root 1.390 "c/who" => {
2899     id => "infobox",
2900     title => "Players",
2901     reply => undef,
2902     tooltip => "Shows players who are currently online",
2903     },
2904     "c/body" => {
2905     id => "infobox",
2906     title => "Body Parts",
2907     reply => undef,
2908     tooltip => "Shows which body parts you posess and are available",
2909     },
2910 root 1.465 "c/statistics" => {
2911     id => "infobox",
2912     title => "Statistics",
2913     reply => undef,
2914     tooltip => "Shows your primary statistics",
2915     },
2916 root 1.450 "c/skills" => {
2917     id => "infobox",
2918     title => "Skills",
2919     reply => undef,
2920     tooltip => "Shows your experience per skill and item power",
2921     },
2922 root 1.470 "c/shopitems" => {
2923     id => "infobox",
2924     title => "Shop Items",
2925     reply => undef,
2926     tooltip => "Shows the items currently for sale in this shop",
2927     },
2928 root 1.465 "c/resistances" => {
2929     id => "infobox",
2930     title => "Resistances",
2931     reply => undef,
2932     tooltip => "Shows your resistances",
2933     },
2934     "c/pets" => {
2935     id => "infobox",
2936     title => "Pets",
2937     reply => undef,
2938     tooltip => "Shows information abotu your pets/a specific pet",
2939     },
2940 root 1.471 "c/perceiveself" => {
2941     id => "infobox",
2942     title => "Perceive Self",
2943     reply => undef,
2944     tooltip => "You gained detailed knowledge about yourself",
2945     },
2946 root 1.390 "c/uptime" => {
2947     id => "infobox",
2948     title => "Uptime",
2949     reply => undef,
2950 root 1.391 tooltip => "How long the server has been running since last restart",
2951 root 1.390 },
2952     "c/mapinfo" => {
2953     id => "infobox",
2954     title => "Map Info",
2955     reply => undef,
2956     tooltip => "Information related to the maps",
2957     },
2958 root 1.426 "c/party" => {
2959     id => "party",
2960     title => "Party",
2961     reply => "gsay ",
2962     tooltip => "Messages and chat related to your party",
2963     },
2964 root 1.464 "c/death" => {
2965     id => "death",
2966     title => "Death",
2967     reply => undef,
2968     tooltip => "Reason for and more info about your most recent death",
2969     },
2970 root 1.462 "c/say" => $SAY_CHANNEL,
2971     "c/chat" => $CHAT_CHANNEL,
2972 root 1.350 );
2973    
2974 root 1.283 sub cf::client::send_msg {
2975 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
2976 root 1.283
2977 root 1.447 $msg = $self->pl->expand_cfpod ($msg)
2978     unless $color & cf::NDI_VERBATIM;
2979 root 1.283
2980 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
2981 root 1.311
2982 root 1.350 # check predefined channels, for the benefit of C
2983 root 1.375 if ($CHANNEL{$channel}) {
2984     $channel = $CHANNEL{$channel};
2985    
2986 root 1.463 $self->ext_msg (channel_info => $channel);
2987 root 1.375 $channel = $channel->{id};
2988 root 1.350
2989 root 1.375 } elsif (ref $channel) {
2990 root 1.311 # send meta info to client, if not yet sent
2991     unless (exists $self->{channel}{$channel->{id}}) {
2992     $self->{channel}{$channel->{id}} = $channel;
2993 root 1.463 $self->ext_msg (channel_info => $channel);
2994 root 1.311 }
2995    
2996     $channel = $channel->{id};
2997     }
2998    
2999 root 1.313 return unless @extra || length $msg;
3000    
3001 root 1.463 # default colour, mask it out
3002     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
3003     if $color & cf::NDI_DEF;
3004    
3005     my $pkt = "msg "
3006     . $self->{json_coder}->encode (
3007     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]
3008     );
3009    
3010 root 1.494 $self->send_big_packet ($pkt);
3011 root 1.283 }
3012    
3013 root 1.316 =item $client->ext_msg ($type, @msg)
3014 root 1.232
3015 root 1.287 Sends an ext event to the client.
3016 root 1.232
3017     =cut
3018    
3019 root 1.316 sub cf::client::ext_msg($$@) {
3020     my ($self, $type, @msg) = @_;
3021 root 1.232
3022 root 1.343 if ($self->extcmd == 2) {
3023 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
3024 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
3025 root 1.316 push @msg, msgtype => "event_$type";
3026 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3027 root 1.316 }
3028 root 1.232 }
3029 root 1.95
3030 root 1.336 =item $client->ext_reply ($msgid, @msg)
3031    
3032     Sends an ext reply to the client.
3033    
3034     =cut
3035    
3036     sub cf::client::ext_reply($$@) {
3037     my ($self, $id, @msg) = @_;
3038    
3039     if ($self->extcmd == 2) {
3040 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
3041 root 1.343 } elsif ($self->extcmd == 1) {
3042 root 1.336 #TODO: version 1, remove
3043     unshift @msg, msgtype => "reply", msgid => $id;
3044 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3045 root 1.336 }
3046     }
3047    
3048 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
3049    
3050     Queues a query to the client, calling the given callback with
3051     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
3052     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
3053    
3054 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
3055     become reliable at some point in the future.
3056 root 1.95
3057     =cut
3058    
3059     sub cf::client::query {
3060     my ($self, $flags, $text, $cb) = @_;
3061    
3062     return unless $self->state == ST_PLAYING
3063     || $self->state == ST_SETUP
3064     || $self->state == ST_CUSTOM;
3065    
3066     $self->state (ST_CUSTOM);
3067    
3068     utf8::encode $text;
3069     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
3070    
3071     $self->send_packet ($self->{query_queue}[0][0])
3072     if @{ $self->{query_queue} } == 1;
3073 root 1.287
3074     1
3075 root 1.95 }
3076    
3077     cf::client->attach (
3078 root 1.290 on_connect => sub {
3079     my ($ns) = @_;
3080    
3081     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
3082     },
3083 root 1.95 on_reply => sub {
3084     my ($ns, $msg) = @_;
3085    
3086     # this weird shuffling is so that direct followup queries
3087     # get handled first
3088 root 1.128 my $queue = delete $ns->{query_queue}
3089 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
3090 root 1.95
3091     (shift @$queue)->[1]->($msg);
3092 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
3093 root 1.95
3094     push @{ $ns->{query_queue} }, @$queue;
3095    
3096     if (@{ $ns->{query_queue} } == @$queue) {
3097     if (@$queue) {
3098     $ns->send_packet ($ns->{query_queue}[0][0]);
3099     } else {
3100 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
3101 root 1.95 }
3102     }
3103     },
3104 root 1.287 on_exticmd => sub {
3105     my ($ns, $buf) = @_;
3106    
3107 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
3108 root 1.287
3109     if (ref $msg) {
3110 root 1.316 my ($type, $reply, @payload) =
3111     "ARRAY" eq ref $msg
3112     ? @$msg
3113     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
3114    
3115 root 1.338 my @reply;
3116    
3117 root 1.316 if (my $cb = $EXTICMD{$type}) {
3118 root 1.338 @reply = $cb->($ns, @payload);
3119     }
3120    
3121     $ns->ext_reply ($reply, @reply)
3122     if $reply;
3123 root 1.316
3124 root 1.287 } else {
3125     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
3126     }
3127    
3128     cf::override;
3129     },
3130 root 1.95 );
3131    
3132 root 1.140 =item $client->async (\&cb)
3133 root 1.96
3134     Create a new coroutine, running the specified callback. The coroutine will
3135     be automatically cancelled when the client gets destroyed (e.g. on logout,
3136     or loss of connection).
3137    
3138     =cut
3139    
3140 root 1.140 sub cf::client::async {
3141 root 1.96 my ($self, $cb) = @_;
3142    
3143 root 1.140 my $coro = &Coro::async ($cb);
3144 root 1.103
3145     $coro->on_destroy (sub {
3146 root 1.96 delete $self->{_coro}{$coro+0};
3147 root 1.103 });
3148 root 1.96
3149     $self->{_coro}{$coro+0} = $coro;
3150 root 1.103
3151     $coro
3152 root 1.96 }
3153    
3154     cf::client->attach (
3155 root 1.509 on_client_destroy => sub {
3156 root 1.96 my ($ns) = @_;
3157    
3158 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
3159 root 1.96 },
3160     );
3161    
3162 root 1.95 =back
3163    
3164 root 1.70
3165     =head2 SAFE SCRIPTING
3166    
3167     Functions that provide a safe environment to compile and execute
3168     snippets of perl code without them endangering the safety of the server
3169     itself. Looping constructs, I/O operators and other built-in functionality
3170     is not available in the safe scripting environment, and the number of
3171 root 1.79 functions and methods that can be called is greatly reduced.
3172 root 1.70
3173     =cut
3174 root 1.23
3175 root 1.42 our $safe = new Safe "safe";
3176 root 1.23 our $safe_hole = new Safe::Hole;
3177    
3178     $SIG{FPE} = 'IGNORE';
3179    
3180 root 1.328 $safe->permit_only (Opcode::opset qw(
3181 elmex 1.498 :base_core :base_mem :base_orig :base_math :base_loop
3182 root 1.328 grepstart grepwhile mapstart mapwhile
3183     sort time
3184     ));
3185 root 1.23
3186 root 1.25 # here we export the classes and methods available to script code
3187    
3188 root 1.70 =pod
3189    
3190 root 1.228 The following functions and methods are available within a safe environment:
3191 root 1.70
3192 root 1.297 cf::object
3193 root 1.383 contr pay_amount pay_player map x y force_find force_add destroy
3194 root 1.425 insert remove name archname title slaying race decrease split
3195 root 1.466 value
3196 root 1.297
3197     cf::object::player
3198     player
3199    
3200     cf::player
3201     peaceful
3202    
3203     cf::map
3204     trigger
3205 root 1.70
3206     =cut
3207    
3208 root 1.25 for (
3209 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
3210 elmex 1.431 insert remove inv nrof name archname title slaying race
3211 root 1.466 decrease split destroy change_exp value msg lore send_msg)],
3212 root 1.25 ["cf::object::player" => qw(player)],
3213 root 1.466 ["cf::player" => qw(peaceful send_msg)],
3214 elmex 1.91 ["cf::map" => qw(trigger)],
3215 root 1.25 ) {
3216     no strict 'refs';
3217     my ($pkg, @funs) = @$_;
3218 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3219 root 1.25 for @funs;
3220     }
3221 root 1.23
3222 root 1.70 =over 4
3223    
3224     =item @retval = safe_eval $code, [var => value, ...]
3225    
3226     Compiled and executes the given perl code snippet. additional var/value
3227     pairs result in temporary local (my) scalar variables of the given name
3228     that are available in the code snippet. Example:
3229    
3230     my $five = safe_eval '$first + $second', first => 1, second => 4;
3231    
3232     =cut
3233    
3234 root 1.23 sub safe_eval($;@) {
3235     my ($code, %vars) = @_;
3236    
3237     my $qcode = $code;
3238     $qcode =~ s/"/‟/g; # not allowed in #line filenames
3239     $qcode =~ s/\n/\\n/g;
3240    
3241 root 1.466 %vars = (_dummy => 0) unless %vars;
3242    
3243 root 1.499 my @res;
3244 root 1.23 local $_;
3245    
3246 root 1.42 my $eval =
3247 root 1.23 "do {\n"
3248     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3249     . "#line 0 \"{$qcode}\"\n"
3250     . $code
3251     . "\n}"
3252 root 1.25 ;
3253    
3254 root 1.499 if ($CFG{safe_eval}) {
3255     sub_generation_inc;
3256     local @safe::cf::_safe_eval_args = values %vars;
3257     @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3258     sub_generation_inc;
3259     } else {
3260     local @cf::_safe_eval_args = values %vars;
3261     @res = wantarray ? eval eval : scalar eval $eval;
3262     }
3263 root 1.25
3264 root 1.42 if ($@) {
3265     warn "$@";
3266     warn "while executing safe code '$code'\n";
3267     warn "with arguments " . (join " ", %vars) . "\n";
3268     }
3269    
3270 root 1.25 wantarray ? @res : $res[0]
3271 root 1.23 }
3272    
3273 root 1.69 =item cf::register_script_function $function => $cb
3274    
3275     Register a function that can be called from within map/npc scripts. The
3276     function should be reasonably secure and should be put into a package name
3277     like the extension.
3278    
3279     Example: register a function that gets called whenever a map script calls
3280     C<rent::overview>, as used by the C<rent> extension.
3281    
3282     cf::register_script_function "rent::overview" => sub {
3283     ...
3284     };
3285    
3286     =cut
3287    
3288 root 1.23 sub register_script_function {
3289     my ($fun, $cb) = @_;
3290    
3291 root 1.501 $fun = "safe::$fun" if $CFG{safe_eval};
3292     *$fun = $safe_hole->wrap ($cb);
3293 root 1.23 }
3294    
3295 root 1.70 =back
3296    
3297 root 1.71 =cut
3298    
3299 root 1.23 #############################################################################
3300 root 1.203 # the server's init and main functions
3301    
3302 root 1.246 sub load_facedata($) {
3303     my ($path) = @_;
3304 root 1.223
3305 root 1.348 # HACK to clear player env face cache, we need some signal framework
3306     # for this (global event?)
3307     %ext::player_env::MUSIC_FACE_CACHE = ();
3308    
3309 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3310 root 1.334
3311 root 1.229 warn "loading facedata from $path\n";
3312 root 1.223
3313 root 1.236 my $facedata;
3314     0 < aio_load $path, $facedata
3315 root 1.223 or die "$path: $!";
3316    
3317 root 1.237 $facedata = Coro::Storable::thaw $facedata;
3318 root 1.223
3319 root 1.236 $facedata->{version} == 2
3320 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3321    
3322 root 1.334 # patch in the exptable
3323 root 1.500 my $exp_table = $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]);
3324 root 1.334 $facedata->{resource}{"res/exp_table"} = {
3325     type => FT_RSRC,
3326 root 1.500 data => $exp_table,
3327     hash => (Digest::MD5::md5 $exp_table),
3328 root 1.334 };
3329     cf::cede_to_tick;
3330    
3331 root 1.236 {
3332     my $faces = $facedata->{faceinfo};
3333    
3334     while (my ($face, $info) = each %$faces) {
3335     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3336 root 1.405
3337 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3338     cf::face::set_magicmap $idx, $info->{magicmap};
3339 root 1.496 cf::face::set_data $idx, 0, $info->{data32}, $info->{hash32};
3340     cf::face::set_data $idx, 1, $info->{data64}, $info->{hash64};
3341 root 1.302
3342     cf::cede_to_tick;
3343 root 1.236 }
3344    
3345     while (my ($face, $info) = each %$faces) {
3346     next unless $info->{smooth};
3347 root 1.405
3348 root 1.236 my $idx = cf::face::find $face
3349     or next;
3350 root 1.405
3351 root 1.236 if (my $smooth = cf::face::find $info->{smooth}) {
3352 root 1.302 cf::face::set_smooth $idx, $smooth;
3353     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3354 root 1.236 } else {
3355     warn "smooth face '$info->{smooth}' not found for face '$face'";
3356     }
3357 root 1.302
3358     cf::cede_to_tick;
3359 root 1.236 }
3360 root 1.223 }
3361    
3362 root 1.236 {
3363     my $anims = $facedata->{animinfo};
3364    
3365     while (my ($anim, $info) = each %$anims) {
3366     cf::anim::set $anim, $info->{frames}, $info->{facings};
3367 root 1.302 cf::cede_to_tick;
3368 root 1.225 }
3369 root 1.236
3370     cf::anim::invalidate_all; # d'oh
3371 root 1.225 }
3372    
3373 root 1.302 {
3374     my $res = $facedata->{resource};
3375    
3376     while (my ($name, $info) = each %$res) {
3377 root 1.405 if (defined $info->{type}) {
3378     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3379    
3380 root 1.496 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3381 root 1.405 cf::face::set_type $idx, $info->{type};
3382 root 1.337 } else {
3383 root 1.405 $RESOURCE{$name} = $info;
3384 root 1.307 }
3385 root 1.302
3386     cf::cede_to_tick;
3387     }
3388 root 1.406 }
3389    
3390     cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3391 root 1.321
3392 root 1.406 1
3393     }
3394    
3395     cf::global->attach (on_resource_update => sub {
3396     if (my $soundconf = $RESOURCE{"res/sound.conf"}) {
3397     $soundconf = JSON::XS->new->utf8->relaxed->decode ($soundconf->{data});
3398    
3399     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3400     my $sound = $soundconf->{compat}[$_]
3401     or next;
3402 root 1.321
3403 root 1.406 my $face = cf::face::find "sound/$sound->[1]";
3404     cf::sound::set $sound->[0] => $face;
3405     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3406     }
3407 root 1.321
3408 root 1.406 while (my ($k, $v) = each %{$soundconf->{event}}) {
3409     my $face = cf::face::find "sound/$v";
3410     cf::sound::set $k => $face;
3411 root 1.321 }
3412 root 1.302 }
3413 root 1.406 });
3414 root 1.223
3415 root 1.318 register_exticmd fx_want => sub {
3416     my ($ns, $want) = @_;
3417    
3418     while (my ($k, $v) = each %$want) {
3419     $ns->fx_want ($k, $v);
3420     }
3421     };
3422    
3423 root 1.423 sub load_resource_file($) {
3424 root 1.424 my $guard = lock_acquire "load_resource_file";
3425    
3426 root 1.423 my $status = load_resource_file_ $_[0];
3427     get_slot 0.1, 100;
3428     cf::arch::commit_load;
3429 root 1.424
3430 root 1.423 $status
3431     }
3432    
3433 root 1.253 sub reload_regions {
3434 root 1.348 # HACK to clear player env face cache, we need some signal framework
3435     # for this (global event?)
3436     %ext::player_env::MUSIC_FACE_CACHE = ();
3437    
3438 root 1.253 load_resource_file "$MAPDIR/regions"
3439     or die "unable to load regions file\n";
3440 root 1.304
3441     for (cf::region::list) {
3442     $_->{match} = qr/$_->{match}/
3443     if exists $_->{match};
3444     }
3445 root 1.253 }
3446    
3447 root 1.246 sub reload_facedata {
3448 root 1.253 load_facedata "$DATADIR/facedata"
3449 root 1.246 or die "unable to load facedata\n";
3450     }
3451    
3452     sub reload_archetypes {
3453 root 1.253 load_resource_file "$DATADIR/archetypes"
3454 root 1.246 or die "unable to load archetypes\n";
3455 root 1.241 }
3456    
3457 root 1.246 sub reload_treasures {
3458 root 1.253 load_resource_file "$DATADIR/treasures"
3459 root 1.246 or die "unable to load treasurelists\n";
3460 root 1.241 }
3461    
3462 root 1.223 sub reload_resources {
3463 root 1.245 warn "reloading resource files...\n";
3464    
3465 root 1.246 reload_facedata;
3466     reload_archetypes;
3467 root 1.423 reload_regions;
3468 root 1.246 reload_treasures;
3469 root 1.245
3470     warn "finished reloading resource files\n";
3471 root 1.223 }
3472    
3473 root 1.345 sub reload_config {
3474 root 1.485 warn "reloading config file...\n";
3475    
3476 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3477 root 1.72 or return;
3478    
3479     local $/;
3480 root 1.485 *CFG = YAML::XS::Load scalar <$fh>;
3481 root 1.131
3482     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
3483    
3484 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3485     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3486    
3487 root 1.131 if (exists $CFG{mlockall}) {
3488     eval {
3489 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3490 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3491     };
3492     warn $@ if $@;
3493     }
3494 root 1.485
3495     warn "finished reloading resource files\n";
3496 root 1.72 }
3497    
3498 root 1.445 sub pidfile() {
3499     sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3500     or die "$PIDFILE: $!";
3501     flock $fh, &Fcntl::LOCK_EX
3502     or die "$PIDFILE: flock: $!";
3503     $fh
3504     }
3505    
3506     # make sure only one server instance is running at any one time
3507     sub atomic {
3508     my $fh = pidfile;
3509    
3510     my $pid = <$fh>;
3511     kill 9, $pid if $pid > 0;
3512    
3513     seek $fh, 0, 0;
3514     print $fh $$;
3515     }
3516    
3517 root 1.474 sub main_loop {
3518     warn "EV::loop starting\n";
3519     if (1) {
3520     EV::loop;
3521     }
3522     warn "EV::loop returned\n";
3523     goto &main_loop unless $REALLY_UNLOOP;
3524     }
3525    
3526 root 1.39 sub main {
3527 root 1.453 cf::init_globals; # initialise logging
3528    
3529     LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3530     LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3531     LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3532     LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen.";
3533    
3534     cf::init_experience;
3535     cf::init_anim;
3536     cf::init_attackmess;
3537     cf::init_dynamic;
3538    
3539     $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3540 root 1.445
3541 root 1.108 # we must not ever block the main coroutine
3542     local $Coro::idle = sub {
3543 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3544 root 1.175 (async {
3545 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3546 root 1.396 EV::loop EV::LOOP_ONESHOT;
3547 root 1.175 })->prio (Coro::PRIO_MAX);
3548 root 1.108 };
3549    
3550 root 1.453 evthread_start IO::AIO::poll_fileno;
3551    
3552     cf::sync_job {
3553 root 1.495 cf::load_settings;
3554     cf::load_materials;
3555    
3556 root 1.453 reload_resources;
3557 root 1.423 reload_config;
3558     db_init;
3559 root 1.453
3560     cf::init_uuid;
3561     cf::init_signals;
3562     cf::init_skills;
3563    
3564     cf::init_beforeplay;
3565    
3566     atomic;
3567    
3568 root 1.423 load_extensions;
3569    
3570 root 1.453 utime time, time, $RUNTIMEFILE;
3571 root 1.183
3572 root 1.453 # no (long-running) fork's whatsoever before this point(!)
3573 root 1.475 use POSIX ();
3574 root 1.453 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3575 root 1.445
3576 root 1.453 (pop @POST_INIT)->(0) while @POST_INIT;
3577     };
3578 root 1.445
3579 root 1.474 main_loop;
3580 root 1.34 }
3581    
3582     #############################################################################
3583 root 1.155 # initialisation and cleanup
3584    
3585     # install some emergency cleanup handlers
3586     BEGIN {
3587 root 1.396 our %SIGWATCHER = ();
3588 root 1.155 for my $signal (qw(INT HUP TERM)) {
3589 root 1.512 $SIGWATCHER{$signal} = AE::signal $signal, sub {
3590 root 1.396 cf::cleanup "SIG$signal";
3591     };
3592 root 1.155 }
3593     }
3594    
3595 root 1.417 sub write_runtime_sync {
3596 root 1.512 my $t0 = AE::time;
3597 root 1.506
3598 root 1.281 # first touch the runtime file to show we are still running:
3599     # the fsync below can take a very very long time.
3600    
3601 root 1.445 IO::AIO::aio_utime $RUNTIMEFILE, undef, undef;
3602 root 1.281
3603     my $guard = cf::lock_acquire "write_runtime";
3604    
3605 root 1.505 my $fh = aio_open "$RUNTIMEFILE~", O_WRONLY | O_CREAT | O_TRUNC, 0644
3606 root 1.281 or return;
3607    
3608     my $value = $cf::RUNTIME + 90 + 10;
3609     # 10 is the runtime save interval, for a monotonic clock
3610     # 60 allows for the watchdog to kill the server.
3611    
3612     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3613     and return;
3614    
3615     # always fsync - this file is important
3616     aio_fsync $fh
3617     and return;
3618    
3619     # touch it again to show we are up-to-date
3620     aio_utime $fh, undef, undef;
3621    
3622     close $fh
3623     or return;
3624    
3625 root 1.445 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3626 root 1.281 and return;
3627    
3628 root 1.512 warn sprintf "runtime file written (%gs).\n", AE::time - $t0;
3629 root 1.281
3630     1
3631     }
3632    
3633 root 1.416 our $uuid_lock;
3634     our $uuid_skip;
3635    
3636     sub write_uuid_sync($) {
3637     $uuid_skip ||= $_[0];
3638    
3639     return if $uuid_lock;
3640     local $uuid_lock = 1;
3641    
3642     my $uuid = "$LOCALDIR/uuid";
3643    
3644     my $fh = aio_open "$uuid~", O_WRONLY | O_CREAT, 0644
3645     or return;
3646    
3647 root 1.454 my $value = uuid_seq uuid_cur;
3648    
3649 root 1.455 unless ($value) {
3650     warn "cowardly refusing to write zero uuid value!\n";
3651 root 1.454 return;
3652     }
3653    
3654     my $value = uuid_str $value + $uuid_skip;
3655 root 1.416 $uuid_skip = 0;
3656    
3657     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3658     and return;
3659    
3660     # always fsync - this file is important
3661     aio_fsync $fh
3662     and return;
3663    
3664     close $fh
3665     or return;
3666    
3667     aio_rename "$uuid~", $uuid
3668     and return;
3669    
3670     warn "uuid file written ($value).\n";
3671    
3672     1
3673    
3674     }
3675    
3676     sub write_uuid($$) {
3677     my ($skip, $sync) = @_;
3678    
3679     $sync ? write_uuid_sync $skip
3680     : async { write_uuid_sync $skip };
3681     }
3682    
3683 root 1.156 sub emergency_save() {
3684 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3685    
3686 root 1.457 warn "emergency_perl_save: enter\n";
3687 root 1.155
3688     cf::sync_job {
3689 root 1.457 # this is a trade-off: we want to be very quick here, so
3690     # save all maps without fsync, and later call a global sync
3691     # (which in turn might be very very slow)
3692     local $USE_FSYNC = 0;
3693    
3694 root 1.155 # use a peculiar iteration method to avoid tripping on perl
3695     # refcount bugs in for. also avoids problems with players
3696 root 1.167 # and maps saved/destroyed asynchronously.
3697 root 1.457 warn "emergency_perl_save: begin player save\n";
3698 root 1.155 for my $login (keys %cf::PLAYER) {
3699     my $pl = $cf::PLAYER{$login} or next;
3700     $pl->valid or next;
3701 root 1.382 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3702 root 1.155 $pl->save;
3703     }
3704 root 1.457 warn "emergency_perl_save: end player save\n";
3705 root 1.155
3706 root 1.457 warn "emergency_perl_save: begin map save\n";
3707 root 1.155 for my $path (keys %cf::MAP) {
3708     my $map = $cf::MAP{$path} or next;
3709     $map->valid or next;
3710     $map->save;
3711     }
3712 root 1.457 warn "emergency_perl_save: end map save\n";
3713 root 1.208
3714 root 1.457 warn "emergency_perl_save: begin database checkpoint\n";
3715 root 1.208 BDB::db_env_txn_checkpoint $DB_ENV;
3716 root 1.457 warn "emergency_perl_save: end database checkpoint\n";
3717 root 1.416
3718 root 1.457 warn "emergency_perl_save: begin write uuid\n";
3719 root 1.416 write_uuid_sync 1;
3720 root 1.457 warn "emergency_perl_save: end write uuid\n";
3721 root 1.155 };
3722    
3723 root 1.457 warn "emergency_perl_save: starting sync()\n";
3724     IO::AIO::aio_sync sub {
3725     warn "emergency_perl_save: finished sync()\n";
3726     };
3727    
3728     warn "emergency_perl_save: leave\n";
3729 root 1.155 }
3730 root 1.22
3731 root 1.211 sub post_cleanup {
3732     my ($make_core) = @_;
3733    
3734     warn Carp::longmess "post_cleanup backtrace"
3735     if $make_core;
3736 root 1.445
3737     my $fh = pidfile;
3738     unlink $PIDFILE if <$fh> == $$;
3739 root 1.211 }
3740    
3741 root 1.441 # a safer delete_package, copied from Symbol
3742     sub clear_package($) {
3743     my $pkg = shift;
3744    
3745     # expand to full symbol table name if needed
3746     unless ($pkg =~ /^main::.*::$/) {
3747     $pkg = "main$pkg" if $pkg =~ /^::/;
3748     $pkg = "main::$pkg" unless $pkg =~ /^main::/;
3749     $pkg .= '::' unless $pkg =~ /::$/;
3750     }
3751    
3752     my($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
3753     my $stem_symtab = *{$stem}{HASH};
3754    
3755     defined $stem_symtab and exists $stem_symtab->{$leaf}
3756     or return;
3757    
3758     # clear all symbols
3759     my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3760     for my $name (keys %$leaf_symtab) {
3761     _gv_clear *{"$pkg$name"};
3762     # use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3763     }
3764 root 1.451 warn "cleared package $pkg\n";#d#
3765 root 1.441 }
3766    
3767 root 1.246 sub do_reload_perl() {
3768 root 1.106 # can/must only be called in main
3769     if ($Coro::current != $Coro::main) {
3770 root 1.183 warn "can only reload from main coroutine";
3771 root 1.106 return;
3772     }
3773    
3774 root 1.441 return if $RELOAD++;
3775    
3776 root 1.512 my $t1 = AE::time;
3777 root 1.457
3778 root 1.441 while ($RELOAD) {
3779     warn "reloading...";
3780 root 1.103
3781 root 1.441 warn "entering sync_job";
3782 root 1.212
3783 root 1.441 cf::sync_job {
3784     cf::write_runtime_sync; # external watchdog should not bark
3785     cf::emergency_save;
3786     cf::write_runtime_sync; # external watchdog should not bark
3787 root 1.183
3788 root 1.441 warn "syncing database to disk";
3789     BDB::db_env_txn_checkpoint $DB_ENV;
3790 root 1.106
3791 root 1.441 # if anything goes wrong in here, we should simply crash as we already saved
3792 root 1.65
3793 root 1.441 warn "flushing outstanding aio requests";
3794     while (IO::AIO::nreqs || BDB::nreqs) {
3795     Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3796     }
3797 root 1.183
3798 root 1.441 warn "cancelling all extension coros";
3799     $_->cancel for values %EXT_CORO;
3800     %EXT_CORO = ();
3801 root 1.223
3802 root 1.441 warn "removing commands";
3803     %COMMAND = ();
3804 root 1.103
3805 root 1.441 warn "removing ext/exti commands";
3806     %EXTCMD = ();
3807     %EXTICMD = ();
3808 root 1.159
3809 root 1.441 warn "unloading/nuking all extensions";
3810     for my $pkg (@EXTS) {
3811     warn "... unloading $pkg";
3812 root 1.159
3813 root 1.441 if (my $cb = $pkg->can ("unload")) {
3814     eval {
3815     $cb->($pkg);
3816     1
3817     } or warn "$pkg unloaded, but with errors: $@";
3818     }
3819 root 1.159
3820 root 1.441 warn "... clearing $pkg";
3821     clear_package $pkg;
3822 root 1.159 }
3823    
3824 root 1.441 warn "unloading all perl modules loaded from $LIBDIR";
3825     while (my ($k, $v) = each %INC) {
3826     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3827 root 1.65
3828 root 1.441 warn "... unloading $k";
3829     delete $INC{$k};
3830 root 1.65
3831 root 1.441 $k =~ s/\.pm$//;
3832     $k =~ s/\//::/g;
3833 root 1.65
3834 root 1.441 if (my $cb = $k->can ("unload_module")) {
3835     $cb->();
3836     }
3837 root 1.65
3838 root 1.441 clear_package $k;
3839 root 1.65 }
3840    
3841 root 1.441 warn "getting rid of safe::, as good as possible";
3842     clear_package "safe::$_"
3843     for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3844 root 1.65
3845 root 1.441 warn "unloading cf.pm \"a bit\"";
3846     delete $INC{"cf.pm"};
3847 root 1.466 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES;
3848 root 1.65
3849 root 1.441 # don't, removes xs symbols, too,
3850     # and global variables created in xs
3851     #clear_package __PACKAGE__;
3852 root 1.65
3853 root 1.441 warn "unload completed, starting to reload now";
3854 root 1.65
3855 root 1.441 warn "reloading cf.pm";
3856     require cf;
3857 root 1.483 cf::_connect_to_perl_1;
3858 root 1.183
3859 root 1.441 warn "loading config and database again";
3860     cf::reload_config;
3861 root 1.100
3862 root 1.441 warn "loading extensions";
3863     cf::load_extensions;
3864 root 1.65
3865 root 1.457 if ($REATTACH_ON_RELOAD) {
3866     warn "reattaching attachments to objects/players";
3867     _global_reattach; # objects, sockets
3868     warn "reattaching attachments to maps";
3869     reattach $_ for values %MAP;
3870     warn "reattaching attachments to players";
3871     reattach $_ for values %PLAYER;
3872     }
3873 root 1.65
3874 root 1.457 warn "running post_init jobs";
3875 root 1.453 (pop @POST_INIT)->(1) while @POST_INIT;
3876    
3877 root 1.441 warn "leaving sync_job";
3878 root 1.183
3879 root 1.441 1
3880     } or do {
3881     warn $@;
3882     cf::cleanup "error while reloading, exiting.";
3883     };
3884 root 1.183
3885 root 1.441 warn "reloaded";
3886     --$RELOAD;
3887     }
3888 root 1.457
3889 root 1.512 $t1 = AE::time - $t1;
3890 root 1.457 warn "reload completed in ${t1}s\n";
3891 root 1.65 };
3892    
3893 root 1.175 our $RELOAD_WATCHER; # used only during reload
3894    
3895 root 1.246 sub reload_perl() {
3896     # doing reload synchronously and two reloads happen back-to-back,
3897     # coro crashes during coro_state_free->destroy here.
3898    
3899 root 1.457 $RELOAD_WATCHER ||= cf::async {
3900     Coro::AIO::aio_wait cache_extensions;
3901    
3902 root 1.512 $RELOAD_WATCHER = AE::timer $TICK * 1.5, 0, sub {
3903 root 1.457 do_reload_perl;
3904     undef $RELOAD_WATCHER;
3905     };
3906 root 1.396 };
3907 root 1.246 }
3908    
3909 root 1.111 register_command "reload" => sub {
3910 root 1.65 my ($who, $arg) = @_;
3911    
3912     if ($who->flag (FLAG_WIZ)) {
3913 root 1.175 $who->message ("reloading server.");
3914 root 1.374 async {
3915     $Coro::current->{desc} = "perl_reload";
3916     reload_perl;
3917     };
3918 root 1.65 }
3919     };
3920    
3921 root 1.27 unshift @INC, $LIBDIR;
3922 root 1.17
3923 root 1.183 my $bug_warning = 0;
3924    
3925 root 1.239 our @WAIT_FOR_TICK;
3926     our @WAIT_FOR_TICK_BEGIN;
3927    
3928     sub wait_for_tick {
3929 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
3930 root 1.241
3931 root 1.239 my $signal = new Coro::Signal;
3932     push @WAIT_FOR_TICK, $signal;
3933     $signal->wait;
3934     }
3935    
3936     sub wait_for_tick_begin {
3937 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
3938 root 1.241
3939 root 1.239 my $signal = new Coro::Signal;
3940     push @WAIT_FOR_TICK_BEGIN, $signal;
3941     $signal->wait;
3942     }
3943    
3944 root 1.412 sub tick {
3945 root 1.396 if ($Coro::current != $Coro::main) {
3946     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3947     unless ++$bug_warning > 10;
3948     return;
3949     }
3950    
3951     cf::server_tick; # one server iteration
3952 root 1.245
3953 root 1.512 #for(1..3e6){} AE::now_update; $NOW=AE::now; # generate load #d#
3954 root 1.502
3955 root 1.396 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3956 root 1.402 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
3957 root 1.396 Coro::async_pool {
3958     $Coro::current->{desc} = "runtime saver";
3959 root 1.417 write_runtime_sync
3960 root 1.396 or warn "ERROR: unable to write runtime file: $!";
3961     };
3962     }
3963 root 1.265
3964 root 1.396 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3965     $sig->send;
3966     }
3967     while (my $sig = shift @WAIT_FOR_TICK) {
3968     $sig->send;
3969     }
3970 root 1.265
3971 root 1.412 $LOAD = ($NOW - $TICK_START) / $TICK;
3972 root 1.396 $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3973 root 1.265
3974 root 1.412 if (0) {
3975     if ($NEXT_TICK) {
3976     my $jitter = $TICK_START - $NEXT_TICK;
3977     $JITTER = $JITTER * 0.75 + $jitter * 0.25;
3978     warn "jitter $JITTER\n";#d#
3979     }
3980     }
3981     }
3982 root 1.35
3983 root 1.206 {
3984 root 1.401 # configure BDB
3985    
3986 root 1.503 BDB::min_parallel 16;
3987 root 1.400 BDB::max_poll_reqs $TICK * 0.1;
3988 root 1.433 $AnyEvent::BDB::WATCHER->priority (1);
3989 root 1.77
3990 root 1.206 unless ($DB_ENV) {
3991     $DB_ENV = BDB::db_env_create;
3992 root 1.437 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT);
3993     $DB_ENV->set_flags (&BDB::LOG_AUTOREMOVE ) if BDB::VERSION v0, v4.7;
3994     $DB_ENV->log_set_config (&BDB::LOG_AUTO_REMOVE) if BDB::VERSION v4.7;
3995 root 1.371 $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
3996     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
3997 root 1.206
3998     cf::sync_job {
3999 root 1.208 eval {
4000     BDB::db_env_open
4001     $DB_ENV,
4002 root 1.253 $BDBDIR,
4003 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
4004     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
4005     0666;
4006    
4007 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
4008 root 1.208 };
4009    
4010     cf::cleanup "db_env_open(db): $@" if $@;
4011 root 1.206 };
4012     }
4013 root 1.363
4014 root 1.396 $BDB_DEADLOCK_WATCHER = EV::periodic 0, 3, 0, sub {
4015     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
4016     };
4017     $BDB_CHECKPOINT_WATCHER = EV::periodic 0, 60, 0, sub {
4018     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
4019     };
4020     $BDB_TRICKLE_WATCHER = EV::periodic 0, 10, 0, sub {
4021     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
4022     };
4023 root 1.206 }
4024    
4025     {
4026 root 1.401 # configure IO::AIO
4027    
4028 root 1.206 IO::AIO::min_parallel 8;
4029     IO::AIO::max_poll_time $TICK * 0.1;
4030 root 1.435 undef $AnyEvent::AIO::WATCHER;
4031 root 1.206 }
4032 root 1.108
4033 root 1.262 my $_log_backtrace;
4034    
4035 root 1.260 sub _log_backtrace {
4036     my ($msg, @addr) = @_;
4037    
4038 root 1.262 $msg =~ s/\n//;
4039 root 1.260
4040 root 1.262 # limit the # of concurrent backtraces
4041     if ($_log_backtrace < 2) {
4042     ++$_log_backtrace;
4043 root 1.446 my $perl_bt = Carp::longmess $msg;
4044 root 1.262 async {
4045 root 1.374 $Coro::current->{desc} = "abt $msg";
4046    
4047 root 1.262 my @bt = fork_call {
4048     @addr = map { sprintf "%x", $_ } @addr;
4049     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
4050     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
4051     or die "addr2line: $!";
4052    
4053     my @funcs;
4054     my @res = <$fh>;
4055     chomp for @res;
4056     while (@res) {
4057     my ($func, $line) = splice @res, 0, 2, ();
4058     push @funcs, "[$func] $line";
4059     }
4060 root 1.260
4061 root 1.262 @funcs
4062     };
4063 root 1.260
4064 root 1.446 LOG llevInfo, "[ABT] $perl_bt\n";
4065     LOG llevInfo, "[ABT] --- C backtrace follows ---\n";
4066 root 1.262 LOG llevInfo, "[ABT] $_\n" for @bt;
4067     --$_log_backtrace;
4068     };
4069     } else {
4070 root 1.260 LOG llevInfo, "[ABT] $msg\n";
4071 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
4072     }
4073 root 1.260 }
4074    
4075 root 1.249 # load additional modules
4076 root 1.467 require "cf/$_.pm" for @EXTRA_MODULES;
4077 root 1.483 cf::_connect_to_perl_2;
4078 root 1.249
4079 root 1.125 END { cf::emergency_save }
4080    
4081 root 1.1 1
4082