ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.495
Committed: Tue Nov 10 04:38:45 2009 UTC (14 years, 6 months ago) by root
Branch: MAIN
Changes since 1.494: +3 -2 lines
Log Message:
material overhaul

File Contents

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