ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.249
Committed: Wed Apr 18 12:43:54 2007 UTC (17 years, 1 month ago) by root
Branch: MAIN
Changes since 1.248: +42 -0 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 package cf;
2    
3 root 1.96 use utf8;
4     use strict;
5    
6 root 1.1 use Symbol;
7     use List::Util;
8 root 1.6 use Storable;
9 root 1.224 use Event;
10 root 1.23 use Opcode;
11     use Safe;
12     use Safe::Hole;
13 root 1.19
14 root 1.244 use Coro 3.61 ();
15 root 1.224 use Coro::State;
16 root 1.96 use Coro::Event;
17     use Coro::Timer;
18     use Coro::Signal;
19     use Coro::Semaphore;
20 root 1.105 use Coro::AIO;
21 root 1.237 use Coro::Storable;
22 root 1.96
23 root 1.206 use BDB ();
24 root 1.154 use Data::Dumper;
25 root 1.108 use Digest::MD5;
26 root 1.105 use Fcntl;
27 root 1.227 use YAML::Syck ();
28 root 1.145 use IO::AIO 2.32 ();
29 root 1.32 use Time::HiRes;
30 root 1.208 use Compress::LZF;
31    
32 root 1.227 # configure various modules to our taste
33     #
34 root 1.237 $Storable::canonical = 1; # reduce rsync transfers
35 root 1.224 Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator
36 root 1.208 Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later
37 root 1.227
38 root 1.224 $Event::Eval = 1; # no idea why this is required, but it is
39 root 1.1
40 root 1.72 # work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
41     $YAML::Syck::ImplicitUnicode = 1;
42    
43 root 1.139 $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
44 root 1.1
45 root 1.227 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
46    
47 root 1.85 our %COMMAND = ();
48     our %COMMAND_TIME = ();
49 root 1.159
50     our @EXTS = (); # list of extension package names
51 root 1.85 our %EXTCMD = ();
52 root 1.159 our %EXT_CORO = (); # coroutines bound to extensions
53 root 1.161 our %EXT_MAP = (); # pluggable maps
54 root 1.85
55 root 1.223 our $RELOAD; # number of reloads so far
56 root 1.1 our @EVENT;
57 root 1.88 our $LIBDIR = datadir . "/ext";
58 root 1.1
59 root 1.245 our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!)
60 root 1.35 our $TICK_WATCHER;
61 root 1.168 our $AIO_POLL_WATCHER;
62 root 1.214 our $NEXT_RUNTIME_WRITE; # when should the runtime file be written
63 root 1.35 our $NEXT_TICK;
64 root 1.103 our $NOW;
65 root 1.205 our $USE_FSYNC = 1; # use fsync to write maps - default off
66 root 1.35
67 root 1.206 our $BDB_POLL_WATCHER;
68     our $DB_ENV;
69    
70 root 1.70 our %CFG;
71    
72 root 1.84 our $UPTIME; $UPTIME ||= time;
73 root 1.103 our $RUNTIME;
74    
75 root 1.143 our %PLAYER; # all users
76     our %MAP; # all maps
77 root 1.166 our $LINK_MAP; # the special {link} map, which is always available
78 root 1.108 our $RANDOM_MAPS = cf::localdir . "/random";
79 root 1.206 our $BDB_ENV_DIR = cf::localdir . "/db";
80 root 1.103
81 root 1.166 # used to convert map paths into valid unix filenames by replacing / by ∕
82     our $PATH_SEP = "∕"; # U+2215, chosen purely for visual reasons
83    
84 root 1.103 binmode STDOUT;
85     binmode STDERR;
86    
87     # read virtual server time, if available
88     unless ($RUNTIME || !-e cf::localdir . "/runtime") {
89     open my $fh, "<", cf::localdir . "/runtime"
90     or die "unable to read runtime file: $!";
91     $RUNTIME = <$fh> + 0.;
92     }
93    
94     mkdir cf::localdir;
95     mkdir cf::localdir . "/" . cf::playerdir;
96     mkdir cf::localdir . "/" . cf::tmpdir;
97     mkdir cf::localdir . "/" . cf::uniquedir;
98 root 1.108 mkdir $RANDOM_MAPS;
99 root 1.206 mkdir $BDB_ENV_DIR;
100 root 1.103
101 root 1.131 our $EMERGENCY_POSITION;
102 root 1.110
103 root 1.199 sub cf::map::normalise;
104    
105 root 1.70 #############################################################################
106    
107     =head2 GLOBAL VARIABLES
108    
109     =over 4
110    
111 root 1.83 =item $cf::UPTIME
112    
113     The timestamp of the server start (so not actually an uptime).
114    
115 root 1.103 =item $cf::RUNTIME
116    
117     The time this server has run, starts at 0 and is increased by $cf::TICK on
118     every server tick.
119    
120 root 1.70 =item $cf::LIBDIR
121    
122     The perl library directory, where extensions and cf-specific modules can
123     be found. It will be added to C<@INC> automatically.
124    
125 root 1.103 =item $cf::NOW
126    
127     The time of the last (current) server tick.
128    
129 root 1.70 =item $cf::TICK
130    
131     The interval between server ticks, in seconds.
132    
133     =item %cf::CFG
134    
135     Configuration for the server, loaded from C</etc/crossfire/config>, or
136     from wherever your confdir points to.
137    
138 root 1.239 =item cf::wait_for_tick, cf::wait_for_tick_begin
139 root 1.155
140 root 1.239 These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only
141     returns directly I<after> the tick processing (and consequently, can only wake one process
142     per tick), while cf::wait_for_tick wakes up all waiters after tick processing.
143 root 1.155
144 root 1.70 =back
145    
146     =cut
147    
148 root 1.1 BEGIN {
149     *CORE::GLOBAL::warn = sub {
150     my $msg = join "", @_;
151 root 1.103 utf8::encode $msg;
152    
153 root 1.1 $msg .= "\n"
154     unless $msg =~ /\n$/;
155    
156 root 1.146 LOG llevError, $msg;
157 root 1.1 };
158     }
159    
160 root 1.93 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
161     @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
162     @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
163     @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
164     @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
165 root 1.41 @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
166 root 1.25
167 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
168 root 1.25 # within the Safe compartment.
169 root 1.86 for my $pkg (qw(
170 root 1.100 cf::global cf::attachable
171 root 1.86 cf::object cf::object::player
172 root 1.89 cf::client cf::player
173 root 1.86 cf::arch cf::living
174     cf::map cf::party cf::region
175     )) {
176 root 1.25 no strict 'refs';
177 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
178 root 1.25 }
179 root 1.1
180 root 1.18 $Event::DIED = sub {
181     warn "error in event callback: @_";
182     };
183    
184 root 1.70 =head2 UTILITY FUNCTIONS
185    
186     =over 4
187    
188 root 1.154 =item dumpval $ref
189    
190 root 1.70 =cut
191 root 1.44
192 root 1.154 sub dumpval {
193     eval {
194     local $SIG{__DIE__};
195     my $d;
196     if (1) {
197     $d = new Data::Dumper([$_[0]], ["*var"]);
198     $d->Terse(1);
199     $d->Indent(2);
200     $d->Quotekeys(0);
201     $d->Useqq(1);
202     #$d->Bless(...);
203     $d->Seen($_[1]) if @_ > 1;
204     $d = $d->Dump();
205     }
206     $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge;
207     $d
208     } || "[unable to dump $_[0]: '$@']";
209     }
210    
211 root 1.227 use JSON::XS qw(to_json from_json); # TODO# replace by JSON::PC once working
212 root 1.44
213 root 1.70 =item $ref = cf::from_json $json
214    
215     Converts a JSON string into the corresponding perl data structure.
216    
217     =item $json = cf::to_json $ref
218    
219     Converts a perl data structure into its JSON representation.
220    
221 root 1.120 =item cf::lock_wait $string
222    
223     Wait until the given lock is available. See cf::lock_acquire.
224    
225     =item my $lock = cf::lock_acquire $string
226    
227     Wait until the given lock is available and then acquires it and returns
228 root 1.135 a Coro::guard object. If the guard object gets destroyed (goes out of scope,
229 root 1.120 for example when the coroutine gets canceled), the lock is automatically
230     returned.
231    
232 root 1.133 Lock names should begin with a unique identifier (for example, cf::map::find
233     uses map_find and cf::map::load uses map_load).
234 root 1.120
235     =cut
236    
237     our %LOCK;
238    
239     sub lock_wait($) {
240     my ($key) = @_;
241    
242     # wait for lock, if any
243     while ($LOCK{$key}) {
244     push @{ $LOCK{$key} }, $Coro::current;
245     Coro::schedule;
246     }
247     }
248    
249     sub lock_acquire($) {
250     my ($key) = @_;
251    
252     # wait, to be sure we are not locked
253     lock_wait $key;
254    
255     $LOCK{$key} = [];
256    
257 root 1.135 Coro::guard {
258 root 1.120 # wake up all waiters, to be on the safe side
259     $_->ready for @{ delete $LOCK{$key} };
260     }
261     }
262    
263 root 1.133 sub freeze_mainloop {
264     return unless $TICK_WATCHER->is_active;
265    
266 root 1.168 my $guard = Coro::guard {
267     $TICK_WATCHER->start;
268     };
269 root 1.133 $TICK_WATCHER->stop;
270     $guard
271     }
272    
273 root 1.140 =item cf::async { BLOCK }
274    
275     Currently the same as Coro::async_pool, meaning you cannot use
276     C<on_destroy>, C<join> or other gimmicks on these coroutines. The only
277     thing you are allowed to do is call C<prio> on it.
278    
279     =cut
280    
281     BEGIN { *async = \&Coro::async_pool }
282    
283 root 1.106 =item cf::sync_job { BLOCK }
284    
285     The design of crossfire+ requires that the main coro ($Coro::main) is
286     always able to handle events or runnable, as crossfire+ is only partly
287     reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable.
288    
289     If it must be done, put the blocking parts into C<sync_job>. This will run
290     the given BLOCK in another coroutine while waiting for the result. The
291     server will be frozen during this time, so the block should either finish
292     fast or be very important.
293    
294     =cut
295    
296 root 1.105 sub sync_job(&) {
297     my ($job) = @_;
298    
299     if ($Coro::current == $Coro::main) {
300 root 1.112 # this is the main coro, too bad, we have to block
301     # till the operation succeeds, freezing the server :/
302    
303 root 1.110 # TODO: use suspend/resume instead
304 root 1.112 # (but this is cancel-safe)
305 root 1.133 my $freeze_guard = freeze_mainloop;
306 root 1.112
307     my $busy = 1;
308     my @res;
309    
310 root 1.140 (async {
311 root 1.112 @res = eval { $job->() };
312     warn $@ if $@;
313     undef $busy;
314     })->prio (Coro::PRIO_MAX);
315    
316 root 1.105 while ($busy) {
317 root 1.141 Coro::cede or Event::one_event;
318 root 1.105 }
319 root 1.112
320     wantarray ? @res : $res[0]
321 root 1.105 } else {
322 root 1.112 # we are in another coroutine, how wonderful, everything just works
323    
324     $job->()
325 root 1.105 }
326     }
327    
328 root 1.140 =item $coro = cf::async_ext { BLOCK }
329 root 1.103
330 root 1.159 Like async, but this coro is automatically being canceled when the
331 root 1.140 extension calling this is being unloaded.
332 root 1.103
333     =cut
334    
335 root 1.140 sub async_ext(&) {
336 root 1.103 my $cb = shift;
337    
338 root 1.140 my $coro = &Coro::async ($cb);
339 root 1.103
340     $coro->on_destroy (sub {
341     delete $EXT_CORO{$coro+0};
342     });
343     $EXT_CORO{$coro+0} = $coro;
344    
345     $coro
346     }
347    
348 root 1.108 sub write_runtime {
349 root 1.219 my $guard = cf::lock_acquire "write_runtime";
350    
351 root 1.108 my $runtime = cf::localdir . "/runtime";
352    
353     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
354     or return;
355    
356 root 1.186 my $value = $cf::RUNTIME + 90 + 10;
357     # 10 is the runtime save interval, for a monotonic clock
358     # 60 allows for the watchdog to kill the server.
359    
360 root 1.108 (aio_write $fh, 0, (length $value), $value, 0) <= 0
361     and return;
362    
363 root 1.204 # always fsync - this file is important
364 root 1.108 aio_fsync $fh
365     and return;
366    
367     close $fh
368     or return;
369    
370     aio_rename "$runtime~", $runtime
371     and return;
372    
373     1
374     }
375    
376 root 1.230 =item cf::datalog type => key => value, ...
377    
378     Log a datalog packet of the given type with the given key-value pairs.
379    
380     =cut
381    
382     sub datalog($@) {
383     my ($type, %kv) = @_;
384     warn "DATALOG ", JSON::XS->new->ascii->encode ({ %kv, type => $type });
385     }
386    
387 root 1.70 =back
388    
389 root 1.71 =cut
390    
391 root 1.44 #############################################################################
392 root 1.39
393 root 1.93 =head2 ATTACHABLE OBJECTS
394    
395 root 1.94 Many objects in crossfire are so-called attachable objects. That means you can
396     attach callbacks/event handlers (a collection of which is called an "attachment")
397     to it. All such attachable objects support the following methods.
398    
399     In the following description, CLASS can be any of C<global>, C<object>
400     C<player>, C<client> or C<map> (i.e. the attachable objects in
401     crossfire+).
402 root 1.55
403     =over 4
404    
405 root 1.94 =item $attachable->attach ($attachment, key => $value...)
406    
407     =item $attachable->detach ($attachment)
408    
409     Attach/detach a pre-registered attachment to a specific object and give it
410     the specified key/value pairs as arguments.
411    
412     Example, attach a minesweeper attachment to the given object, making it a
413     10x10 minesweeper game:
414 root 1.46
415 root 1.94 $obj->attach (minesweeper => width => 10, height => 10);
416 root 1.53
417 root 1.93 =item $bool = $attachable->attached ($name)
418 root 1.46
419 root 1.93 Checks wether the named attachment is currently attached to the object.
420 root 1.46
421 root 1.94 =item cf::CLASS->attach ...
422 root 1.46
423 root 1.94 =item cf::CLASS->detach ...
424 root 1.92
425 root 1.94 Define an anonymous attachment and attach it to all objects of the given
426     CLASS. See the next function for an explanation of its arguments.
427 root 1.92
428 root 1.93 You can attach to global events by using the C<cf::global> class.
429 root 1.92
430 root 1.94 Example, log all player logins:
431    
432     cf::player->attach (
433     on_login => sub {
434     my ($pl) = @_;
435     ...
436     },
437     );
438    
439     Example, attach to the jeweler skill:
440    
441     cf::object->attach (
442     type => cf::SKILL,
443     subtype => cf::SK_JEWELER,
444     on_use_skill => sub {
445     my ($sk, $ob, $part, $dir, $msg) = @_;
446     ...
447     },
448     );
449    
450     =item cf::CLASS::attachment $name, ...
451    
452     Register an attachment by C<$name> through which attachable objects of the
453     given CLASS can refer to this attachment.
454    
455     Some classes such as crossfire maps and objects can specify attachments
456     that are attached at load/instantiate time, thus the need for a name.
457    
458     These calls expect any number of the following handler/hook descriptions:
459 root 1.46
460     =over 4
461    
462     =item prio => $number
463    
464     Set the priority for all following handlers/hooks (unless overwritten
465     by another C<prio> setting). Lower priority handlers get executed
466     earlier. The default priority is C<0>, and many built-in handlers are
467     registered at priority C<-1000>, so lower priorities should not be used
468     unless you know what you are doing.
469    
470 root 1.93 =item type => $type
471    
472     (Only for C<< cf::object->attach >> calls), limits the attachment to the
473     given type of objects only (the additional parameter C<subtype> can be
474     used to further limit to the given subtype).
475    
476 root 1.46 =item on_I<event> => \&cb
477    
478     Call the given code reference whenever the named event happens (event is
479     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
480     handlers are recognised generally depends on the type of object these
481     handlers attach to).
482    
483     See F<include/eventinc.h> for the full list of events supported, and their
484     class.
485    
486     =item package => package::
487    
488     Look for sub functions of the name C<< on_I<event> >> in the given
489     package and register them. Only handlers for eevents supported by the
490     object/class are recognised.
491    
492     =back
493    
494 root 1.94 Example, define an attachment called "sockpuppet" that calls the given
495     event handler when a monster attacks:
496    
497     cf::object::attachment sockpuppet =>
498     on_skill_attack => sub {
499     my ($self, $victim) = @_;
500     ...
501     }
502     }
503    
504 root 1.96 =item $attachable->valid
505    
506     Just because you have a perl object does not mean that the corresponding
507     C-level object still exists. If you try to access an object that has no
508     valid C counterpart anymore you get an exception at runtime. This method
509     can be used to test for existence of the C object part without causing an
510     exception.
511    
512 root 1.39 =cut
513    
514 root 1.40 # the following variables are defined in .xs and must not be re-created
515 root 1.100 our @CB_GLOBAL = (); # registry for all global events
516     our @CB_ATTACHABLE = (); # registry for all attachables
517     our @CB_OBJECT = (); # all objects (should not be used except in emergency)
518     our @CB_PLAYER = ();
519     our @CB_CLIENT = ();
520     our @CB_TYPE = (); # registry for type (cf-object class) based events
521     our @CB_MAP = ();
522 root 1.39
523 root 1.45 my %attachment;
524    
525 root 1.170 sub cf::attachable::thawer_merge {
526     # simply override everything except _meta
527     local $_[0]{_meta};
528     %{$_[0]} = %{$_[1]};
529     }
530    
531 root 1.93 sub _attach_cb($$$$) {
532     my ($registry, $event, $prio, $cb) = @_;
533 root 1.39
534     use sort 'stable';
535    
536     $cb = [$prio, $cb];
537    
538     @{$registry->[$event]} = sort
539     { $a->[0] cmp $b->[0] }
540     @{$registry->[$event] || []}, $cb;
541     }
542    
543 root 1.100 # hack
544     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
545    
546 root 1.39 # attach handles attaching event callbacks
547     # the only thing the caller has to do is pass the correct
548     # registry (== where the callback attaches to).
549 root 1.93 sub _attach {
550 root 1.45 my ($registry, $klass, @arg) = @_;
551 root 1.39
552 root 1.93 my $object_type;
553 root 1.39 my $prio = 0;
554     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
555    
556 root 1.100 #TODO: get rid of this hack
557     if ($attachable_klass{$klass}) {
558     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
559     }
560    
561 root 1.45 while (@arg) {
562     my $type = shift @arg;
563 root 1.39
564     if ($type eq "prio") {
565 root 1.45 $prio = shift @arg;
566 root 1.39
567 root 1.93 } elsif ($type eq "type") {
568     $object_type = shift @arg;
569     $registry = $CB_TYPE[$object_type] ||= [];
570    
571     } elsif ($type eq "subtype") {
572     defined $object_type or Carp::croak "subtype specified without type";
573     my $object_subtype = shift @arg;
574     $registry = $CB_TYPE[$object_type + $object_subtype * NUM_SUBTYPES] ||= [];
575    
576 root 1.39 } elsif ($type eq "package") {
577 root 1.45 my $pkg = shift @arg;
578 root 1.39
579     while (my ($name, $id) = each %cb_id) {
580     if (my $cb = $pkg->can ($name)) {
581 root 1.93 _attach_cb $registry, $id, $prio, $cb;
582 root 1.39 }
583     }
584    
585     } elsif (exists $cb_id{$type}) {
586 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
587 root 1.39
588     } elsif (ref $type) {
589     warn "attaching objects not supported, ignoring.\n";
590    
591     } else {
592 root 1.45 shift @arg;
593 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
594     }
595     }
596     }
597    
598 root 1.93 sub _object_attach {
599 root 1.48 my ($obj, $name, %arg) = @_;
600 root 1.46
601 root 1.55 return if exists $obj->{_attachment}{$name};
602    
603 root 1.46 if (my $attach = $attachment{$name}) {
604     my $registry = $obj->registry;
605    
606 root 1.47 for (@$attach) {
607     my ($klass, @attach) = @$_;
608 root 1.93 _attach $registry, $klass, @attach;
609 root 1.47 }
610 root 1.46
611 root 1.48 $obj->{$name} = \%arg;
612 root 1.46 } else {
613     warn "object uses attachment '$name' that is not available, postponing.\n";
614     }
615    
616 root 1.50 $obj->{_attachment}{$name} = undef;
617 root 1.46 }
618    
619 root 1.93 sub cf::attachable::attach {
620     if (ref $_[0]) {
621     _object_attach @_;
622     } else {
623     _attach shift->_attach_registry, @_;
624     }
625 root 1.55 };
626 root 1.46
627 root 1.54 # all those should be optimised
628 root 1.93 sub cf::attachable::detach {
629 root 1.54 my ($obj, $name) = @_;
630 root 1.46
631 root 1.93 if (ref $obj) {
632     delete $obj->{_attachment}{$name};
633     reattach ($obj);
634     } else {
635     Carp::croak "cannot, currently, detach class attachments";
636     }
637 root 1.55 };
638    
639 root 1.93 sub cf::attachable::attached {
640 root 1.55 my ($obj, $name) = @_;
641    
642     exists $obj->{_attachment}{$name}
643 root 1.39 }
644    
645 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
646 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
647     sub cf::\L$klass\E::_attach_registry {
648     (\\\@CB_$klass, KLASS_$klass)
649     }
650 root 1.45
651 root 1.93 sub cf::\L$klass\E::attachment {
652     my \$name = shift;
653 root 1.39
654 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
655     }
656     ";
657     die if $@;
658 root 1.52 }
659    
660 root 1.39 our $override;
661 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
662 root 1.39
663 root 1.45 sub override {
664     $override = 1;
665     @invoke_results = ();
666 root 1.39 }
667    
668 root 1.45 sub do_invoke {
669 root 1.39 my $event = shift;
670 root 1.40 my $callbacks = shift;
671 root 1.39
672 root 1.45 @invoke_results = ();
673    
674 root 1.39 local $override;
675    
676 root 1.40 for (@$callbacks) {
677 root 1.39 eval { &{$_->[1]} };
678    
679     if ($@) {
680     warn "$@";
681 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
682 root 1.39 override;
683     }
684    
685     return 1 if $override;
686     }
687    
688     0
689     }
690    
691 root 1.96 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
692 root 1.55
693 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
694 root 1.55
695 root 1.96 Generate an object-specific event with the given arguments.
696 root 1.55
697 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
698 root 1.55 removed in future versions), and there is no public API to access override
699     results (if you must, access C<@cf::invoke_results> directly).
700    
701     =back
702    
703 root 1.71 =cut
704    
705 root 1.70 #############################################################################
706 root 1.45 # object support
707    
708 root 1.102 sub reattach {
709     # basically do the same as instantiate, without calling instantiate
710     my ($obj) = @_;
711    
712 root 1.169 bless $obj, ref $obj; # re-bless in case extensions have been reloaded
713    
714 root 1.102 my $registry = $obj->registry;
715    
716     @$registry = ();
717    
718     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
719    
720     for my $name (keys %{ $obj->{_attachment} || {} }) {
721     if (my $attach = $attachment{$name}) {
722     for (@$attach) {
723     my ($klass, @attach) = @$_;
724     _attach $registry, $klass, @attach;
725     }
726     } else {
727     warn "object uses attachment '$name' that is not available, postponing.\n";
728     }
729     }
730     }
731    
732 root 1.100 cf::attachable->attach (
733     prio => -1000000,
734     on_instantiate => sub {
735     my ($obj, $data) = @_;
736 root 1.45
737 root 1.100 $data = from_json $data;
738 root 1.45
739 root 1.100 for (@$data) {
740     my ($name, $args) = @$_;
741 root 1.49
742 root 1.100 $obj->attach ($name, %{$args || {} });
743     }
744     },
745 root 1.102 on_reattach => \&reattach,
746 root 1.100 on_clone => sub {
747     my ($src, $dst) = @_;
748    
749     @{$dst->registry} = @{$src->registry};
750    
751     %$dst = %$src;
752    
753     %{$dst->{_attachment}} = %{$src->{_attachment}}
754     if exists $src->{_attachment};
755     },
756     );
757 root 1.45
758 root 1.46 sub object_freezer_save {
759 root 1.59 my ($filename, $rdata, $objs) = @_;
760 root 1.46
761 root 1.105 sync_job {
762     if (length $$rdata) {
763     warn sprintf "saving %s (%d,%d)\n",
764     $filename, length $$rdata, scalar @$objs;
765 root 1.60
766 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
767 root 1.60 chmod SAVE_MODE, $fh;
768 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
769 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
770 root 1.60 close $fh;
771 root 1.105
772     if (@$objs) {
773     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
774     chmod SAVE_MODE, $fh;
775     my $data = Storable::nfreeze { version => 1, objs => $objs };
776     aio_write $fh, 0, (length $data), $data, 0;
777 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
778 root 1.105 close $fh;
779     aio_rename "$filename.pst~", "$filename.pst";
780     }
781     } else {
782     aio_unlink "$filename.pst";
783     }
784    
785     aio_rename "$filename~", $filename;
786 root 1.60 } else {
787 root 1.105 warn "FATAL: $filename~: $!\n";
788 root 1.60 }
789 root 1.59 } else {
790 root 1.105 aio_unlink $filename;
791     aio_unlink "$filename.pst";
792 root 1.59 }
793 root 1.45 }
794     }
795    
796 root 1.80 sub object_freezer_as_string {
797     my ($rdata, $objs) = @_;
798    
799     use Data::Dumper;
800    
801 root 1.81 $$rdata . Dumper $objs
802 root 1.80 }
803    
804 root 1.46 sub object_thawer_load {
805     my ($filename) = @_;
806    
807 root 1.105 my ($data, $av);
808 root 1.61
809 root 1.105 (aio_load $filename, $data) >= 0
810     or return;
811 root 1.61
812 root 1.105 unless (aio_stat "$filename.pst") {
813     (aio_load "$filename.pst", $av) >= 0
814     or return;
815 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
816 root 1.61 }
817 root 1.45
818 root 1.118 warn sprintf "loading %s (%d)\n",
819     $filename, length $data, scalar @{$av || []};#d#
820 root 1.105 return ($data, $av);
821 root 1.45 }
822    
823     #############################################################################
824 root 1.85 # command handling &c
825 root 1.39
826 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
827 root 1.1
828 root 1.85 Register a callback for execution when the client sends the user command
829     $name.
830 root 1.5
831 root 1.85 =cut
832 root 1.5
833 root 1.85 sub register_command {
834     my ($name, $cb) = @_;
835 root 1.5
836 root 1.85 my $caller = caller;
837     #warn "registering command '$name/$time' to '$caller'";
838 root 1.1
839 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
840 root 1.1 }
841    
842 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
843 root 1.1
844 root 1.85 Register a callbackf ro execution when the client sends an extcmd packet.
845 root 1.1
846 root 1.85 If the callback returns something, it is sent back as if reply was being
847     called.
848 root 1.1
849 root 1.85 =cut
850 root 1.1
851 root 1.16 sub register_extcmd {
852     my ($name, $cb) = @_;
853    
854 root 1.159 $EXTCMD{$name} = $cb;
855 root 1.16 }
856    
857 root 1.93 cf::player->attach (
858 root 1.85 on_command => sub {
859     my ($pl, $name, $params) = @_;
860    
861     my $cb = $COMMAND{$name}
862     or return;
863    
864     for my $cmd (@$cb) {
865     $cmd->[1]->($pl->ob, $params);
866     }
867    
868     cf::override;
869     },
870     on_extcmd => sub {
871     my ($pl, $buf) = @_;
872    
873     my $msg = eval { from_json $buf };
874    
875     if (ref $msg) {
876     if (my $cb = $EXTCMD{$msg->{msgtype}}) {
877 root 1.159 if (my %reply = $cb->($pl, $msg)) {
878 root 1.85 $pl->ext_reply ($msg->{msgid}, %reply);
879     }
880     }
881     } else {
882     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
883     }
884    
885     cf::override;
886     },
887 root 1.93 );
888 root 1.85
889 root 1.1 sub load_extension {
890     my ($path) = @_;
891    
892     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
893 root 1.5 my $base = $1;
894 root 1.1 my $pkg = $1;
895     $pkg =~ s/[^[:word:]]/_/g;
896 root 1.41 $pkg = "ext::$pkg";
897 root 1.1
898 root 1.160 warn "... loading '$path' into '$pkg'\n";
899 root 1.1
900     open my $fh, "<:utf8", $path
901     or die "$path: $!";
902    
903     my $source =
904     "package $pkg; use strict; use utf8;\n"
905     . "#line 1 \"$path\"\n{\n"
906     . (do { local $/; <$fh> })
907     . "\n};\n1";
908    
909 root 1.166 unless (eval $source) {
910     my $msg = $@ ? "$path: $@\n"
911     : "extension disabled.\n";
912     if ($source =~ /^#!.*perl.*#.*MANDATORY/m) { # ugly match
913     warn $@;
914     warn "mandatory extension failed to load, exiting.\n";
915     exit 1;
916     }
917     die $@;
918     }
919 root 1.1
920 root 1.159 push @EXTS, $pkg;
921 root 1.1 }
922    
923     sub load_extensions {
924     for my $ext (<$LIBDIR/*.ext>) {
925 root 1.3 next unless -r $ext;
926 root 1.2 eval {
927     load_extension $ext;
928     1
929     } or warn "$ext not loaded: $@";
930 root 1.1 }
931     }
932    
933 root 1.8 #############################################################################
934 root 1.70
935     =head2 CORE EXTENSIONS
936    
937     Functions and methods that extend core crossfire objects.
938    
939 root 1.143 =cut
940    
941     package cf::player;
942    
943 root 1.154 use Coro::AIO;
944    
945 root 1.95 =head3 cf::player
946    
947 root 1.70 =over 4
948 root 1.22
949 root 1.143 =item cf::player::find $login
950 root 1.23
951 root 1.143 Returns the given player object, loading it if necessary (might block).
952 root 1.23
953     =cut
954    
955 root 1.145 sub playerdir($) {
956     cf::localdir
957     . "/"
958     . cf::playerdir
959     . "/"
960     . (ref $_[0] ? $_[0]->ob->name : $_[0])
961     }
962    
963 root 1.143 sub path($) {
964 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
965    
966 root 1.234 (playerdir $login) . "/playerdata"
967 root 1.143 }
968    
969     sub find_active($) {
970     $cf::PLAYER{$_[0]}
971     and $cf::PLAYER{$_[0]}->active
972     and $cf::PLAYER{$_[0]}
973     }
974    
975     sub exists($) {
976     my ($login) = @_;
977    
978     $cf::PLAYER{$login}
979 root 1.180 or cf::sync_job { !aio_stat path $login }
980 root 1.143 }
981    
982     sub find($) {
983     return $cf::PLAYER{$_[0]} || do {
984     my $login = $_[0];
985    
986     my $guard = cf::lock_acquire "user_find:$login";
987    
988 root 1.151 $cf::PLAYER{$_[0]} || do {
989 root 1.234 # rename old playerfiles to new ones
990     #TODO: remove when no longer required
991     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
992     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
993     aio_unlink +(playerdir $login) . "/$login.pl.pst";
994     aio_unlink +(playerdir $login) . "/$login.pl";
995    
996 root 1.151 my $pl = load_pl path $login
997     or return;
998     $cf::PLAYER{$login} = $pl
999     }
1000     }
1001 root 1.143 }
1002    
1003     sub save($) {
1004     my ($pl) = @_;
1005    
1006     return if $pl->{deny_save};
1007    
1008     my $path = path $pl;
1009     my $guard = cf::lock_acquire "user_save:$path";
1010    
1011     return if $pl->{deny_save};
1012 root 1.146
1013 root 1.154 aio_mkdir playerdir $pl, 0770;
1014 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1015    
1016     $pl->save_pl ($path);
1017     Coro::cede;
1018     }
1019    
1020     sub new($) {
1021     my ($login) = @_;
1022    
1023     my $self = create;
1024    
1025     $self->ob->name ($login);
1026     $self->{deny_save} = 1;
1027    
1028     $cf::PLAYER{$login} = $self;
1029    
1030     $self
1031 root 1.23 }
1032    
1033 root 1.154 =item $pl->quit_character
1034    
1035     Nukes the player without looking back. If logged in, the connection will
1036     be destroyed. May block for a long time.
1037    
1038     =cut
1039    
1040 root 1.145 sub quit_character {
1041     my ($pl) = @_;
1042    
1043 root 1.220 my $name = $pl->ob->name;
1044    
1045 root 1.145 $pl->{deny_save} = 1;
1046     $pl->password ("*"); # this should lock out the player until we nuked the dir
1047    
1048     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1049     $pl->deactivate;
1050     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1051     $pl->ns->destroy if $pl->ns;
1052    
1053     my $path = playerdir $pl;
1054     my $temp = "$path~$cf::RUNTIME~deleting~";
1055 root 1.154 aio_rename $path, $temp;
1056 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1057     $pl->destroy;
1058 root 1.220
1059     my $prefix = qr<^~\Q$name\E/>;
1060    
1061     # nuke player maps
1062     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1063    
1064 root 1.150 IO::AIO::aio_rmtree $temp;
1065 root 1.145 }
1066    
1067 pippijn 1.221 =item $pl->kick
1068    
1069     Kicks a player out of the game. This destroys the connection.
1070    
1071     =cut
1072    
1073     sub kick {
1074     my ($pl, $kicker) = @_;
1075    
1076     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1077     $pl->killer ("kicked");
1078     $pl->ns->destroy;
1079     }
1080    
1081 root 1.154 =item cf::player::list_logins
1082    
1083     Returns am arrayref of all valid playernames in the system, can take a
1084     while and may block, so not sync_job-capable, ever.
1085    
1086     =cut
1087    
1088     sub list_logins {
1089     my $dirs = aio_readdir cf::localdir . "/" . cf::playerdir
1090     or return [];
1091    
1092     my @logins;
1093    
1094     for my $login (@$dirs) {
1095     my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next;
1096     aio_read $fh, 0, 512, my $buf, 0 or next;
1097 root 1.155 $buf !~ /^password -------------$/m or next; # official not-valid tag
1098 root 1.154
1099     utf8::decode $login;
1100     push @logins, $login;
1101     }
1102    
1103     \@logins
1104     }
1105    
1106     =item $player->maps
1107    
1108 root 1.166 Returns an arrayref of map paths that are private for this
1109 root 1.154 player. May block.
1110    
1111     =cut
1112    
1113     sub maps($) {
1114     my ($pl) = @_;
1115    
1116 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1117    
1118 root 1.154 my $files = aio_readdir playerdir $pl
1119     or return;
1120    
1121     my @paths;
1122    
1123     for (@$files) {
1124     utf8::decode $_;
1125     next if /\.(?:pl|pst)$/;
1126 root 1.158 next unless /^$PATH_SEP/o;
1127 root 1.154
1128 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1129 root 1.154 }
1130    
1131     \@paths
1132     }
1133    
1134 root 1.231 =item $player->ext_reply ($msgid, %msg)
1135 root 1.95
1136     Sends an ext reply to the player.
1137    
1138     =cut
1139    
1140 root 1.231 sub ext_reply($$%) {
1141 root 1.95 my ($self, $id, %msg) = @_;
1142    
1143     $msg{msgid} = $id;
1144    
1145 root 1.143 $self->send ("ext " . cf::to_json \%msg);
1146 root 1.95 }
1147    
1148 root 1.231 =item $player->ext_event ($type, %msg)
1149    
1150     Sends an ext event to the client.
1151    
1152     =cut
1153    
1154     sub ext_event($$%) {
1155     my ($self, $type, %msg) = @_;
1156    
1157 root 1.232 $self->ns->ext_event ($type, %msg);
1158 root 1.231 }
1159    
1160 root 1.238 =head3 cf::region
1161    
1162     =over 4
1163    
1164     =cut
1165    
1166     package cf::region;
1167    
1168     =item cf::region::find_by_path $path
1169    
1170     Tries to decuce the probable region for a map knowing only its path.
1171    
1172     =cut
1173    
1174     sub find_by_path($) {
1175     my ($path) = @_;
1176    
1177     my ($match, $specificity);
1178    
1179     for my $region (list) {
1180     if ($region->match && $path =~ $region->match) {
1181     ($match, $specificity) = ($region, $region->specificity)
1182     if $region->specificity > $specificity;
1183     }
1184     }
1185    
1186     $match
1187     }
1188 root 1.143
1189 root 1.95 =back
1190    
1191 root 1.110 =head3 cf::map
1192    
1193     =over 4
1194    
1195     =cut
1196    
1197     package cf::map;
1198    
1199     use Fcntl;
1200     use Coro::AIO;
1201    
1202 root 1.166 use overload
1203 root 1.173 '""' => \&as_string,
1204     fallback => 1;
1205 root 1.166
1206 root 1.133 our $MAX_RESET = 3600;
1207     our $DEFAULT_RESET = 3000;
1208 root 1.110
1209     sub generate_random_map {
1210 root 1.166 my ($self, $rmp) = @_;
1211 root 1.110 # mit "rum" bekleckern, nicht
1212 root 1.166 $self->_create_random_map (
1213 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1214     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1215     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1216     $rmp->{exit_on_final_map},
1217     $rmp->{xsize}, $rmp->{ysize},
1218     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1219     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1220     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1221     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1222     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1223 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1224     )
1225 root 1.110 }
1226    
1227 root 1.187 =item cf::map->register ($regex, $prio)
1228    
1229     Register a handler for the map path matching the given regex at the
1230     givne priority (higher is better, built-in handlers have priority 0, the
1231     default).
1232    
1233     =cut
1234    
1235 root 1.166 sub register {
1236 root 1.187 my (undef, $regex, $prio) = @_;
1237 root 1.166 my $pkg = caller;
1238    
1239     no strict;
1240     push @{"$pkg\::ISA"}, __PACKAGE__;
1241    
1242 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1243 root 1.166 }
1244    
1245     # also paths starting with '/'
1246 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1247 root 1.166
1248 root 1.170 sub thawer_merge {
1249 root 1.172 my ($self, $merge) = @_;
1250    
1251 root 1.170 # we have to keep some variables in memory intact
1252 root 1.172 local $self->{path};
1253     local $self->{load_path};
1254     local $self->{deny_save};
1255     local $self->{deny_reset};
1256 root 1.170
1257 root 1.172 $self->SUPER::thawer_merge ($merge);
1258 root 1.170 }
1259    
1260 root 1.166 sub normalise {
1261     my ($path, $base) = @_;
1262    
1263 root 1.192 $path = "$path"; # make sure its a string
1264    
1265 root 1.199 $path =~ s/\.map$//;
1266    
1267 root 1.166 # map plan:
1268     #
1269     # /! non-realised random map exit (special hack!)
1270     # {... are special paths that are not being touched
1271     # ?xxx/... are special absolute paths
1272     # ?random/... random maps
1273     # /... normal maps
1274     # ~user/... per-player map of a specific user
1275    
1276     $path =~ s/$PATH_SEP/\//go;
1277    
1278     # treat it as relative path if it starts with
1279     # something that looks reasonable
1280     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1281     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1282    
1283     $base =~ s{[^/]+/?$}{};
1284     $path = "$base/$path";
1285     }
1286    
1287     for ($path) {
1288     redo if s{//}{/};
1289     redo if s{/\.?/}{/};
1290     redo if s{/[^/]+/\.\./}{/};
1291     }
1292    
1293     $path
1294     }
1295    
1296     sub new_from_path {
1297     my (undef, $path, $base) = @_;
1298    
1299     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1300    
1301     $path = normalise $path, $base;
1302    
1303 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1304     if ($path =~ $EXT_MAP{$pkg}[1]) {
1305 root 1.166 my $self = bless cf::map::new, $pkg;
1306     $self->{path} = $path; $self->path ($path);
1307     $self->init; # pass $1 etc.
1308     return $self;
1309     }
1310     }
1311    
1312 root 1.192 Carp::carp "unable to resolve path '$path' (base '$base').";
1313 root 1.166 ()
1314     }
1315    
1316     sub init {
1317     my ($self) = @_;
1318    
1319     $self
1320     }
1321    
1322     sub as_string {
1323     my ($self) = @_;
1324    
1325     "$self->{path}"
1326     }
1327    
1328     # the displayed name, this is a one way mapping
1329     sub visible_name {
1330     &as_string
1331     }
1332    
1333     # the original (read-only) location
1334     sub load_path {
1335     my ($self) = @_;
1336    
1337 root 1.200 sprintf "%s/%s/%s.map", cf::datadir, cf::mapdir, $self->{path}
1338 root 1.166 }
1339    
1340     # the temporary/swap location
1341     sub save_path {
1342     my ($self) = @_;
1343    
1344 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1345 root 1.200 sprintf "%s/%s/%s.map", cf::localdir, cf::tmpdir, $path
1346 root 1.166 }
1347    
1348     # the unique path, undef == no special unique path
1349     sub uniq_path {
1350     my ($self) = @_;
1351    
1352 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1353     sprintf "%s/%s/%s", cf::localdir, cf::uniquedir, $path
1354 root 1.166 }
1355    
1356 root 1.110 # and all this just because we cannot iterate over
1357     # all maps in C++...
1358     sub change_all_map_light {
1359     my ($change) = @_;
1360    
1361 root 1.122 $_->change_map_light ($change)
1362     for grep $_->outdoor, values %cf::MAP;
1363 root 1.110 }
1364    
1365 root 1.166 sub unlink_save {
1366     my ($self) = @_;
1367    
1368     utf8::encode (my $save = $self->save_path);
1369 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1370     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1371 root 1.166 }
1372    
1373     sub load_header_from($) {
1374     my ($self, $path) = @_;
1375 root 1.110
1376     utf8::encode $path;
1377 root 1.200 #aio_open $path, O_RDONLY, 0
1378     # or return;
1379 root 1.110
1380 root 1.166 $self->_load_header ($path)
1381 root 1.110 or return;
1382    
1383 root 1.166 $self->{load_path} = $path;
1384 root 1.135
1385 root 1.166 1
1386     }
1387 root 1.110
1388 root 1.188 sub load_header_orig {
1389 root 1.166 my ($self) = @_;
1390 root 1.110
1391 root 1.166 $self->load_header_from ($self->load_path)
1392 root 1.110 }
1393    
1394 root 1.188 sub load_header_temp {
1395 root 1.166 my ($self) = @_;
1396 root 1.110
1397 root 1.166 $self->load_header_from ($self->save_path)
1398     }
1399 root 1.110
1400 root 1.188 sub prepare_temp {
1401     my ($self) = @_;
1402    
1403     $self->last_access ((delete $self->{last_access})
1404     || $cf::RUNTIME); #d#
1405     # safety
1406     $self->{instantiate_time} = $cf::RUNTIME
1407     if $self->{instantiate_time} > $cf::RUNTIME;
1408     }
1409    
1410     sub prepare_orig {
1411     my ($self) = @_;
1412    
1413     $self->{load_original} = 1;
1414     $self->{instantiate_time} = $cf::RUNTIME;
1415     $self->last_access ($cf::RUNTIME);
1416     $self->instantiate;
1417     }
1418    
1419 root 1.166 sub load_header {
1420     my ($self) = @_;
1421 root 1.110
1422 root 1.188 if ($self->load_header_temp) {
1423     $self->prepare_temp;
1424 root 1.166 } else {
1425 root 1.188 $self->load_header_orig
1426 root 1.166 or return;
1427 root 1.188 $self->prepare_orig;
1428 root 1.166 }
1429 root 1.120
1430 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1431     unless $self->default_region;
1432    
1433 root 1.166 1
1434     }
1435 root 1.110
1436 root 1.166 sub find;
1437     sub find {
1438     my ($path, $origin) = @_;
1439 root 1.134
1440 root 1.166 $path = normalise $path, $origin && $origin->path;
1441 root 1.110
1442 root 1.166 cf::lock_wait "map_find:$path";
1443 root 1.110
1444 root 1.166 $cf::MAP{$path} || do {
1445     my $guard = cf::lock_acquire "map_find:$path";
1446     my $map = new_from_path cf::map $path
1447     or return;
1448 root 1.110
1449 root 1.116 $map->{last_save} = $cf::RUNTIME;
1450 root 1.110
1451 root 1.166 $map->load_header
1452     or return;
1453 root 1.134
1454 root 1.195 if ($map->should_reset && 0) {#d#TODO# disabled, crashy (locking issue?)
1455 root 1.185 # doing this can freeze the server in a sync job, obviously
1456     #$cf::WAIT_FOR_TICK->wait;
1457 root 1.112 $map->reset;
1458 root 1.123 undef $guard;
1459 root 1.192 return find $path;
1460 root 1.112 }
1461 root 1.110
1462 root 1.166 $cf::MAP{$path} = $map
1463 root 1.110 }
1464     }
1465    
1466 root 1.188 sub pre_load { }
1467     sub post_load { }
1468    
1469 root 1.110 sub load {
1470     my ($self) = @_;
1471    
1472 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
1473    
1474 root 1.120 my $path = $self->{path};
1475 root 1.166 my $guard = cf::lock_acquire "map_load:$path";
1476 root 1.120
1477 root 1.110 return if $self->in_memory != cf::MAP_SWAPPED;
1478    
1479     $self->in_memory (cf::MAP_LOADING);
1480    
1481     $self->alloc;
1482 root 1.188
1483     $self->pre_load;
1484 root 1.239 Coro::cede;
1485 root 1.188
1486 root 1.166 $self->_load_objects ($self->{load_path}, 1)
1487 root 1.110 or return;
1488    
1489 root 1.112 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1490     if delete $self->{load_original};
1491 root 1.111
1492 root 1.166 if (my $uniq = $self->uniq_path) {
1493 root 1.110 utf8::encode $uniq;
1494     if (aio_open $uniq, O_RDONLY, 0) {
1495     $self->clear_unique_items;
1496 root 1.166 $self->_load_objects ($uniq, 0);
1497 root 1.110 }
1498     }
1499    
1500 root 1.134 Coro::cede;
1501 root 1.110 # now do the right thing for maps
1502     $self->link_multipart_objects;
1503 root 1.239 Coro::cede;
1504 root 1.110
1505 root 1.166 unless ($self->{deny_activate}) {
1506 root 1.164 $self->decay_objects;
1507 root 1.110 $self->fix_auto_apply;
1508     $self->update_buttons;
1509 root 1.166 Coro::cede;
1510 root 1.110 $self->set_darkness_map;
1511     $self->difficulty ($self->estimate_difficulty)
1512     unless $self->difficulty;
1513 root 1.166 Coro::cede;
1514 root 1.110 $self->activate;
1515 root 1.239 Coro::cede;
1516 root 1.110 }
1517    
1518 root 1.188 $self->post_load;
1519 root 1.239 Coro::cede;
1520 root 1.188
1521 root 1.166 $self->in_memory (cf::MAP_IN_MEMORY);
1522     }
1523    
1524     sub customise_for {
1525     my ($self, $ob) = @_;
1526    
1527     return find "~" . $ob->name . "/" . $self->{path}
1528     if $self->per_player;
1529 root 1.134
1530 root 1.166 $self
1531 root 1.110 }
1532    
1533 root 1.157 # find and load all maps in the 3x3 area around a map
1534     sub load_diag {
1535     my ($map) = @_;
1536    
1537     my @diag; # diagonal neighbours
1538    
1539     for (0 .. 3) {
1540     my $neigh = $map->tile_path ($_)
1541     or next;
1542     $neigh = find $neigh, $map
1543     or next;
1544     $neigh->load;
1545    
1546     push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh],
1547     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1548     }
1549    
1550     for (@diag) {
1551     my $neigh = find @$_
1552     or next;
1553     $neigh->load;
1554     }
1555     }
1556    
1557 root 1.133 sub find_sync {
1558 root 1.110 my ($path, $origin) = @_;
1559    
1560 root 1.157 cf::sync_job { find $path, $origin }
1561 root 1.133 }
1562    
1563     sub do_load_sync {
1564     my ($map) = @_;
1565 root 1.110
1566 root 1.133 cf::sync_job { $map->load };
1567 root 1.110 }
1568    
1569 root 1.157 our %MAP_PREFETCH;
1570 root 1.183 our $MAP_PREFETCHER = undef;
1571 root 1.157
1572     sub find_async {
1573     my ($path, $origin) = @_;
1574    
1575 root 1.166 $path = normalise $path, $origin && $origin->{path};
1576 root 1.157
1577 root 1.166 if (my $map = $cf::MAP{$path}) {
1578 root 1.157 return $map if $map->in_memory == cf::MAP_IN_MEMORY;
1579     }
1580    
1581 root 1.183 undef $MAP_PREFETCH{$path};
1582     $MAP_PREFETCHER ||= cf::async {
1583     while (%MAP_PREFETCH) {
1584     for my $path (keys %MAP_PREFETCH) {
1585     my $map = find $path
1586     or next;
1587     $map->load;
1588    
1589     delete $MAP_PREFETCH{$path};
1590     }
1591     }
1592     undef $MAP_PREFETCHER;
1593     };
1594 root 1.189 $MAP_PREFETCHER->prio (6);
1595 root 1.157
1596     ()
1597     }
1598    
1599 root 1.110 sub save {
1600     my ($self) = @_;
1601    
1602 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1603    
1604 root 1.110 $self->{last_save} = $cf::RUNTIME;
1605    
1606     return unless $self->dirty;
1607    
1608 root 1.166 my $save = $self->save_path; utf8::encode $save;
1609     my $uniq = $self->uniq_path; utf8::encode $uniq;
1610 root 1.117
1611 root 1.110 $self->{load_path} = $save;
1612    
1613     return if $self->{deny_save};
1614    
1615 root 1.132 local $self->{last_access} = $self->last_access;#d#
1616    
1617 root 1.143 cf::async {
1618     $_->contr->save for $self->players;
1619     };
1620    
1621 root 1.110 if ($uniq) {
1622 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1623     $self->_save_objects ($uniq, cf::IO_UNIQUES);
1624 root 1.110 } else {
1625 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1626 root 1.110 }
1627     }
1628    
1629     sub swap_out {
1630     my ($self) = @_;
1631    
1632 root 1.130 # save first because save cedes
1633     $self->save;
1634    
1635 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1636    
1637 root 1.110 return if $self->players;
1638     return if $self->in_memory != cf::MAP_IN_MEMORY;
1639     return if $self->{deny_save};
1640    
1641     $self->clear;
1642     $self->in_memory (cf::MAP_SWAPPED);
1643     }
1644    
1645 root 1.112 sub reset_at {
1646     my ($self) = @_;
1647 root 1.110
1648     # TODO: safety, remove and allow resettable per-player maps
1649 root 1.169 return 1e99 if $self->isa ("ext::map_per_player");#d#
1650 root 1.114 return 1e99 if $self->{deny_reset};
1651 root 1.110
1652 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1653 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1654 root 1.110
1655 root 1.112 $time + $to
1656     }
1657    
1658     sub should_reset {
1659     my ($self) = @_;
1660    
1661     $self->reset_at <= $cf::RUNTIME
1662 root 1.111 }
1663    
1664 root 1.110 sub reset {
1665     my ($self) = @_;
1666    
1667 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
1668 root 1.137
1669 root 1.110 return if $self->players;
1670 root 1.166 return if $self->isa ("ext::map_per_player");#d#
1671 root 1.110
1672     warn "resetting map ", $self->path;#d#
1673    
1674 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
1675    
1676     # need to save uniques path
1677     unless ($self->{deny_save}) {
1678     my $uniq = $self->uniq_path; utf8::encode $uniq;
1679    
1680     $self->_save_objects ($uniq, cf::IO_UNIQUES)
1681     if $uniq;
1682     }
1683    
1684 root 1.111 delete $cf::MAP{$self->path};
1685 root 1.110
1686 root 1.167 $self->clear;
1687    
1688 root 1.110 $_->clear_links_to ($self) for values %cf::MAP;
1689    
1690 root 1.166 $self->unlink_save;
1691 root 1.111 $self->destroy;
1692 root 1.110 }
1693    
1694 root 1.114 my $nuke_counter = "aaaa";
1695    
1696     sub nuke {
1697     my ($self) = @_;
1698    
1699 root 1.174 delete $cf::MAP{$self->path};
1700    
1701     $self->unlink_save;
1702    
1703     bless $self, "cf::map";
1704     delete $self->{deny_reset};
1705 root 1.114 $self->{deny_save} = 1;
1706     $self->reset_timeout (1);
1707 root 1.174 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
1708    
1709     $cf::MAP{$self->path} = $self;
1710    
1711 root 1.114 $self->reset; # polite request, might not happen
1712     }
1713    
1714 root 1.158 =item cf::map::unique_maps
1715    
1716 root 1.166 Returns an arrayref of paths of all shared maps that have
1717 root 1.158 instantiated unique items. May block.
1718    
1719     =cut
1720    
1721     sub unique_maps() {
1722     my $files = aio_readdir cf::localdir . "/" . cf::uniquedir
1723     or return;
1724    
1725     my @paths;
1726    
1727     for (@$files) {
1728     utf8::decode $_;
1729     next if /\.pst$/;
1730     next unless /^$PATH_SEP/o;
1731    
1732 root 1.199 push @paths, cf::map::normalise $_;
1733 root 1.158 }
1734    
1735     \@paths
1736     }
1737    
1738 root 1.155 package cf;
1739    
1740     =back
1741    
1742     =head3 cf::object
1743    
1744     =cut
1745    
1746     package cf::object;
1747    
1748     =over 4
1749    
1750     =item $ob->inv_recursive
1751 root 1.110
1752 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
1753 root 1.110
1754 root 1.155 =cut
1755 root 1.144
1756 root 1.155 sub inv_recursive_;
1757     sub inv_recursive_ {
1758     map { $_, inv_recursive_ $_->inv } @_
1759     }
1760 root 1.110
1761 root 1.155 sub inv_recursive {
1762     inv_recursive_ inv $_[0]
1763 root 1.110 }
1764    
1765     package cf;
1766    
1767     =back
1768    
1769 root 1.95 =head3 cf::object::player
1770    
1771     =over 4
1772    
1773 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
1774 root 1.28
1775     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
1776     can be C<undef>. Does the right thing when the player is currently in a
1777     dialogue with the given NPC character.
1778    
1779     =cut
1780    
1781 root 1.22 # rough implementation of a future "reply" method that works
1782     # with dialog boxes.
1783 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
1784 root 1.23 sub cf::object::player::reply($$$;$) {
1785     my ($self, $npc, $msg, $flags) = @_;
1786    
1787     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1788 root 1.22
1789 root 1.24 if ($self->{record_replies}) {
1790     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
1791     } else {
1792     $msg = $npc->name . " says: $msg" if $npc;
1793     $self->message ($msg, $flags);
1794     }
1795 root 1.22 }
1796    
1797 root 1.79 =item $player_object->may ("access")
1798    
1799     Returns wether the given player is authorized to access resource "access"
1800     (e.g. "command_wizcast").
1801    
1802     =cut
1803    
1804     sub cf::object::player::may {
1805     my ($self, $access) = @_;
1806    
1807     $self->flag (cf::FLAG_WIZ) ||
1808     (ref $cf::CFG{"may_$access"}
1809     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
1810     : $cf::CFG{"may_$access"})
1811     }
1812 root 1.70
1813 root 1.115 =item $player_object->enter_link
1814    
1815     Freezes the player and moves him/her to a special map (C<{link}>).
1816    
1817 root 1.166 The player should be reasonably safe there for short amounts of time. You
1818 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
1819    
1820 root 1.166 Will never block.
1821    
1822 root 1.115 =item $player_object->leave_link ($map, $x, $y)
1823    
1824 root 1.166 Moves the player out of the special C<{link}> map onto the specified
1825     map. If the map is not valid (or omitted), the player will be moved back
1826     to the location he/she was before the call to C<enter_link>, or, if that
1827     fails, to the emergency map position.
1828 root 1.115
1829     Might block.
1830    
1831     =cut
1832    
1833 root 1.166 sub link_map {
1834     unless ($LINK_MAP) {
1835     $LINK_MAP = cf::map::find "{link}"
1836 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
1837 root 1.166 $LINK_MAP->load;
1838     }
1839    
1840     $LINK_MAP
1841     }
1842    
1843 root 1.110 sub cf::object::player::enter_link {
1844     my ($self) = @_;
1845    
1846 root 1.120 $self->deactivate_recursive;
1847    
1848 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
1849 root 1.110
1850 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
1851 root 1.110 if $self->map;
1852    
1853 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
1854 root 1.110 }
1855    
1856     sub cf::object::player::leave_link {
1857     my ($self, $map, $x, $y) = @_;
1858    
1859     my $link_pos = delete $self->{_link_pos};
1860    
1861     unless ($map) {
1862     # restore original map position
1863     ($map, $x, $y) = @{ $link_pos || [] };
1864 root 1.133 $map = cf::map::find $map;
1865 root 1.110
1866     unless ($map) {
1867     ($map, $x, $y) = @$EMERGENCY_POSITION;
1868 root 1.133 $map = cf::map::find $map
1869 root 1.110 or die "FATAL: cannot load emergency map\n";
1870     }
1871     }
1872    
1873     ($x, $y) = (-1, -1)
1874     unless (defined $x) && (defined $y);
1875    
1876     # use -1 or undef as default coordinates, not 0, 0
1877     ($x, $y) = ($map->enter_x, $map->enter_y)
1878     if $x <=0 && $y <= 0;
1879    
1880     $map->load;
1881 root 1.157 $map->load_diag;
1882 root 1.110
1883 root 1.143 return unless $self->contr->active;
1884 root 1.110 $self->activate_recursive;
1885 root 1.215
1886     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
1887 root 1.110 $self->enter_map ($map, $x, $y);
1888     }
1889    
1890 root 1.120 cf::player->attach (
1891     on_logout => sub {
1892     my ($pl) = @_;
1893    
1894     # abort map switching before logout
1895     if ($pl->ob->{_link_pos}) {
1896     cf::sync_job {
1897     $pl->ob->leave_link
1898     };
1899     }
1900     },
1901     on_login => sub {
1902     my ($pl) = @_;
1903    
1904     # try to abort aborted map switching on player login :)
1905     # should happen only on crashes
1906     if ($pl->ob->{_link_pos}) {
1907     $pl->ob->enter_link;
1908 root 1.140 (async {
1909     $pl->ob->reply (undef,
1910     "There was an internal problem at your last logout, "
1911     . "the server will try to bring you to your intended destination in a second.",
1912     cf::NDI_RED);
1913 root 1.215 # we need this sleep as the login has a concurrent enter_exit running
1914     # and this sleep increases chances of the player not ending up in scorn
1915 root 1.120 Coro::Timer::sleep 1;
1916     $pl->ob->leave_link;
1917 root 1.139 })->prio (2);
1918 root 1.120 }
1919     },
1920     );
1921    
1922 root 1.136 =item $player_object->goto ($path, $x, $y)
1923 root 1.110
1924     =cut
1925    
1926 root 1.136 sub cf::object::player::goto {
1927 root 1.110 my ($self, $path, $x, $y) = @_;
1928    
1929     $self->enter_link;
1930    
1931 root 1.140 (async {
1932 root 1.197 my $map = eval {
1933     my $map = cf::map::find $path;
1934     $map = $map->customise_for ($self) if $map;
1935     $map
1936     } or
1937     $self->message ("The exit to '$path' is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1938 root 1.115
1939 root 1.110 $self->leave_link ($map, $x, $y);
1940     })->prio (1);
1941     }
1942    
1943     =item $player_object->enter_exit ($exit_object)
1944    
1945     =cut
1946    
1947     sub parse_random_map_params {
1948     my ($spec) = @_;
1949    
1950     my $rmp = { # defaults
1951 root 1.181 xsize => (cf::rndm 15, 40),
1952     ysize => (cf::rndm 15, 40),
1953     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
1954 root 1.182 #layout => string,
1955 root 1.110 };
1956    
1957     for (split /\n/, $spec) {
1958     my ($k, $v) = split /\s+/, $_, 2;
1959    
1960     $rmp->{lc $k} = $v if (length $k) && (length $v);
1961     }
1962    
1963     $rmp
1964     }
1965    
1966     sub prepare_random_map {
1967     my ($exit) = @_;
1968    
1969 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
1970    
1971 root 1.110 # all this does is basically replace the /! path by
1972     # a new random map path (?random/...) with a seed
1973     # that depends on the exit object
1974    
1975     my $rmp = parse_random_map_params $exit->msg;
1976    
1977     if ($exit->map) {
1978 root 1.198 $rmp->{region} = $exit->region->name;
1979 root 1.110 $rmp->{origin_map} = $exit->map->path;
1980     $rmp->{origin_x} = $exit->x;
1981     $rmp->{origin_y} = $exit->y;
1982     }
1983    
1984     $rmp->{random_seed} ||= $exit->random_seed;
1985    
1986     my $data = cf::to_json $rmp;
1987     my $md5 = Digest::MD5::md5_hex $data;
1988 root 1.177 my $meta = "$cf::RANDOM_MAPS/$md5.meta";
1989 root 1.110
1990 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
1991 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
1992 root 1.177 undef $fh;
1993     aio_rename "$meta~", $meta;
1994 root 1.110
1995     $exit->slaying ("?random/$md5");
1996     $exit->msg (undef);
1997     }
1998     }
1999    
2000     sub cf::object::player::enter_exit {
2001     my ($self, $exit) = @_;
2002    
2003     return unless $self->type == cf::PLAYER;
2004    
2005 root 1.195 if ($exit->slaying eq "/!") {
2006     #TODO: this should de-fi-ni-te-ly not be a sync-job
2007 root 1.233 # the problem is that $exit might not survive long enough
2008     # so it needs to be done right now, right here
2009 root 1.195 cf::sync_job { prepare_random_map $exit };
2010     }
2011    
2012     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2013     my $hp = $exit->stats->hp;
2014     my $sp = $exit->stats->sp;
2015    
2016 root 1.110 $self->enter_link;
2017    
2018 root 1.140 (async {
2019 root 1.133 $self->deactivate_recursive; # just to be sure
2020 root 1.110 unless (eval {
2021 root 1.195 $self->goto ($slaying, $hp, $sp);
2022 root 1.110
2023     1;
2024     }) {
2025     $self->message ("Something went wrong deep within the crossfire server. "
2026 root 1.233 . "I'll try to bring you back to the map you were before. "
2027     . "Please report this to the dungeon master!",
2028     cf::NDI_UNIQUE | cf::NDI_RED);
2029 root 1.110
2030     warn "ERROR in enter_exit: $@";
2031     $self->leave_link;
2032     }
2033     })->prio (1);
2034     }
2035    
2036 root 1.95 =head3 cf::client
2037    
2038     =over 4
2039    
2040     =item $client->send_drawinfo ($text, $flags)
2041    
2042     Sends a drawinfo packet to the client. Circumvents output buffering so
2043     should not be used under normal circumstances.
2044    
2045 root 1.70 =cut
2046    
2047 root 1.95 sub cf::client::send_drawinfo {
2048     my ($self, $text, $flags) = @_;
2049    
2050     utf8::encode $text;
2051 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2052 root 1.95 }
2053    
2054 root 1.232 =item $client->ext_event ($type, %msg)
2055    
2056     Sends an exti event to the client.
2057    
2058     =cut
2059    
2060     sub cf::client::ext_event($$%) {
2061     my ($self, $type, %msg) = @_;
2062    
2063     $msg{msgtype} = "event_$type";
2064     $self->send_packet ("ext " . cf::to_json \%msg);
2065     }
2066 root 1.95
2067     =item $success = $client->query ($flags, "text", \&cb)
2068    
2069     Queues a query to the client, calling the given callback with
2070     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2071     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2072    
2073     Queries can fail, so check the return code. Or don't, as queries will become
2074     reliable at some point in the future.
2075    
2076     =cut
2077    
2078     sub cf::client::query {
2079     my ($self, $flags, $text, $cb) = @_;
2080    
2081     return unless $self->state == ST_PLAYING
2082     || $self->state == ST_SETUP
2083     || $self->state == ST_CUSTOM;
2084    
2085     $self->state (ST_CUSTOM);
2086    
2087     utf8::encode $text;
2088     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2089    
2090     $self->send_packet ($self->{query_queue}[0][0])
2091     if @{ $self->{query_queue} } == 1;
2092     }
2093    
2094     cf::client->attach (
2095     on_reply => sub {
2096     my ($ns, $msg) = @_;
2097    
2098     # this weird shuffling is so that direct followup queries
2099     # get handled first
2100 root 1.128 my $queue = delete $ns->{query_queue}
2101 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2102 root 1.95
2103     (shift @$queue)->[1]->($msg);
2104 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2105 root 1.95
2106     push @{ $ns->{query_queue} }, @$queue;
2107    
2108     if (@{ $ns->{query_queue} } == @$queue) {
2109     if (@$queue) {
2110     $ns->send_packet ($ns->{query_queue}[0][0]);
2111     } else {
2112 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2113 root 1.95 }
2114     }
2115     },
2116     );
2117    
2118 root 1.140 =item $client->async (\&cb)
2119 root 1.96
2120     Create a new coroutine, running the specified callback. The coroutine will
2121     be automatically cancelled when the client gets destroyed (e.g. on logout,
2122     or loss of connection).
2123    
2124     =cut
2125    
2126 root 1.140 sub cf::client::async {
2127 root 1.96 my ($self, $cb) = @_;
2128    
2129 root 1.140 my $coro = &Coro::async ($cb);
2130 root 1.103
2131     $coro->on_destroy (sub {
2132 root 1.96 delete $self->{_coro}{$coro+0};
2133 root 1.103 });
2134 root 1.96
2135     $self->{_coro}{$coro+0} = $coro;
2136 root 1.103
2137     $coro
2138 root 1.96 }
2139    
2140     cf::client->attach (
2141     on_destroy => sub {
2142     my ($ns) = @_;
2143    
2144 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2145 root 1.96 },
2146     );
2147    
2148 root 1.95 =back
2149    
2150 root 1.70
2151     =head2 SAFE SCRIPTING
2152    
2153     Functions that provide a safe environment to compile and execute
2154     snippets of perl code without them endangering the safety of the server
2155     itself. Looping constructs, I/O operators and other built-in functionality
2156     is not available in the safe scripting environment, and the number of
2157 root 1.79 functions and methods that can be called is greatly reduced.
2158 root 1.70
2159     =cut
2160 root 1.23
2161 root 1.42 our $safe = new Safe "safe";
2162 root 1.23 our $safe_hole = new Safe::Hole;
2163    
2164     $SIG{FPE} = 'IGNORE';
2165    
2166     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
2167    
2168 root 1.25 # here we export the classes and methods available to script code
2169    
2170 root 1.70 =pod
2171    
2172 root 1.228 The following functions and methods are available within a safe environment:
2173 root 1.70
2174 elmex 1.91 cf::object contr pay_amount pay_player map
2175 root 1.70 cf::object::player player
2176     cf::player peaceful
2177 elmex 1.91 cf::map trigger
2178 root 1.70
2179     =cut
2180    
2181 root 1.25 for (
2182 elmex 1.91 ["cf::object" => qw(contr pay_amount pay_player map)],
2183 root 1.25 ["cf::object::player" => qw(player)],
2184     ["cf::player" => qw(peaceful)],
2185 elmex 1.91 ["cf::map" => qw(trigger)],
2186 root 1.25 ) {
2187     no strict 'refs';
2188     my ($pkg, @funs) = @$_;
2189 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2190 root 1.25 for @funs;
2191     }
2192 root 1.23
2193 root 1.70 =over 4
2194    
2195     =item @retval = safe_eval $code, [var => value, ...]
2196    
2197     Compiled and executes the given perl code snippet. additional var/value
2198     pairs result in temporary local (my) scalar variables of the given name
2199     that are available in the code snippet. Example:
2200    
2201     my $five = safe_eval '$first + $second', first => 1, second => 4;
2202    
2203     =cut
2204    
2205 root 1.23 sub safe_eval($;@) {
2206     my ($code, %vars) = @_;
2207    
2208     my $qcode = $code;
2209     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2210     $qcode =~ s/\n/\\n/g;
2211    
2212     local $_;
2213 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2214 root 1.23
2215 root 1.42 my $eval =
2216 root 1.23 "do {\n"
2217     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2218     . "#line 0 \"{$qcode}\"\n"
2219     . $code
2220     . "\n}"
2221 root 1.25 ;
2222    
2223     sub_generation_inc;
2224 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2225 root 1.25 sub_generation_inc;
2226    
2227 root 1.42 if ($@) {
2228     warn "$@";
2229     warn "while executing safe code '$code'\n";
2230     warn "with arguments " . (join " ", %vars) . "\n";
2231     }
2232    
2233 root 1.25 wantarray ? @res : $res[0]
2234 root 1.23 }
2235    
2236 root 1.69 =item cf::register_script_function $function => $cb
2237    
2238     Register a function that can be called from within map/npc scripts. The
2239     function should be reasonably secure and should be put into a package name
2240     like the extension.
2241    
2242     Example: register a function that gets called whenever a map script calls
2243     C<rent::overview>, as used by the C<rent> extension.
2244    
2245     cf::register_script_function "rent::overview" => sub {
2246     ...
2247     };
2248    
2249     =cut
2250    
2251 root 1.23 sub register_script_function {
2252     my ($fun, $cb) = @_;
2253    
2254     no strict 'refs';
2255 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2256 root 1.23 }
2257    
2258 root 1.70 =back
2259    
2260 root 1.71 =cut
2261    
2262 root 1.23 #############################################################################
2263 root 1.65
2264     =head2 EXTENSION DATABASE SUPPORT
2265    
2266     Crossfire maintains a very simple database for extension use. It can
2267     currently store anything that can be serialised using Storable, which
2268     excludes objects.
2269    
2270     The parameter C<$family> should best start with the name of the extension
2271     using it, it should be unique.
2272    
2273     =over 4
2274    
2275     =item $value = cf::db_get $family => $key
2276    
2277 root 1.208 Returns a single value from the database.
2278 root 1.65
2279     =item cf::db_put $family => $key => $value
2280    
2281 root 1.208 Stores the given C<$value> in the family.
2282 root 1.65
2283     =cut
2284    
2285 root 1.78 our $DB;
2286    
2287 root 1.210 sub db_init {
2288     unless ($DB) {
2289     $DB = BDB::db_create $DB_ENV;
2290 root 1.65
2291 root 1.210 cf::sync_job {
2292     eval {
2293     $DB->set_flags (BDB::CHKSUM);
2294 root 1.65
2295 root 1.210 BDB::db_open $DB, undef, "db", undef, BDB::BTREE,
2296     BDB::CREATE | BDB::AUTO_COMMIT, 0666;
2297     cf::cleanup "db_open(db): $!" if $!;
2298     };
2299     cf::cleanup "db_open(db): $@" if $@;
2300 root 1.208 };
2301 root 1.65
2302 root 1.210 my $path = cf::localdir . "/database.pst";
2303     if (stat $path) {
2304     cf::sync_job {
2305     my $pst = Storable::retrieve $path;
2306 root 1.209
2307 root 1.210 cf::db_put (board => data => $pst->{board});
2308     cf::db_put (guildrules => data => $pst->{guildrules});
2309     cf::db_put (rent => balance => $pst->{rent}{balance});
2310     BDB::db_env_txn_checkpoint $DB_ENV;
2311 root 1.65
2312 root 1.210 unlink $path;
2313     };
2314     }
2315 root 1.65 }
2316 root 1.208 }
2317 root 1.65
2318 root 1.208 sub db_get($$) {
2319     my $key = "$_[0]/$_[1]";
2320 root 1.65
2321 root 1.208 cf::sync_job {
2322     BDB::db_get $DB, undef, $key, my $data;
2323 root 1.65
2324 root 1.208 $! ? ()
2325     : Compress::LZF::sthaw $data
2326 root 1.65 }
2327 root 1.208 }
2328 root 1.65
2329 root 1.208 sub db_put($$$) {
2330     BDB::dbreq_pri 4;
2331     BDB::db_put $DB, undef, "$_[0]/$_[1]", Compress::LZF::sfreeze_cr $_[2], 0, sub { };
2332 root 1.65 }
2333    
2334 root 1.249 =item cf::cache $id => ...
2335    
2336     Generic caching function that returns the value of the resource $id,
2337     caching and regenerating as required.
2338    
2339     This function can block.
2340    
2341     source => filename returning the data (must be a scalar)
2342     expensive => true == try to cache harder
2343     filter => sub that processes the data into a scalar
2344    
2345     =cut
2346    
2347     sub cache {
2348     my ($id, %arg) = @_;
2349    
2350     aio_stat $arg{source}
2351     and Carp::croak "$arg{source}: $!";
2352    
2353     my $meta = join ":", (stat _)[7,9];
2354     my $md5;
2355    
2356     if ($arg{expensive}) {
2357     0 <= aio_load $arg{source}, my $buf
2358     or Carp::croak "$arg{source}: $!";
2359    
2360     $md5 = Digest::MD5::md5_hex $buf;
2361     }
2362    
2363     my $dbmeta = db_get "$id/meta";
2364     if ($dbmeta ne $meta) {
2365     # changed, we need to process
2366     } else {
2367     # just fetch
2368     }
2369    
2370    
2371     }
2372    
2373 root 1.65 #############################################################################
2374 root 1.203 # the server's init and main functions
2375    
2376 root 1.246 sub load_facedata($) {
2377     my ($path) = @_;
2378 root 1.223
2379 root 1.229 warn "loading facedata from $path\n";
2380 root 1.223
2381 root 1.236 my $facedata;
2382     0 < aio_load $path, $facedata
2383 root 1.223 or die "$path: $!";
2384    
2385 root 1.237 $facedata = Coro::Storable::thaw $facedata;
2386 root 1.223
2387 root 1.236 $facedata->{version} == 2
2388 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
2389    
2390 root 1.236 {
2391     my $faces = $facedata->{faceinfo};
2392    
2393     while (my ($face, $info) = each %$faces) {
2394     my $idx = (cf::face::find $face) || cf::face::alloc $face;
2395     cf::face::set $idx, $info->{visibility}, $info->{magicmap};
2396     cf::face::set_data $idx, 0, $info->{data32}, $info->{chksum32};
2397     cf::face::set_data $idx, 1, $info->{data64}, $info->{chksum64};
2398     Coro::cede;
2399     }
2400    
2401     while (my ($face, $info) = each %$faces) {
2402     next unless $info->{smooth};
2403     my $idx = cf::face::find $face
2404     or next;
2405     if (my $smooth = cf::face::find $info->{smooth}) {
2406     cf::face::set_smooth $idx, $smooth, $info->{smoothlevel};
2407     } else {
2408     warn "smooth face '$info->{smooth}' not found for face '$face'";
2409     }
2410     Coro::cede;
2411     }
2412 root 1.223 }
2413    
2414 root 1.236 {
2415     my $anims = $facedata->{animinfo};
2416    
2417     while (my ($anim, $info) = each %$anims) {
2418     cf::anim::set $anim, $info->{frames}, $info->{facings};
2419     Coro::cede;
2420 root 1.225 }
2421 root 1.236
2422     cf::anim::invalidate_all; # d'oh
2423 root 1.225 }
2424    
2425 root 1.223 1
2426     }
2427    
2428 root 1.246 sub reload_facedata {
2429     load_facedata sprintf "%s/facedata", cf::datadir
2430     or die "unable to load facedata\n";
2431     }
2432    
2433     sub reload_regions {
2434     load_resource_file sprintf "%s/%s/regions", cf::datadir, cf::mapdir
2435     or die "unable to load regions file\n";
2436     }
2437    
2438     sub reload_archetypes {
2439 root 1.248 load_resource_file sprintf "%s/archetypes", cf::datadir
2440 root 1.246 or die "unable to load archetypes\n";
2441 root 1.241 }
2442    
2443 root 1.246 sub reload_treasures {
2444 root 1.247 load_resource_file sprintf "%s/treasures", cf::datadir
2445 root 1.246 or die "unable to load treasurelists\n";
2446 root 1.241 }
2447    
2448 root 1.223 sub reload_resources {
2449 root 1.245 warn "reloading resource files...\n";
2450    
2451 root 1.246 reload_regions;
2452     reload_facedata;
2453     reload_archetypes;
2454     reload_treasures;
2455 root 1.245
2456     warn "finished reloading resource files\n";
2457 root 1.223 }
2458    
2459     sub init {
2460     reload_resources;
2461 root 1.203 }
2462 root 1.34
2463 root 1.73 sub cfg_load {
2464 root 1.72 open my $fh, "<:utf8", cf::confdir . "/config"
2465     or return;
2466    
2467     local $/;
2468     *CFG = YAML::Syck::Load <$fh>;
2469 root 1.131
2470     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
2471    
2472 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
2473     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
2474    
2475 root 1.131 if (exists $CFG{mlockall}) {
2476     eval {
2477 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
2478 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
2479     };
2480     warn $@ if $@;
2481     }
2482 root 1.72 }
2483    
2484 root 1.39 sub main {
2485 root 1.108 # we must not ever block the main coroutine
2486     local $Coro::idle = sub {
2487 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
2488 root 1.175 (async {
2489     Event::one_event;
2490     })->prio (Coro::PRIO_MAX);
2491 root 1.108 };
2492    
2493 root 1.73 cfg_load;
2494 root 1.210 db_init;
2495 root 1.61 load_extensions;
2496 root 1.183
2497     $TICK_WATCHER->start;
2498 root 1.34 Event::loop;
2499     }
2500    
2501     #############################################################################
2502 root 1.155 # initialisation and cleanup
2503    
2504     # install some emergency cleanup handlers
2505     BEGIN {
2506     for my $signal (qw(INT HUP TERM)) {
2507     Event->signal (
2508 root 1.189 reentrant => 0,
2509     data => WF_AUTOCANCEL,
2510     signal => $signal,
2511 root 1.191 prio => 0,
2512 root 1.189 cb => sub {
2513 root 1.155 cf::cleanup "SIG$signal";
2514     },
2515     );
2516     }
2517     }
2518    
2519 root 1.156 sub emergency_save() {
2520 root 1.155 my $freeze_guard = cf::freeze_mainloop;
2521    
2522     warn "enter emergency perl save\n";
2523    
2524     cf::sync_job {
2525     # use a peculiar iteration method to avoid tripping on perl
2526     # refcount bugs in for. also avoids problems with players
2527 root 1.167 # and maps saved/destroyed asynchronously.
2528 root 1.155 warn "begin emergency player save\n";
2529     for my $login (keys %cf::PLAYER) {
2530     my $pl = $cf::PLAYER{$login} or next;
2531     $pl->valid or next;
2532     $pl->save;
2533     }
2534     warn "end emergency player save\n";
2535    
2536     warn "begin emergency map save\n";
2537     for my $path (keys %cf::MAP) {
2538     my $map = $cf::MAP{$path} or next;
2539     $map->valid or next;
2540     $map->save;
2541     }
2542     warn "end emergency map save\n";
2543 root 1.208
2544     warn "begin emergency database checkpoint\n";
2545     BDB::db_env_txn_checkpoint $DB_ENV;
2546     warn "end emergency database checkpoint\n";
2547 root 1.155 };
2548    
2549     warn "leave emergency perl save\n";
2550     }
2551 root 1.22
2552 root 1.211 sub post_cleanup {
2553     my ($make_core) = @_;
2554    
2555     warn Carp::longmess "post_cleanup backtrace"
2556     if $make_core;
2557     }
2558    
2559 root 1.246 sub do_reload_perl() {
2560 root 1.106 # can/must only be called in main
2561     if ($Coro::current != $Coro::main) {
2562 root 1.183 warn "can only reload from main coroutine";
2563 root 1.106 return;
2564     }
2565    
2566 root 1.103 warn "reloading...";
2567    
2568 root 1.212 warn "entering sync_job";
2569    
2570 root 1.213 cf::sync_job {
2571 root 1.214 cf::write_runtime; # external watchdog should not bark
2572 root 1.212 cf::emergency_save;
2573 root 1.214 cf::write_runtime; # external watchdog should not bark
2574 root 1.183
2575 root 1.212 warn "syncing database to disk";
2576     BDB::db_env_txn_checkpoint $DB_ENV;
2577 root 1.106
2578     # if anything goes wrong in here, we should simply crash as we already saved
2579 root 1.65
2580 root 1.183 warn "cancelling all WF_AUTOCANCEL watchers";
2581 root 1.87 for (Event::all_watchers) {
2582     $_->cancel if $_->data & WF_AUTOCANCEL;
2583     }
2584 root 1.65
2585 root 1.183 warn "flushing outstanding aio requests";
2586     for (;;) {
2587 root 1.208 BDB::flush;
2588 root 1.183 IO::AIO::flush;
2589     Coro::cede;
2590 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
2591 root 1.183 warn "iterate...";
2592     }
2593    
2594 root 1.223 ++$RELOAD;
2595    
2596 root 1.183 warn "cancelling all extension coros";
2597 root 1.103 $_->cancel for values %EXT_CORO;
2598     %EXT_CORO = ();
2599    
2600 root 1.183 warn "removing commands";
2601 root 1.159 %COMMAND = ();
2602    
2603 root 1.183 warn "removing ext commands";
2604 root 1.159 %EXTCMD = ();
2605    
2606 root 1.183 warn "unloading/nuking all extensions";
2607 root 1.159 for my $pkg (@EXTS) {
2608 root 1.160 warn "... unloading $pkg";
2609 root 1.159
2610     if (my $cb = $pkg->can ("unload")) {
2611     eval {
2612     $cb->($pkg);
2613     1
2614     } or warn "$pkg unloaded, but with errors: $@";
2615     }
2616    
2617 root 1.160 warn "... nuking $pkg";
2618 root 1.159 Symbol::delete_package $pkg;
2619 root 1.65 }
2620    
2621 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
2622 root 1.65 while (my ($k, $v) = each %INC) {
2623     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
2624    
2625 root 1.183 warn "... unloading $k";
2626 root 1.65 delete $INC{$k};
2627    
2628     $k =~ s/\.pm$//;
2629     $k =~ s/\//::/g;
2630    
2631     if (my $cb = $k->can ("unload_module")) {
2632     $cb->();
2633     }
2634    
2635     Symbol::delete_package $k;
2636     }
2637    
2638 root 1.183 warn "getting rid of safe::, as good as possible";
2639 root 1.65 Symbol::delete_package "safe::$_"
2640 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
2641 root 1.65
2642 root 1.183 warn "unloading cf.pm \"a bit\"";
2643 root 1.65 delete $INC{"cf.pm"};
2644    
2645     # don't, removes xs symbols, too,
2646     # and global variables created in xs
2647     #Symbol::delete_package __PACKAGE__;
2648    
2649 root 1.183 warn "unload completed, starting to reload now";
2650    
2651 root 1.103 warn "reloading cf.pm";
2652 root 1.65 require cf;
2653 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
2654    
2655 root 1.183 warn "loading config and database again";
2656 root 1.73 cf::cfg_load;
2657 root 1.65
2658 root 1.183 warn "loading extensions";
2659 root 1.65 cf::load_extensions;
2660    
2661 root 1.183 warn "reattaching attachments to objects/players";
2662 root 1.222 _global_reattach; # objects, sockets
2663 root 1.183 warn "reattaching attachments to maps";
2664 root 1.144 reattach $_ for values %MAP;
2665 root 1.222 warn "reattaching attachments to players";
2666     reattach $_ for values %PLAYER;
2667 root 1.183
2668 root 1.212 warn "leaving sync_job";
2669 root 1.183
2670 root 1.212 1
2671     } or do {
2672 root 1.106 warn $@;
2673     warn "error while reloading, exiting.";
2674     exit 1;
2675 root 1.212 };
2676 root 1.106
2677 root 1.159 warn "reloaded";
2678 root 1.65 };
2679    
2680 root 1.175 our $RELOAD_WATCHER; # used only during reload
2681    
2682 root 1.246 sub reload_perl() {
2683     # doing reload synchronously and two reloads happen back-to-back,
2684     # coro crashes during coro_state_free->destroy here.
2685    
2686     $RELOAD_WATCHER ||= Event->timer (
2687     reentrant => 0,
2688     after => 0,
2689     data => WF_AUTOCANCEL,
2690     cb => sub {
2691     do_reload_perl;
2692     undef $RELOAD_WATCHER;
2693     },
2694     );
2695     }
2696    
2697 root 1.111 register_command "reload" => sub {
2698 root 1.65 my ($who, $arg) = @_;
2699    
2700     if ($who->flag (FLAG_WIZ)) {
2701 root 1.175 $who->message ("reloading server.");
2702 root 1.246 async { reload_perl };
2703 root 1.65 }
2704     };
2705    
2706 root 1.27 unshift @INC, $LIBDIR;
2707 root 1.17
2708 root 1.183 my $bug_warning = 0;
2709    
2710 root 1.239 our @WAIT_FOR_TICK;
2711     our @WAIT_FOR_TICK_BEGIN;
2712    
2713     sub wait_for_tick {
2714 root 1.240 return unless $TICK_WATCHER->is_active;
2715 root 1.241 return if $Coro::current == $Coro::main;
2716    
2717 root 1.239 my $signal = new Coro::Signal;
2718     push @WAIT_FOR_TICK, $signal;
2719     $signal->wait;
2720     }
2721    
2722     sub wait_for_tick_begin {
2723 root 1.240 return unless $TICK_WATCHER->is_active;
2724 root 1.241 return if $Coro::current == $Coro::main;
2725    
2726 root 1.239 my $signal = new Coro::Signal;
2727     push @WAIT_FOR_TICK_BEGIN, $signal;
2728     $signal->wait;
2729     }
2730    
2731 root 1.35 $TICK_WATCHER = Event->timer (
2732 root 1.104 reentrant => 0,
2733 root 1.183 parked => 1,
2734 root 1.191 prio => 0,
2735 root 1.104 at => $NEXT_TICK || $TICK,
2736     data => WF_AUTOCANCEL,
2737     cb => sub {
2738 root 1.183 if ($Coro::current != $Coro::main) {
2739     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
2740     unless ++$bug_warning > 10;
2741     return;
2742     }
2743    
2744 root 1.163 $NOW = Event::time;
2745    
2746 root 1.133 cf::server_tick; # one server iteration
2747 root 1.245
2748 root 1.133 $RUNTIME += $TICK;
2749 root 1.35 $NEXT_TICK += $TICK;
2750    
2751 root 1.214 if ($NOW >= $NEXT_RUNTIME_WRITE) {
2752     $NEXT_RUNTIME_WRITE = $NOW + 10;
2753     Coro::async_pool {
2754     write_runtime
2755     or warn "ERROR: unable to write runtime file: $!";
2756     };
2757     }
2758    
2759 root 1.191 # my $AFTER = Event::time;
2760     # warn $AFTER - $NOW;#d#
2761 root 1.190
2762 root 1.78 # if we are delayed by four ticks or more, skip them all
2763 root 1.103 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4;
2764 root 1.35
2765     $TICK_WATCHER->at ($NEXT_TICK);
2766     $TICK_WATCHER->start;
2767 root 1.245
2768     if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
2769     $sig->send;
2770     }
2771     while (my $sig = shift @WAIT_FOR_TICK) {
2772     $sig->send;
2773     }
2774    
2775     _post_tick;
2776 root 1.35 },
2777     );
2778    
2779 root 1.206 {
2780     BDB::max_poll_time $TICK * 0.1;
2781     $BDB_POLL_WATCHER = Event->io (
2782     reentrant => 0,
2783     fd => BDB::poll_fileno,
2784     poll => 'r',
2785     prio => 0,
2786     data => WF_AUTOCANCEL,
2787     cb => \&BDB::poll_cb,
2788     );
2789     BDB::min_parallel 8;
2790    
2791     BDB::set_sync_prepare {
2792     my $status;
2793     my $current = $Coro::current;
2794     (
2795     sub {
2796     $status = $!;
2797     $current->ready; undef $current;
2798     },
2799     sub {
2800     Coro::schedule while defined $current;
2801     $! = $status;
2802     },
2803     )
2804     };
2805 root 1.77
2806 root 1.206 unless ($DB_ENV) {
2807     $DB_ENV = BDB::db_env_create;
2808    
2809     cf::sync_job {
2810 root 1.208 eval {
2811     BDB::db_env_open
2812     $DB_ENV,
2813     $BDB_ENV_DIR,
2814     BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
2815     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
2816     0666;
2817    
2818     cf::cleanup "db_env_open($BDB_ENV_DIR): $!" if $!;
2819    
2820     $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC, 1);
2821     $DB_ENV->set_lk_detect;
2822     };
2823    
2824     cf::cleanup "db_env_open(db): $@" if $@;
2825 root 1.206 };
2826     }
2827     }
2828    
2829     {
2830     IO::AIO::min_parallel 8;
2831    
2832     undef $Coro::AIO::WATCHER;
2833     IO::AIO::max_poll_time $TICK * 0.1;
2834     $AIO_POLL_WATCHER = Event->io (
2835     reentrant => 0,
2836 root 1.214 data => WF_AUTOCANCEL,
2837 root 1.206 fd => IO::AIO::poll_fileno,
2838     poll => 'r',
2839     prio => 6,
2840     cb => \&IO::AIO::poll_cb,
2841     );
2842     }
2843 root 1.108
2844 root 1.249 # load additional modules
2845     use cf::pod;
2846    
2847 root 1.125 END { cf::emergency_save }
2848    
2849 root 1.1 1
2850