ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.513
Committed: Mon Apr 12 05:22:38 2010 UTC (14 years, 1 month ago) by root
Branch: MAIN
Changes since 1.512: +3 -0 lines
Log Message:
freelist management

File Contents

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