ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.473
Committed: Fri Jun 26 16:32:10 2009 UTC (14 years, 11 months ago) by root
Branch: MAIN
Changes since 1.472: +8 -2 lines
Log Message:
*** empty log message ***

File Contents

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