ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.130
Committed: Thu Jan 4 00:08:08 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.129: +3 -1 lines
Log Message:
- save before testing the map on swap_out, as save cedes and players
  might hop on the map while the map saves, changing its status.

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.23 use Opcode;
10     use Safe;
11     use Safe::Hole;
12 root 1.19
13 root 1.127 use Coro 3.3 ();
14 root 1.96 use Coro::Event;
15     use Coro::Timer;
16     use Coro::Signal;
17     use Coro::Semaphore;
18 root 1.105 use Coro::AIO;
19 root 1.96
20 root 1.108 use Digest::MD5;
21 root 1.105 use Fcntl;
22     use IO::AIO 2.31 ();
23 root 1.72 use YAML::Syck ();
24 root 1.32 use Time::HiRes;
25 root 1.96
26     use Event; $Event::Eval = 1; # no idea why this is required, but it is
27 root 1.1
28 root 1.72 # work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
29     $YAML::Syck::ImplicitUnicode = 1;
30    
31 root 1.103 $Coro::main->prio (2); # run main coroutine ("the server") with very high priority
32 root 1.1
33 root 1.87 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
34    
35 root 1.85 our %COMMAND = ();
36     our %COMMAND_TIME = ();
37     our %EXTCMD = ();
38    
39 root 1.1 our @EVENT;
40 root 1.88 our $LIBDIR = datadir . "/ext";
41 root 1.1
42 root 1.35 our $TICK = MAX_TIME * 1e-6;
43     our $TICK_WATCHER;
44     our $NEXT_TICK;
45 root 1.103 our $NOW;
46 root 1.35
47 root 1.70 our %CFG;
48    
49 root 1.84 our $UPTIME; $UPTIME ||= time;
50 root 1.103 our $RUNTIME;
51    
52     our %MAP; # all maps
53     our $LINK_MAP; # the special {link} map
54     our $FREEZE;
55 root 1.108 our $RANDOM_MAPS = cf::localdir . "/random";
56     our %EXT_CORO;
57 root 1.103
58     binmode STDOUT;
59     binmode STDERR;
60    
61     # read virtual server time, if available
62     unless ($RUNTIME || !-e cf::localdir . "/runtime") {
63     open my $fh, "<", cf::localdir . "/runtime"
64     or die "unable to read runtime file: $!";
65     $RUNTIME = <$fh> + 0.;
66     }
67    
68     mkdir cf::localdir;
69     mkdir cf::localdir . "/" . cf::playerdir;
70     mkdir cf::localdir . "/" . cf::tmpdir;
71     mkdir cf::localdir . "/" . cf::uniquedir;
72 root 1.108 mkdir $RANDOM_MAPS;
73 root 1.103
74 root 1.108 # a special map that is always available
75     our $LINK_MAP;
76 root 1.76
77 root 1.110 our $EMERGENCY_POSITION = $cf::CFG{emergency_position} || ["/world/world_105_115", 5, 37];
78    
79 root 1.70 #############################################################################
80    
81     =head2 GLOBAL VARIABLES
82    
83     =over 4
84    
85 root 1.83 =item $cf::UPTIME
86    
87     The timestamp of the server start (so not actually an uptime).
88    
89 root 1.103 =item $cf::RUNTIME
90    
91     The time this server has run, starts at 0 and is increased by $cf::TICK on
92     every server tick.
93    
94 root 1.70 =item $cf::LIBDIR
95    
96     The perl library directory, where extensions and cf-specific modules can
97     be found. It will be added to C<@INC> automatically.
98    
99 root 1.103 =item $cf::NOW
100    
101     The time of the last (current) server tick.
102    
103 root 1.70 =item $cf::TICK
104    
105     The interval between server ticks, in seconds.
106    
107     =item %cf::CFG
108    
109     Configuration for the server, loaded from C</etc/crossfire/config>, or
110     from wherever your confdir points to.
111    
112     =back
113    
114     =cut
115    
116 root 1.1 BEGIN {
117     *CORE::GLOBAL::warn = sub {
118     my $msg = join "", @_;
119 root 1.103 utf8::encode $msg;
120    
121 root 1.1 $msg .= "\n"
122     unless $msg =~ /\n$/;
123    
124     LOG llevError, "cfperl: $msg";
125     };
126     }
127    
128 root 1.93 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
129     @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
130     @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
131     @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
132     @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
133 root 1.41 @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
134 root 1.25
135 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
136 root 1.25 # within the Safe compartment.
137 root 1.86 for my $pkg (qw(
138 root 1.100 cf::global cf::attachable
139 root 1.86 cf::object cf::object::player
140 root 1.89 cf::client cf::player
141 root 1.86 cf::arch cf::living
142     cf::map cf::party cf::region
143     )) {
144 root 1.25 no strict 'refs';
145 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
146 root 1.25 }
147 root 1.1
148 root 1.18 $Event::DIED = sub {
149     warn "error in event callback: @_";
150     };
151    
152 root 1.5 my %ext_pkg;
153 root 1.1 my @exts;
154     my @hook;
155    
156 root 1.70 =head2 UTILITY FUNCTIONS
157    
158     =over 4
159    
160     =cut
161 root 1.44
162 root 1.45 use JSON::Syck (); # TODO# replace by JSON::PC once working
163 root 1.44
164 root 1.70 =item $ref = cf::from_json $json
165    
166     Converts a JSON string into the corresponding perl data structure.
167    
168     =cut
169    
170 root 1.45 sub from_json($) {
171     $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
172     JSON::Syck::Load $_[0]
173 root 1.44 }
174    
175 root 1.70 =item $json = cf::to_json $ref
176    
177     Converts a perl data structure into its JSON representation.
178    
179     =cut
180    
181 root 1.45 sub to_json($) {
182     $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
183     JSON::Syck::Dump $_[0]
184 root 1.44 }
185    
186 root 1.120 =item my $guard = cf::guard { BLOCK }
187    
188     Run the given callback when the guard object gets destroyed (useful for
189     coroutine cancellations).
190    
191     You can call C<< ->cancel >> on the guard object to stop the block from
192     being executed.
193    
194     =cut
195    
196     sub guard(&) {
197     bless \(my $cb = $_[0]), cf::guard::;
198     }
199    
200     sub cf::guard::cancel {
201     ${$_[0]} = sub { };
202     }
203    
204     sub cf::guard::DESTROY {
205     ${$_[0]}->();
206     }
207    
208     =item cf::lock_wait $string
209    
210     Wait until the given lock is available. See cf::lock_acquire.
211    
212     =item my $lock = cf::lock_acquire $string
213    
214     Wait until the given lock is available and then acquires it and returns
215     a guard object. If the guard object gets destroyed (goes out of scope,
216     for example when the coroutine gets canceled), the lock is automatically
217     returned.
218    
219     Lock names should begin with a unique identifier (for example, find_map
220     uses map_find and load_map uses map_load).
221    
222     =cut
223    
224     our %LOCK;
225    
226     sub lock_wait($) {
227     my ($key) = @_;
228    
229     # wait for lock, if any
230     while ($LOCK{$key}) {
231     push @{ $LOCK{$key} }, $Coro::current;
232     Coro::schedule;
233     }
234     }
235    
236     sub lock_acquire($) {
237     my ($key) = @_;
238    
239     # wait, to be sure we are not locked
240     lock_wait $key;
241    
242     $LOCK{$key} = [];
243    
244     cf::guard {
245     # wake up all waiters, to be on the safe side
246     $_->ready for @{ delete $LOCK{$key} };
247     }
248     }
249    
250 root 1.127 =item cf::async { BLOCK }
251    
252     Like C<Coro::async>, but runs the given BLOCK in an eval and only logs the
253     error instead of exiting the server in case of a problem.
254    
255     =cut
256    
257     sub async(&) {
258     my ($cb) = @_;
259    
260     Coro::async {
261     eval { $cb->() };
262     warn $@ if $@;
263     }
264     }
265    
266 root 1.106 =item cf::sync_job { BLOCK }
267    
268     The design of crossfire+ requires that the main coro ($Coro::main) is
269     always able to handle events or runnable, as crossfire+ is only partly
270     reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable.
271    
272     If it must be done, put the blocking parts into C<sync_job>. This will run
273     the given BLOCK in another coroutine while waiting for the result. The
274     server will be frozen during this time, so the block should either finish
275     fast or be very important.
276    
277     =cut
278    
279 root 1.105 sub sync_job(&) {
280     my ($job) = @_;
281    
282     if ($Coro::current == $Coro::main) {
283 root 1.112 # this is the main coro, too bad, we have to block
284     # till the operation succeeds, freezing the server :/
285    
286 root 1.110 # TODO: use suspend/resume instead
287 root 1.112 # (but this is cancel-safe)
288 root 1.110 local $FREEZE = 1;
289 root 1.112
290     my $busy = 1;
291     my @res;
292    
293     (Coro::async {
294     @res = eval { $job->() };
295     warn $@ if $@;
296     undef $busy;
297     })->prio (Coro::PRIO_MAX);
298    
299 root 1.105 while ($busy) {
300     Coro::cede_notself;
301     Event::one_event unless Coro::nready;
302     }
303 root 1.112
304     wantarray ? @res : $res[0]
305 root 1.105 } else {
306 root 1.112 # we are in another coroutine, how wonderful, everything just works
307    
308     $job->()
309 root 1.105 }
310     }
311    
312 root 1.103 =item $coro = cf::coro { BLOCK }
313    
314     Creates and returns a new coro. This coro is automcatially being canceled
315     when the extension calling this is being unloaded.
316    
317     =cut
318    
319     sub coro(&) {
320     my $cb = shift;
321    
322 root 1.127 my $coro = &cf::async ($cb);
323 root 1.103
324     $coro->on_destroy (sub {
325     delete $EXT_CORO{$coro+0};
326     });
327     $EXT_CORO{$coro+0} = $coro;
328    
329     $coro
330     }
331    
332 root 1.108 sub write_runtime {
333     my $runtime = cf::localdir . "/runtime";
334    
335     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
336     or return;
337    
338 root 1.112 my $value = $cf::RUNTIME + 1 + 10; # 10 is the runtime save interval, for a monotonic clock
339 root 1.108 (aio_write $fh, 0, (length $value), $value, 0) <= 0
340     and return;
341    
342     aio_fsync $fh
343     and return;
344    
345     close $fh
346     or return;
347    
348     aio_rename "$runtime~", $runtime
349     and return;
350    
351     1
352     }
353    
354 root 1.70 =back
355    
356 root 1.71 =cut
357    
358 root 1.44 #############################################################################
359 root 1.39
360 root 1.108 package cf::path;
361    
362     sub new {
363     my ($class, $path, $base) = @_;
364    
365 root 1.110 $path = $path->as_string if ref $path;
366    
367 root 1.108 my $self = bless { }, $class;
368    
369 root 1.114 # {... are special paths that are not touched
370     # ?xxx/... are special absolute paths
371     # ?random/... random maps
372     # /! non-realised random map exit
373     # /... normal maps
374     # ~/... per-player maps without a specific player (DO NOT USE)
375     # ~user/... per-player map of a specific user
376    
377     if ($path =~ /^{/) {
378     # fine as it is
379     } elsif ($path =~ s{^\?random/}{}) {
380 root 1.109 Coro::AIO::aio_load "$cf::RANDOM_MAPS/$path.meta", my $data;
381     $self->{random} = cf::from_json $data;
382 root 1.108 } else {
383     if ($path =~ s{^~([^/]+)?}{}) {
384     $self->{user_rel} = 1;
385    
386     if (defined $1) {
387     $self->{user} = $1;
388     } elsif ($base =~ m{^~([^/]+)/}) {
389     $self->{user} = $1;
390     } else {
391     warn "cannot resolve user-relative path without user <$path,$base>\n";
392     }
393     } elsif ($path =~ /^\//) {
394     # already absolute
395     } else {
396     $base =~ s{[^/]+/?$}{};
397     return $class->new ("$base/$path");
398     }
399    
400     for ($path) {
401     redo if s{/\.?/}{/};
402     redo if s{/[^/]+/\.\./}{/};
403     }
404     }
405    
406     $self->{path} = $path;
407    
408     $self
409     }
410    
411     # the name / primary key / in-game path
412     sub as_string {
413     my ($self) = @_;
414    
415     $self->{user_rel} ? "~$self->{user}$self->{path}"
416     : $self->{random} ? "?random/$self->{path}"
417     : $self->{path}
418     }
419    
420     # the displayed name, this is a one way mapping
421     sub visible_name {
422     my ($self) = @_;
423    
424 root 1.109 # if (my $rmp = $self->{random}) {
425     # # todo: be more intelligent about this
426     # "?random/$rmp->{origin_map}+$rmp->{origin_x}+$rmp->{origin_y}/$rmp->{dungeon_level}"
427     # } else {
428     $self->as_string
429     # }
430 root 1.108 }
431    
432     # escape the /'s in the path
433     sub _escaped_path {
434     # ∕ is U+2215
435     (my $path = $_[0]{path}) =~ s/\//∕/g;
436     $path
437     }
438    
439     # the original (read-only) location
440     sub load_path {
441     my ($self) = @_;
442    
443     sprintf "%s/%s/%s", cf::datadir, cf::mapdir, $self->{path}
444     }
445    
446     # the temporary/swap location
447     sub save_path {
448     my ($self) = @_;
449    
450     $self->{user_rel} ? sprintf "%s/%s/%s/%s", cf::localdir, cf::playerdir, $self->{user}, $self->_escaped_path
451 root 1.109 : $self->{random} ? sprintf "%s/%s", $RANDOM_MAPS, $self->{path}
452 root 1.108 : sprintf "%s/%s/%s", cf::localdir, cf::tmpdir, $self->_escaped_path
453     }
454    
455     # the unique path, might be eq to save_path
456     sub uniq_path {
457     my ($self) = @_;
458    
459     $self->{user_rel} || $self->{random}
460     ? undef
461     : sprintf "%s/%s/%s", cf::localdir, cf::uniquedir, $self->_escaped_path
462     }
463    
464     # return random map parameters, or undef
465     sub random_map_params {
466     my ($self) = @_;
467    
468     $self->{random}
469     }
470    
471     # this is somewhat ugly, but style maps do need special treatment
472     sub is_style_map {
473     $_[0]{path} =~ m{^/styles/}
474     }
475    
476     package cf;
477    
478     #############################################################################
479    
480 root 1.93 =head2 ATTACHABLE OBJECTS
481    
482 root 1.94 Many objects in crossfire are so-called attachable objects. That means you can
483     attach callbacks/event handlers (a collection of which is called an "attachment")
484     to it. All such attachable objects support the following methods.
485    
486     In the following description, CLASS can be any of C<global>, C<object>
487     C<player>, C<client> or C<map> (i.e. the attachable objects in
488     crossfire+).
489 root 1.55
490     =over 4
491    
492 root 1.94 =item $attachable->attach ($attachment, key => $value...)
493    
494     =item $attachable->detach ($attachment)
495    
496     Attach/detach a pre-registered attachment to a specific object and give it
497     the specified key/value pairs as arguments.
498    
499     Example, attach a minesweeper attachment to the given object, making it a
500     10x10 minesweeper game:
501 root 1.46
502 root 1.94 $obj->attach (minesweeper => width => 10, height => 10);
503 root 1.53
504 root 1.93 =item $bool = $attachable->attached ($name)
505 root 1.46
506 root 1.93 Checks wether the named attachment is currently attached to the object.
507 root 1.46
508 root 1.94 =item cf::CLASS->attach ...
509 root 1.46
510 root 1.94 =item cf::CLASS->detach ...
511 root 1.92
512 root 1.94 Define an anonymous attachment and attach it to all objects of the given
513     CLASS. See the next function for an explanation of its arguments.
514 root 1.92
515 root 1.93 You can attach to global events by using the C<cf::global> class.
516 root 1.92
517 root 1.94 Example, log all player logins:
518    
519     cf::player->attach (
520     on_login => sub {
521     my ($pl) = @_;
522     ...
523     },
524     );
525    
526     Example, attach to the jeweler skill:
527    
528     cf::object->attach (
529     type => cf::SKILL,
530     subtype => cf::SK_JEWELER,
531     on_use_skill => sub {
532     my ($sk, $ob, $part, $dir, $msg) = @_;
533     ...
534     },
535     );
536    
537     =item cf::CLASS::attachment $name, ...
538    
539     Register an attachment by C<$name> through which attachable objects of the
540     given CLASS can refer to this attachment.
541    
542     Some classes such as crossfire maps and objects can specify attachments
543     that are attached at load/instantiate time, thus the need for a name.
544    
545     These calls expect any number of the following handler/hook descriptions:
546 root 1.46
547     =over 4
548    
549     =item prio => $number
550    
551     Set the priority for all following handlers/hooks (unless overwritten
552     by another C<prio> setting). Lower priority handlers get executed
553     earlier. The default priority is C<0>, and many built-in handlers are
554     registered at priority C<-1000>, so lower priorities should not be used
555     unless you know what you are doing.
556    
557 root 1.93 =item type => $type
558    
559     (Only for C<< cf::object->attach >> calls), limits the attachment to the
560     given type of objects only (the additional parameter C<subtype> can be
561     used to further limit to the given subtype).
562    
563 root 1.46 =item on_I<event> => \&cb
564    
565     Call the given code reference whenever the named event happens (event is
566     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
567     handlers are recognised generally depends on the type of object these
568     handlers attach to).
569    
570     See F<include/eventinc.h> for the full list of events supported, and their
571     class.
572    
573     =item package => package::
574    
575     Look for sub functions of the name C<< on_I<event> >> in the given
576     package and register them. Only handlers for eevents supported by the
577     object/class are recognised.
578    
579     =back
580    
581 root 1.94 Example, define an attachment called "sockpuppet" that calls the given
582     event handler when a monster attacks:
583    
584     cf::object::attachment sockpuppet =>
585     on_skill_attack => sub {
586     my ($self, $victim) = @_;
587     ...
588     }
589     }
590    
591 root 1.96 =item $attachable->valid
592    
593     Just because you have a perl object does not mean that the corresponding
594     C-level object still exists. If you try to access an object that has no
595     valid C counterpart anymore you get an exception at runtime. This method
596     can be used to test for existence of the C object part without causing an
597     exception.
598    
599 root 1.39 =cut
600    
601 root 1.40 # the following variables are defined in .xs and must not be re-created
602 root 1.100 our @CB_GLOBAL = (); # registry for all global events
603     our @CB_ATTACHABLE = (); # registry for all attachables
604     our @CB_OBJECT = (); # all objects (should not be used except in emergency)
605     our @CB_PLAYER = ();
606     our @CB_CLIENT = ();
607     our @CB_TYPE = (); # registry for type (cf-object class) based events
608     our @CB_MAP = ();
609 root 1.39
610 root 1.45 my %attachment;
611    
612 root 1.93 sub _attach_cb($$$$) {
613     my ($registry, $event, $prio, $cb) = @_;
614 root 1.39
615     use sort 'stable';
616    
617     $cb = [$prio, $cb];
618    
619     @{$registry->[$event]} = sort
620     { $a->[0] cmp $b->[0] }
621     @{$registry->[$event] || []}, $cb;
622     }
623    
624 root 1.100 # hack
625     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
626    
627 root 1.39 # attach handles attaching event callbacks
628     # the only thing the caller has to do is pass the correct
629     # registry (== where the callback attaches to).
630 root 1.93 sub _attach {
631 root 1.45 my ($registry, $klass, @arg) = @_;
632 root 1.39
633 root 1.93 my $object_type;
634 root 1.39 my $prio = 0;
635     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
636    
637 root 1.100 #TODO: get rid of this hack
638     if ($attachable_klass{$klass}) {
639     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
640     }
641    
642 root 1.45 while (@arg) {
643     my $type = shift @arg;
644 root 1.39
645     if ($type eq "prio") {
646 root 1.45 $prio = shift @arg;
647 root 1.39
648 root 1.93 } elsif ($type eq "type") {
649     $object_type = shift @arg;
650     $registry = $CB_TYPE[$object_type] ||= [];
651    
652     } elsif ($type eq "subtype") {
653     defined $object_type or Carp::croak "subtype specified without type";
654     my $object_subtype = shift @arg;
655     $registry = $CB_TYPE[$object_type + $object_subtype * NUM_SUBTYPES] ||= [];
656    
657 root 1.39 } elsif ($type eq "package") {
658 root 1.45 my $pkg = shift @arg;
659 root 1.39
660     while (my ($name, $id) = each %cb_id) {
661     if (my $cb = $pkg->can ($name)) {
662 root 1.93 _attach_cb $registry, $id, $prio, $cb;
663 root 1.39 }
664     }
665    
666     } elsif (exists $cb_id{$type}) {
667 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
668 root 1.39
669     } elsif (ref $type) {
670     warn "attaching objects not supported, ignoring.\n";
671    
672     } else {
673 root 1.45 shift @arg;
674 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
675     }
676     }
677     }
678    
679 root 1.93 sub _object_attach {
680 root 1.48 my ($obj, $name, %arg) = @_;
681 root 1.46
682 root 1.55 return if exists $obj->{_attachment}{$name};
683    
684 root 1.46 if (my $attach = $attachment{$name}) {
685     my $registry = $obj->registry;
686    
687 root 1.47 for (@$attach) {
688     my ($klass, @attach) = @$_;
689 root 1.93 _attach $registry, $klass, @attach;
690 root 1.47 }
691 root 1.46
692 root 1.48 $obj->{$name} = \%arg;
693 root 1.46 } else {
694     warn "object uses attachment '$name' that is not available, postponing.\n";
695     }
696    
697 root 1.50 $obj->{_attachment}{$name} = undef;
698 root 1.46 }
699    
700 root 1.93 sub cf::attachable::attach {
701     if (ref $_[0]) {
702     _object_attach @_;
703     } else {
704     _attach shift->_attach_registry, @_;
705     }
706 root 1.55 };
707 root 1.46
708 root 1.54 # all those should be optimised
709 root 1.93 sub cf::attachable::detach {
710 root 1.54 my ($obj, $name) = @_;
711 root 1.46
712 root 1.93 if (ref $obj) {
713     delete $obj->{_attachment}{$name};
714     reattach ($obj);
715     } else {
716     Carp::croak "cannot, currently, detach class attachments";
717     }
718 root 1.55 };
719    
720 root 1.93 sub cf::attachable::attached {
721 root 1.55 my ($obj, $name) = @_;
722    
723     exists $obj->{_attachment}{$name}
724 root 1.39 }
725    
726 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
727 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
728     sub cf::\L$klass\E::_attach_registry {
729     (\\\@CB_$klass, KLASS_$klass)
730     }
731 root 1.45
732 root 1.93 sub cf::\L$klass\E::attachment {
733     my \$name = shift;
734 root 1.39
735 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
736     }
737     ";
738     die if $@;
739 root 1.52 }
740    
741 root 1.39 our $override;
742 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
743 root 1.39
744 root 1.45 sub override {
745     $override = 1;
746     @invoke_results = ();
747 root 1.39 }
748    
749 root 1.45 sub do_invoke {
750 root 1.39 my $event = shift;
751 root 1.40 my $callbacks = shift;
752 root 1.39
753 root 1.45 @invoke_results = ();
754    
755 root 1.39 local $override;
756    
757 root 1.40 for (@$callbacks) {
758 root 1.39 eval { &{$_->[1]} };
759    
760     if ($@) {
761     warn "$@";
762 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
763 root 1.39 override;
764     }
765    
766     return 1 if $override;
767     }
768    
769     0
770     }
771    
772 root 1.96 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
773 root 1.55
774 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
775 root 1.55
776 root 1.96 Generate an object-specific event with the given arguments.
777 root 1.55
778 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
779 root 1.55 removed in future versions), and there is no public API to access override
780     results (if you must, access C<@cf::invoke_results> directly).
781    
782     =back
783    
784 root 1.71 =cut
785    
786 root 1.70 #############################################################################
787 root 1.45 # object support
788    
789 root 1.102 sub reattach {
790     # basically do the same as instantiate, without calling instantiate
791     my ($obj) = @_;
792    
793     my $registry = $obj->registry;
794    
795     @$registry = ();
796    
797     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
798    
799     for my $name (keys %{ $obj->{_attachment} || {} }) {
800     if (my $attach = $attachment{$name}) {
801     for (@$attach) {
802     my ($klass, @attach) = @$_;
803     _attach $registry, $klass, @attach;
804     }
805     } else {
806     warn "object uses attachment '$name' that is not available, postponing.\n";
807     }
808     }
809     }
810    
811 root 1.100 cf::attachable->attach (
812     prio => -1000000,
813     on_instantiate => sub {
814     my ($obj, $data) = @_;
815 root 1.45
816 root 1.100 $data = from_json $data;
817 root 1.45
818 root 1.100 for (@$data) {
819     my ($name, $args) = @$_;
820 root 1.49
821 root 1.100 $obj->attach ($name, %{$args || {} });
822     }
823     },
824 root 1.102 on_reattach => \&reattach,
825 root 1.100 on_clone => sub {
826     my ($src, $dst) = @_;
827    
828     @{$dst->registry} = @{$src->registry};
829    
830     %$dst = %$src;
831    
832     %{$dst->{_attachment}} = %{$src->{_attachment}}
833     if exists $src->{_attachment};
834     },
835     );
836 root 1.45
837 root 1.46 sub object_freezer_save {
838 root 1.59 my ($filename, $rdata, $objs) = @_;
839 root 1.46
840 root 1.105 sync_job {
841     if (length $$rdata) {
842     warn sprintf "saving %s (%d,%d)\n",
843     $filename, length $$rdata, scalar @$objs;
844 root 1.60
845 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
846 root 1.60 chmod SAVE_MODE, $fh;
847 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
848     aio_fsync $fh;
849 root 1.60 close $fh;
850 root 1.105
851     if (@$objs) {
852     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
853     chmod SAVE_MODE, $fh;
854     my $data = Storable::nfreeze { version => 1, objs => $objs };
855     aio_write $fh, 0, (length $data), $data, 0;
856     aio_fsync $fh;
857     close $fh;
858     aio_rename "$filename.pst~", "$filename.pst";
859     }
860     } else {
861     aio_unlink "$filename.pst";
862     }
863    
864     aio_rename "$filename~", $filename;
865 root 1.60 } else {
866 root 1.105 warn "FATAL: $filename~: $!\n";
867 root 1.60 }
868 root 1.59 } else {
869 root 1.105 aio_unlink $filename;
870     aio_unlink "$filename.pst";
871 root 1.59 }
872 root 1.45 }
873     }
874    
875 root 1.80 sub object_freezer_as_string {
876     my ($rdata, $objs) = @_;
877    
878     use Data::Dumper;
879    
880 root 1.81 $$rdata . Dumper $objs
881 root 1.80 }
882    
883 root 1.46 sub object_thawer_load {
884     my ($filename) = @_;
885    
886 root 1.105 my ($data, $av);
887 root 1.61
888 root 1.105 (aio_load $filename, $data) >= 0
889     or return;
890 root 1.61
891 root 1.105 unless (aio_stat "$filename.pst") {
892     (aio_load "$filename.pst", $av) >= 0
893     or return;
894 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
895 root 1.61 }
896 root 1.45
897 root 1.118 warn sprintf "loading %s (%d)\n",
898     $filename, length $data, scalar @{$av || []};#d#
899 root 1.105 return ($data, $av);
900 root 1.45 }
901    
902     #############################################################################
903 root 1.85 # command handling &c
904 root 1.39
905 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
906 root 1.1
907 root 1.85 Register a callback for execution when the client sends the user command
908     $name.
909 root 1.5
910 root 1.85 =cut
911 root 1.5
912 root 1.85 sub register_command {
913     my ($name, $cb) = @_;
914 root 1.5
915 root 1.85 my $caller = caller;
916     #warn "registering command '$name/$time' to '$caller'";
917 root 1.1
918 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
919 root 1.1 }
920    
921 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
922 root 1.1
923 root 1.85 Register a callbackf ro execution when the client sends an extcmd packet.
924 root 1.1
925 root 1.85 If the callback returns something, it is sent back as if reply was being
926     called.
927 root 1.1
928 root 1.85 =cut
929 root 1.1
930 root 1.16 sub register_extcmd {
931     my ($name, $cb) = @_;
932    
933     my $caller = caller;
934     #warn "registering extcmd '$name' to '$caller'";
935    
936 root 1.85 $EXTCMD{$name} = [$cb, $caller];
937 root 1.16 }
938    
939 root 1.93 cf::player->attach (
940 root 1.85 on_command => sub {
941     my ($pl, $name, $params) = @_;
942    
943     my $cb = $COMMAND{$name}
944     or return;
945    
946     for my $cmd (@$cb) {
947     $cmd->[1]->($pl->ob, $params);
948     }
949    
950     cf::override;
951     },
952     on_extcmd => sub {
953     my ($pl, $buf) = @_;
954    
955     my $msg = eval { from_json $buf };
956    
957     if (ref $msg) {
958     if (my $cb = $EXTCMD{$msg->{msgtype}}) {
959     if (my %reply = $cb->[0]->($pl, $msg)) {
960     $pl->ext_reply ($msg->{msgid}, %reply);
961     }
962     }
963     } else {
964     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
965     }
966    
967     cf::override;
968     },
969 root 1.93 );
970 root 1.85
971 root 1.6 sub register {
972     my ($base, $pkg) = @_;
973    
974 root 1.45 #TODO
975 root 1.6 }
976    
977 root 1.1 sub load_extension {
978     my ($path) = @_;
979    
980     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
981 root 1.5 my $base = $1;
982 root 1.1 my $pkg = $1;
983     $pkg =~ s/[^[:word:]]/_/g;
984 root 1.41 $pkg = "ext::$pkg";
985 root 1.1
986     warn "loading '$path' into '$pkg'\n";
987    
988     open my $fh, "<:utf8", $path
989     or die "$path: $!";
990    
991     my $source =
992     "package $pkg; use strict; use utf8;\n"
993     . "#line 1 \"$path\"\n{\n"
994     . (do { local $/; <$fh> })
995     . "\n};\n1";
996    
997     eval $source
998 root 1.82 or die $@ ? "$path: $@\n"
999     : "extension disabled.\n";
1000 root 1.1
1001     push @exts, $pkg;
1002 root 1.5 $ext_pkg{$base} = $pkg;
1003 root 1.1
1004 root 1.6 # no strict 'refs';
1005 root 1.23 # @{"$pkg\::ISA"} = ext::;
1006 root 1.1
1007 root 1.6 register $base, $pkg;
1008 root 1.1 }
1009    
1010     sub unload_extension {
1011     my ($pkg) = @_;
1012    
1013     warn "removing extension $pkg\n";
1014    
1015     # remove hooks
1016 root 1.45 #TODO
1017     # for my $idx (0 .. $#PLUGIN_EVENT) {
1018     # delete $hook[$idx]{$pkg};
1019     # }
1020 root 1.1
1021     # remove commands
1022 root 1.85 for my $name (keys %COMMAND) {
1023     my @cb = grep $_->[0] ne $pkg, @{ $COMMAND{$name} };
1024 root 1.1
1025     if (@cb) {
1026 root 1.85 $COMMAND{$name} = \@cb;
1027 root 1.1 } else {
1028 root 1.85 delete $COMMAND{$name};
1029 root 1.1 }
1030     }
1031    
1032 root 1.15 # remove extcmds
1033 root 1.85 for my $name (grep $EXTCMD{$_}[1] eq $pkg, keys %EXTCMD) {
1034     delete $EXTCMD{$name};
1035 root 1.15 }
1036    
1037 root 1.43 if (my $cb = $pkg->can ("unload")) {
1038 elmex 1.31 eval {
1039     $cb->($pkg);
1040     1
1041     } or warn "$pkg unloaded, but with errors: $@";
1042     }
1043    
1044 root 1.1 Symbol::delete_package $pkg;
1045     }
1046    
1047     sub load_extensions {
1048     for my $ext (<$LIBDIR/*.ext>) {
1049 root 1.3 next unless -r $ext;
1050 root 1.2 eval {
1051     load_extension $ext;
1052     1
1053     } or warn "$ext not loaded: $@";
1054 root 1.1 }
1055     }
1056    
1057 root 1.8 #############################################################################
1058     # load/save/clean perl data associated with a map
1059    
1060 root 1.39 *cf::mapsupport::on_clean = sub {
1061 root 1.13 my ($map) = @_;
1062 root 1.7
1063     my $path = $map->tmpname;
1064     defined $path or return;
1065    
1066 root 1.46 unlink "$path.pst";
1067 root 1.7 };
1068    
1069 root 1.93 cf::map->attach (prio => -10000, package => cf::mapsupport::);
1070 root 1.39
1071 root 1.8 #############################################################################
1072     # load/save perl data associated with player->ob objects
1073    
1074 root 1.33 sub all_objects(@) {
1075     @_, map all_objects ($_->inv), @_
1076     }
1077    
1078 root 1.60 # TODO: compatibility cruft, remove when no longer needed
1079 root 1.93 cf::player->attach (
1080 root 1.39 on_load => sub {
1081     my ($pl, $path) = @_;
1082    
1083     for my $o (all_objects $pl->ob) {
1084     if (my $value = $o->get_ob_key_value ("_perl_data")) {
1085     $o->set_ob_key_value ("_perl_data");
1086 root 1.8
1087 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
1088     }
1089 root 1.11 }
1090 root 1.39 },
1091 root 1.93 );
1092 root 1.6
1093 root 1.22 #############################################################################
1094 root 1.70
1095     =head2 CORE EXTENSIONS
1096    
1097     Functions and methods that extend core crossfire objects.
1098    
1099 root 1.95 =head3 cf::player
1100    
1101 root 1.70 =over 4
1102 root 1.22
1103 root 1.23 =item cf::player::exists $login
1104    
1105     Returns true when the given account exists.
1106    
1107     =cut
1108    
1109     sub cf::player::exists($) {
1110     cf::player::find $_[0]
1111     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
1112     }
1113    
1114 root 1.95 =item $player->ext_reply ($msgid, $msgtype, %msg)
1115    
1116     Sends an ext reply to the player.
1117    
1118     =cut
1119    
1120     sub cf::player::ext_reply($$$%) {
1121     my ($self, $id, %msg) = @_;
1122    
1123     $msg{msgid} = $id;
1124    
1125     $self->send ("ext " . to_json \%msg);
1126     }
1127    
1128     =back
1129    
1130 root 1.110
1131     =head3 cf::map
1132    
1133     =over 4
1134    
1135     =cut
1136    
1137     package cf::map;
1138    
1139     use Fcntl;
1140     use Coro::AIO;
1141    
1142     our $MAX_RESET = 7200;
1143     our $DEFAULT_RESET = 3600;
1144    
1145     sub generate_random_map {
1146     my ($path, $rmp) = @_;
1147    
1148     # mit "rum" bekleckern, nicht
1149     cf::map::_create_random_map
1150     $path,
1151     $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1152     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1153     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1154     $rmp->{exit_on_final_map},
1155     $rmp->{xsize}, $rmp->{ysize},
1156     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1157     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1158     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1159     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1160     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1161     (cf::region::find $rmp->{region})
1162     }
1163    
1164     # and all this just because we cannot iterate over
1165     # all maps in C++...
1166     sub change_all_map_light {
1167     my ($change) = @_;
1168    
1169 root 1.122 $_->change_map_light ($change)
1170     for grep $_->outdoor, values %cf::MAP;
1171 root 1.110 }
1172    
1173     sub try_load_header($) {
1174     my ($path) = @_;
1175    
1176     utf8::encode $path;
1177     aio_open $path, O_RDONLY, 0
1178     or return;
1179    
1180     my $map = cf::map::new
1181     or return;
1182    
1183     $map->load_header ($path)
1184     or return;
1185    
1186     $map->{load_path} = $path;
1187    
1188     $map
1189     }
1190    
1191 root 1.126 sub find_map;
1192 root 1.110 sub find_map {
1193     my ($path, $origin) = @_;
1194    
1195     #warn "find_map<$path,$origin>\n";#d#
1196    
1197 root 1.112 $path = new cf::path $path, $origin && $origin->path;
1198 root 1.110 my $key = $path->as_string;
1199    
1200 root 1.120 cf::lock_wait "map_find:$key";
1201    
1202 root 1.110 $cf::MAP{$key} || do {
1203 root 1.120 my $guard = cf::lock_acquire "map_find:$key";
1204    
1205 root 1.110 # do it the slow way
1206     my $map = try_load_header $path->save_path;
1207    
1208     if ($map) {
1209     # safety
1210     $map->{instantiate_time} = $cf::RUNTIME
1211     if $map->{instantiate_time} > $cf::RUNTIME;
1212     } else {
1213     if (my $rmp = $path->random_map_params) {
1214     $map = generate_random_map $key, $rmp;
1215     } else {
1216     $map = try_load_header $path->load_path;
1217     }
1218    
1219     $map or return;
1220    
1221 root 1.111 $map->{load_original} = 1;
1222 root 1.110 $map->{instantiate_time} = $cf::RUNTIME;
1223     $map->instantiate;
1224    
1225     # per-player maps become, after loading, normal maps
1226     $map->per_player (0) if $path->{user_rel};
1227     }
1228    
1229     $map->path ($key);
1230     $map->{path} = $path;
1231 root 1.116 $map->{last_save} = $cf::RUNTIME;
1232 root 1.110 $map->last_access ($cf::RUNTIME);
1233    
1234 root 1.112 if ($map->should_reset) {
1235     $map->reset;
1236 root 1.123 undef $guard;
1237 root 1.126 $map = find_map $path
1238 root 1.124 or return;
1239 root 1.112 }
1240 root 1.110
1241     $cf::MAP{$key} = $map
1242     }
1243     }
1244    
1245     sub load {
1246     my ($self) = @_;
1247    
1248 root 1.120 my $path = $self->{path};
1249     my $guard = cf::lock_acquire "map_load:" . $path->as_string;
1250    
1251 root 1.110 return if $self->in_memory != cf::MAP_SWAPPED;
1252    
1253     $self->in_memory (cf::MAP_LOADING);
1254    
1255     $self->alloc;
1256     $self->load_objects ($self->{load_path}, 1)
1257     or return;
1258    
1259 root 1.112 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1260     if delete $self->{load_original};
1261 root 1.111
1262 root 1.110 if (my $uniq = $path->uniq_path) {
1263     utf8::encode $uniq;
1264     if (aio_open $uniq, O_RDONLY, 0) {
1265     $self->clear_unique_items;
1266     $self->load_objects ($uniq, 0);
1267     }
1268     }
1269    
1270     # now do the right thing for maps
1271     $self->link_multipart_objects;
1272    
1273     if ($self->{path}->is_style_map) {
1274     $self->{deny_save} = 1;
1275     $self->{deny_reset} = 1;
1276     } else {
1277     $self->fix_auto_apply;
1278     $self->decay_objects;
1279     $self->update_buttons;
1280     $self->set_darkness_map;
1281     $self->difficulty ($self->estimate_difficulty)
1282     unless $self->difficulty;
1283     $self->activate;
1284     }
1285    
1286     $self->in_memory (cf::MAP_IN_MEMORY);
1287     }
1288    
1289     sub load_map_sync {
1290     my ($path, $origin) = @_;
1291    
1292     #warn "load_map_sync<$path, $origin>\n";#d#
1293    
1294     cf::sync_job {
1295     my $map = cf::map::find_map $path, $origin
1296     or return;
1297     $map->load;
1298     $map
1299     }
1300     }
1301    
1302     sub save {
1303     my ($self) = @_;
1304    
1305     $self->{last_save} = $cf::RUNTIME;
1306    
1307     return unless $self->dirty;
1308    
1309 root 1.117 my $save = $self->{path}->save_path; utf8::encode $save;
1310     my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1311    
1312 root 1.110 $self->{load_path} = $save;
1313    
1314     return if $self->{deny_save};
1315    
1316     if ($uniq) {
1317     $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1318     $self->save_objects ($uniq, cf::IO_UNIQUES);
1319     } else {
1320     $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1321     }
1322     }
1323    
1324     sub swap_out {
1325     my ($self) = @_;
1326    
1327 root 1.130 # save first because save cedes
1328     $self->save;
1329    
1330 root 1.110 return if $self->players;
1331     return if $self->in_memory != cf::MAP_IN_MEMORY;
1332     return if $self->{deny_save};
1333    
1334     $self->clear;
1335     $self->in_memory (cf::MAP_SWAPPED);
1336     }
1337    
1338 root 1.112 sub reset_at {
1339     my ($self) = @_;
1340 root 1.110
1341     # TODO: safety, remove and allow resettable per-player maps
1342 root 1.114 return 1e99 if $self->{path}{user_rel};
1343     return 1e99 if $self->{deny_reset};
1344 root 1.110
1345 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1346 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1347 root 1.110
1348 root 1.112 $time + $to
1349     }
1350    
1351     sub should_reset {
1352     my ($self) = @_;
1353    
1354     $self->reset_at <= $cf::RUNTIME
1355 root 1.111 }
1356    
1357     sub unlink_save {
1358     my ($self) = @_;
1359    
1360     utf8::encode (my $save = $self->{path}->save_path);
1361     aioreq_pri 3; IO::AIO::aio_unlink $save;
1362     aioreq_pri 3; IO::AIO::aio_unlink "$save.pst";
1363 root 1.110 }
1364    
1365 root 1.113 sub rename {
1366     my ($self, $new_path) = @_;
1367    
1368     $self->unlink_save;
1369    
1370     delete $cf::MAP{$self->path};
1371     $self->{path} = new cf::path $new_path;
1372 root 1.114 $self->path ($self->{path}->as_string);
1373 root 1.113 $cf::MAP{$self->path} = $self;
1374    
1375     $self->save;
1376     }
1377    
1378 root 1.110 sub reset {
1379     my ($self) = @_;
1380    
1381     return if $self->players;
1382     return if $self->{path}{user_rel};#d#
1383    
1384     warn "resetting map ", $self->path;#d#
1385    
1386 root 1.111 delete $cf::MAP{$self->path};
1387 root 1.110
1388     $_->clear_links_to ($self) for values %cf::MAP;
1389    
1390 root 1.111 $self->unlink_save;
1391     $self->destroy;
1392 root 1.110 }
1393    
1394 root 1.114 my $nuke_counter = "aaaa";
1395    
1396     sub nuke {
1397     my ($self) = @_;
1398    
1399     $self->{deny_save} = 1;
1400     $self->reset_timeout (1);
1401     $self->rename ("{nuke}/" . ($nuke_counter++));
1402     $self->reset; # polite request, might not happen
1403     }
1404    
1405 root 1.110 sub customise_for {
1406     my ($map, $ob) = @_;
1407    
1408     if ($map->per_player) {
1409     return cf::map::find_map "~" . $ob->name . "/" . $map->{path}{path};
1410     }
1411    
1412     $map
1413     }
1414    
1415     sub emergency_save {
1416     local $cf::FREEZE = 1;
1417    
1418     warn "enter emergency map save\n";
1419    
1420     cf::sync_job {
1421     warn "begin emergency map save\n";
1422     $_->save for values %cf::MAP;
1423     };
1424    
1425     warn "end emergency map save\n";
1426     }
1427    
1428     package cf;
1429    
1430     =back
1431    
1432    
1433 root 1.95 =head3 cf::object::player
1434    
1435     =over 4
1436    
1437 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
1438 root 1.28
1439     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
1440     can be C<undef>. Does the right thing when the player is currently in a
1441     dialogue with the given NPC character.
1442    
1443     =cut
1444    
1445 root 1.22 # rough implementation of a future "reply" method that works
1446     # with dialog boxes.
1447 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
1448 root 1.23 sub cf::object::player::reply($$$;$) {
1449     my ($self, $npc, $msg, $flags) = @_;
1450    
1451     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1452 root 1.22
1453 root 1.24 if ($self->{record_replies}) {
1454     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
1455     } else {
1456     $msg = $npc->name . " says: $msg" if $npc;
1457     $self->message ($msg, $flags);
1458     }
1459 root 1.22 }
1460    
1461 root 1.79 =item $player_object->may ("access")
1462    
1463     Returns wether the given player is authorized to access resource "access"
1464     (e.g. "command_wizcast").
1465    
1466     =cut
1467    
1468     sub cf::object::player::may {
1469     my ($self, $access) = @_;
1470    
1471     $self->flag (cf::FLAG_WIZ) ||
1472     (ref $cf::CFG{"may_$access"}
1473     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
1474     : $cf::CFG{"may_$access"})
1475     }
1476 root 1.70
1477 root 1.115 =item $player_object->enter_link
1478    
1479     Freezes the player and moves him/her to a special map (C<{link}>).
1480    
1481     The player should be reaosnably safe there for short amounts of time. You
1482     I<MUST> call C<leave_link> as soon as possible, though.
1483    
1484     =item $player_object->leave_link ($map, $x, $y)
1485    
1486     Moves the player out of the specila link map onto the given map. If the
1487     map is not valid (or omitted), the player will be moved back to the
1488     location he/she was before the call to C<enter_link>, or, if that fails,
1489     to the emergency map position.
1490    
1491     Might block.
1492    
1493     =cut
1494    
1495 root 1.110 sub cf::object::player::enter_link {
1496     my ($self) = @_;
1497    
1498 root 1.120 $self->deactivate_recursive;
1499    
1500 root 1.110 return if $self->map == $LINK_MAP;
1501    
1502 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
1503 root 1.110 if $self->map;
1504    
1505     $self->enter_map ($LINK_MAP, 20, 20);
1506     }
1507    
1508     sub cf::object::player::leave_link {
1509     my ($self, $map, $x, $y) = @_;
1510    
1511     my $link_pos = delete $self->{_link_pos};
1512    
1513     unless ($map) {
1514     # restore original map position
1515     ($map, $x, $y) = @{ $link_pos || [] };
1516     $map = cf::map::find_map $map;
1517    
1518     unless ($map) {
1519     ($map, $x, $y) = @$EMERGENCY_POSITION;
1520     $map = cf::map::find_map $map
1521     or die "FATAL: cannot load emergency map\n";
1522     }
1523     }
1524    
1525     ($x, $y) = (-1, -1)
1526     unless (defined $x) && (defined $y);
1527    
1528     # use -1 or undef as default coordinates, not 0, 0
1529     ($x, $y) = ($map->enter_x, $map->enter_y)
1530     if $x <=0 && $y <= 0;
1531    
1532     $map->load;
1533    
1534     $self->activate_recursive;
1535     $self->enter_map ($map, $x, $y);
1536     }
1537    
1538 root 1.120 cf::player->attach (
1539     on_logout => sub {
1540     my ($pl) = @_;
1541    
1542     # abort map switching before logout
1543     if ($pl->ob->{_link_pos}) {
1544     cf::sync_job {
1545     $pl->ob->leave_link
1546     };
1547     }
1548     },
1549     on_login => sub {
1550     my ($pl) = @_;
1551    
1552     # try to abort aborted map switching on player login :)
1553     # should happen only on crashes
1554     if ($pl->ob->{_link_pos}) {
1555     $pl->ob->enter_link;
1556 root 1.127 cf::async {
1557 root 1.120 # we need this sleep as the login has a concurrent enter_exit running
1558     # and this sleep increases chances of the player not ending up in scorn
1559     Coro::Timer::sleep 1;
1560     $pl->ob->leave_link;
1561     };
1562     }
1563     },
1564     );
1565    
1566 root 1.118 =item $player_object->goto_map ($path, $x, $y)
1567 root 1.110
1568     =cut
1569    
1570     sub cf::object::player::goto_map {
1571     my ($self, $path, $x, $y) = @_;
1572    
1573     $self->enter_link;
1574    
1575 root 1.127 (cf::async {
1576 root 1.110 $path = new cf::path $path;
1577    
1578     my $map = cf::map::find_map $path->as_string;
1579     $map = $map->customise_for ($self) if $map;
1580    
1581 root 1.119 # warn "entering ", $map->path, " at ($x, $y)\n"
1582     # if $map;
1583 root 1.110
1584 root 1.115 $map or $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1585    
1586 root 1.110 $self->leave_link ($map, $x, $y);
1587     })->prio (1);
1588     }
1589    
1590     =item $player_object->enter_exit ($exit_object)
1591    
1592     =cut
1593    
1594     sub parse_random_map_params {
1595     my ($spec) = @_;
1596    
1597     my $rmp = { # defaults
1598     xsize => 10,
1599     ysize => 10,
1600     };
1601    
1602     for (split /\n/, $spec) {
1603     my ($k, $v) = split /\s+/, $_, 2;
1604    
1605     $rmp->{lc $k} = $v if (length $k) && (length $v);
1606     }
1607    
1608     $rmp
1609     }
1610    
1611     sub prepare_random_map {
1612     my ($exit) = @_;
1613    
1614     # all this does is basically replace the /! path by
1615     # a new random map path (?random/...) with a seed
1616     # that depends on the exit object
1617    
1618     my $rmp = parse_random_map_params $exit->msg;
1619    
1620     if ($exit->map) {
1621     $rmp->{region} = $exit->map->region_name;
1622     $rmp->{origin_map} = $exit->map->path;
1623     $rmp->{origin_x} = $exit->x;
1624     $rmp->{origin_y} = $exit->y;
1625     }
1626    
1627     $rmp->{random_seed} ||= $exit->random_seed;
1628    
1629     my $data = cf::to_json $rmp;
1630     my $md5 = Digest::MD5::md5_hex $data;
1631    
1632     if (my $fh = aio_open "$cf::RANDOM_MAPS/$md5.meta", O_WRONLY | O_CREAT, 0666) {
1633     aio_write $fh, 0, (length $data), $data, 0;
1634    
1635     $exit->slaying ("?random/$md5");
1636     $exit->msg (undef);
1637     }
1638     }
1639    
1640     sub cf::object::player::enter_exit {
1641     my ($self, $exit) = @_;
1642    
1643     return unless $self->type == cf::PLAYER;
1644    
1645     $self->enter_link;
1646    
1647 root 1.127 (cf::async {
1648 root 1.110 unless (eval {
1649     prepare_random_map $exit
1650     if $exit->slaying eq "/!";
1651    
1652     my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1653     $self->goto_map ($path, $exit->stats->hp, $exit->stats->sp);
1654    
1655     1;
1656     }) {
1657     $self->message ("Something went wrong deep within the crossfire server. "
1658     . "I'll try to bring you back to the map you were before. "
1659     . "Please report this to the dungeon master",
1660     cf::NDI_UNIQUE | cf::NDI_RED);
1661    
1662     warn "ERROR in enter_exit: $@";
1663     $self->leave_link;
1664     }
1665     })->prio (1);
1666     }
1667    
1668 root 1.95 =head3 cf::client
1669    
1670     =over 4
1671    
1672     =item $client->send_drawinfo ($text, $flags)
1673    
1674     Sends a drawinfo packet to the client. Circumvents output buffering so
1675     should not be used under normal circumstances.
1676    
1677 root 1.70 =cut
1678    
1679 root 1.95 sub cf::client::send_drawinfo {
1680     my ($self, $text, $flags) = @_;
1681    
1682     utf8::encode $text;
1683     $self->send_packet (sprintf "drawinfo %d %s", $flags, $text);
1684     }
1685    
1686    
1687     =item $success = $client->query ($flags, "text", \&cb)
1688    
1689     Queues a query to the client, calling the given callback with
1690     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
1691     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
1692    
1693     Queries can fail, so check the return code. Or don't, as queries will become
1694     reliable at some point in the future.
1695    
1696     =cut
1697    
1698     sub cf::client::query {
1699     my ($self, $flags, $text, $cb) = @_;
1700    
1701     return unless $self->state == ST_PLAYING
1702     || $self->state == ST_SETUP
1703     || $self->state == ST_CUSTOM;
1704    
1705     $self->state (ST_CUSTOM);
1706    
1707     utf8::encode $text;
1708     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
1709    
1710     $self->send_packet ($self->{query_queue}[0][0])
1711     if @{ $self->{query_queue} } == 1;
1712     }
1713    
1714     cf::client->attach (
1715     on_reply => sub {
1716     my ($ns, $msg) = @_;
1717    
1718     # this weird shuffling is so that direct followup queries
1719     # get handled first
1720 root 1.128 my $queue = delete $ns->{query_queue}
1721 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
1722 root 1.95
1723     (shift @$queue)->[1]->($msg);
1724    
1725     push @{ $ns->{query_queue} }, @$queue;
1726    
1727     if (@{ $ns->{query_queue} } == @$queue) {
1728     if (@$queue) {
1729     $ns->send_packet ($ns->{query_queue}[0][0]);
1730     } else {
1731 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
1732 root 1.95 }
1733     }
1734     },
1735     );
1736    
1737 root 1.96 =item $client->coro (\&cb)
1738    
1739     Create a new coroutine, running the specified callback. The coroutine will
1740     be automatically cancelled when the client gets destroyed (e.g. on logout,
1741     or loss of connection).
1742    
1743     =cut
1744    
1745     sub cf::client::coro {
1746     my ($self, $cb) = @_;
1747    
1748 root 1.127 my $coro = &cf::async ($cb);
1749 root 1.103
1750     $coro->on_destroy (sub {
1751 root 1.96 delete $self->{_coro}{$coro+0};
1752 root 1.103 });
1753 root 1.96
1754     $self->{_coro}{$coro+0} = $coro;
1755 root 1.103
1756     $coro
1757 root 1.96 }
1758    
1759     cf::client->attach (
1760     on_destroy => sub {
1761     my ($ns) = @_;
1762    
1763 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
1764 root 1.96 },
1765     );
1766    
1767 root 1.95 =back
1768    
1769 root 1.70
1770     =head2 SAFE SCRIPTING
1771    
1772     Functions that provide a safe environment to compile and execute
1773     snippets of perl code without them endangering the safety of the server
1774     itself. Looping constructs, I/O operators and other built-in functionality
1775     is not available in the safe scripting environment, and the number of
1776 root 1.79 functions and methods that can be called is greatly reduced.
1777 root 1.70
1778     =cut
1779 root 1.23
1780 root 1.42 our $safe = new Safe "safe";
1781 root 1.23 our $safe_hole = new Safe::Hole;
1782    
1783     $SIG{FPE} = 'IGNORE';
1784    
1785     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
1786    
1787 root 1.25 # here we export the classes and methods available to script code
1788    
1789 root 1.70 =pod
1790    
1791     The following fucntions and emthods are available within a safe environment:
1792    
1793 elmex 1.91 cf::object contr pay_amount pay_player map
1794 root 1.70 cf::object::player player
1795     cf::player peaceful
1796 elmex 1.91 cf::map trigger
1797 root 1.70
1798     =cut
1799    
1800 root 1.25 for (
1801 elmex 1.91 ["cf::object" => qw(contr pay_amount pay_player map)],
1802 root 1.25 ["cf::object::player" => qw(player)],
1803     ["cf::player" => qw(peaceful)],
1804 elmex 1.91 ["cf::map" => qw(trigger)],
1805 root 1.25 ) {
1806     no strict 'refs';
1807     my ($pkg, @funs) = @$_;
1808 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
1809 root 1.25 for @funs;
1810     }
1811 root 1.23
1812 root 1.70 =over 4
1813    
1814     =item @retval = safe_eval $code, [var => value, ...]
1815    
1816     Compiled and executes the given perl code snippet. additional var/value
1817     pairs result in temporary local (my) scalar variables of the given name
1818     that are available in the code snippet. Example:
1819    
1820     my $five = safe_eval '$first + $second', first => 1, second => 4;
1821    
1822     =cut
1823    
1824 root 1.23 sub safe_eval($;@) {
1825     my ($code, %vars) = @_;
1826    
1827     my $qcode = $code;
1828     $qcode =~ s/"/‟/g; # not allowed in #line filenames
1829     $qcode =~ s/\n/\\n/g;
1830    
1831     local $_;
1832 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
1833 root 1.23
1834 root 1.42 my $eval =
1835 root 1.23 "do {\n"
1836     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
1837     . "#line 0 \"{$qcode}\"\n"
1838     . $code
1839     . "\n}"
1840 root 1.25 ;
1841    
1842     sub_generation_inc;
1843 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
1844 root 1.25 sub_generation_inc;
1845    
1846 root 1.42 if ($@) {
1847     warn "$@";
1848     warn "while executing safe code '$code'\n";
1849     warn "with arguments " . (join " ", %vars) . "\n";
1850     }
1851    
1852 root 1.25 wantarray ? @res : $res[0]
1853 root 1.23 }
1854    
1855 root 1.69 =item cf::register_script_function $function => $cb
1856    
1857     Register a function that can be called from within map/npc scripts. The
1858     function should be reasonably secure and should be put into a package name
1859     like the extension.
1860    
1861     Example: register a function that gets called whenever a map script calls
1862     C<rent::overview>, as used by the C<rent> extension.
1863    
1864     cf::register_script_function "rent::overview" => sub {
1865     ...
1866     };
1867    
1868     =cut
1869    
1870 root 1.23 sub register_script_function {
1871     my ($fun, $cb) = @_;
1872    
1873     no strict 'refs';
1874 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
1875 root 1.23 }
1876    
1877 root 1.70 =back
1878    
1879 root 1.71 =cut
1880    
1881 root 1.23 #############################################################################
1882 root 1.65
1883     =head2 EXTENSION DATABASE SUPPORT
1884    
1885     Crossfire maintains a very simple database for extension use. It can
1886     currently store anything that can be serialised using Storable, which
1887     excludes objects.
1888    
1889     The parameter C<$family> should best start with the name of the extension
1890     using it, it should be unique.
1891    
1892     =over 4
1893    
1894     =item $hashref = cf::db_get $family
1895    
1896     Return a hashref for use by the extension C<$family>, which can be
1897     modified. After modifications, you have to call C<cf::db_dirty> or
1898     C<cf::db_sync>.
1899    
1900     =item $value = cf::db_get $family => $key
1901    
1902     Returns a single value from the database
1903    
1904     =item cf::db_put $family => $hashref
1905    
1906     Stores the given family hashref into the database. Updates are delayed, if
1907     you want the data to be synced to disk immediately, use C<cf::db_sync>.
1908    
1909     =item cf::db_put $family => $key => $value
1910    
1911     Stores the given C<$value> in the family hash. Updates are delayed, if you
1912     want the data to be synced to disk immediately, use C<cf::db_sync>.
1913    
1914     =item cf::db_dirty
1915    
1916     Marks the database as dirty, to be updated at a later time.
1917    
1918     =item cf::db_sync
1919    
1920     Immediately write the database to disk I<if it is dirty>.
1921    
1922     =cut
1923    
1924 root 1.78 our $DB;
1925    
1926 root 1.65 {
1927 root 1.66 my $path = cf::localdir . "/database.pst";
1928 root 1.65
1929     sub db_load() {
1930 root 1.78 $DB = stat $path ? Storable::retrieve $path : { };
1931 root 1.65 }
1932    
1933     my $pid;
1934    
1935     sub db_save() {
1936     waitpid $pid, 0 if $pid;
1937 root 1.67 if (0 == ($pid = fork)) {
1938 root 1.78 $DB->{_meta}{version} = 1;
1939     Storable::nstore $DB, "$path~";
1940 root 1.65 rename "$path~", $path;
1941     cf::_exit 0 if defined $pid;
1942     }
1943     }
1944    
1945     my $dirty;
1946    
1947     sub db_sync() {
1948     db_save if $dirty;
1949     undef $dirty;
1950     }
1951    
1952 root 1.87 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, data => WF_AUTOCANCEL, cb => sub {
1953 root 1.65 db_sync;
1954     });
1955    
1956     sub db_dirty() {
1957     $dirty = 1;
1958     $idle->start;
1959     }
1960    
1961     sub db_get($;$) {
1962     @_ >= 2
1963 root 1.78 ? $DB->{$_[0]}{$_[1]}
1964     : ($DB->{$_[0]} ||= { })
1965 root 1.65 }
1966    
1967     sub db_put($$;$) {
1968     if (@_ >= 3) {
1969 root 1.78 $DB->{$_[0]}{$_[1]} = $_[2];
1970 root 1.65 } else {
1971 root 1.78 $DB->{$_[0]} = $_[1];
1972 root 1.65 }
1973     db_dirty;
1974     }
1975 root 1.67
1976 root 1.93 cf::global->attach (
1977     prio => 10000,
1978 root 1.67 on_cleanup => sub {
1979     db_sync;
1980     },
1981 root 1.93 );
1982 root 1.65 }
1983    
1984     #############################################################################
1985 root 1.34 # the server's main()
1986    
1987 root 1.73 sub cfg_load {
1988 root 1.72 open my $fh, "<:utf8", cf::confdir . "/config"
1989     or return;
1990    
1991     local $/;
1992     *CFG = YAML::Syck::Load <$fh>;
1993     }
1994    
1995 root 1.39 sub main {
1996 root 1.108 # we must not ever block the main coroutine
1997     local $Coro::idle = sub {
1998 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
1999 root 1.108 (Coro::unblock_sub {
2000     Event::one_event;
2001     })->();
2002     };
2003    
2004 root 1.73 cfg_load;
2005 root 1.65 db_load;
2006 root 1.61 load_extensions;
2007 root 1.34 Event::loop;
2008     }
2009    
2010     #############################################################################
2011 root 1.22 # initialisation
2012    
2013 root 1.111 sub reload() {
2014 root 1.106 # can/must only be called in main
2015     if ($Coro::current != $Coro::main) {
2016     warn "can only reload from main coroutine\n";
2017     return;
2018     }
2019    
2020 root 1.103 warn "reloading...";
2021    
2022 root 1.106 local $FREEZE = 1;
2023     cf::emergency_save;
2024    
2025 root 1.103 eval {
2026 root 1.106 # if anything goes wrong in here, we should simply crash as we already saved
2027 root 1.65
2028     # cancel all watchers
2029 root 1.87 for (Event::all_watchers) {
2030     $_->cancel if $_->data & WF_AUTOCANCEL;
2031     }
2032 root 1.65
2033 root 1.103 # cancel all extension coros
2034     $_->cancel for values %EXT_CORO;
2035     %EXT_CORO = ();
2036    
2037 root 1.65 # unload all extensions
2038     for (@exts) {
2039 root 1.103 warn "unloading <$_>";
2040 root 1.65 unload_extension $_;
2041     }
2042    
2043     # unload all modules loaded from $LIBDIR
2044     while (my ($k, $v) = each %INC) {
2045     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
2046    
2047 root 1.103 warn "removing <$k>";
2048 root 1.65 delete $INC{$k};
2049    
2050     $k =~ s/\.pm$//;
2051     $k =~ s/\//::/g;
2052    
2053     if (my $cb = $k->can ("unload_module")) {
2054     $cb->();
2055     }
2056    
2057     Symbol::delete_package $k;
2058     }
2059    
2060     # sync database to disk
2061     cf::db_sync;
2062 root 1.103 IO::AIO::flush;
2063 root 1.65
2064     # get rid of safe::, as good as possible
2065     Symbol::delete_package "safe::$_"
2066 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
2067 root 1.65
2068     # remove register_script_function callbacks
2069     # TODO
2070    
2071     # unload cf.pm "a bit"
2072     delete $INC{"cf.pm"};
2073    
2074     # don't, removes xs symbols, too,
2075     # and global variables created in xs
2076     #Symbol::delete_package __PACKAGE__;
2077    
2078     # reload cf.pm
2079 root 1.103 warn "reloading cf.pm";
2080 root 1.65 require cf;
2081 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
2082    
2083 root 1.73 # load config and database again
2084     cf::cfg_load;
2085 root 1.65 cf::db_load;
2086    
2087     # load extensions
2088 root 1.103 warn "load extensions";
2089 root 1.65 cf::load_extensions;
2090    
2091     # reattach attachments to objects
2092 root 1.103 warn "reattach";
2093 root 1.65 _global_reattach;
2094     };
2095    
2096 root 1.106 if ($@) {
2097     warn $@;
2098     warn "error while reloading, exiting.";
2099     exit 1;
2100     }
2101    
2102     warn "reloaded successfully";
2103 root 1.65 };
2104    
2105 root 1.108 #############################################################################
2106    
2107     unless ($LINK_MAP) {
2108     $LINK_MAP = cf::map::new;
2109    
2110     $LINK_MAP->width (41);
2111     $LINK_MAP->height (41);
2112     $LINK_MAP->alloc;
2113     $LINK_MAP->path ("{link}");
2114     $LINK_MAP->{path} = bless { path => "{link}" }, "cf::path";
2115     $LINK_MAP->in_memory (MAP_IN_MEMORY);
2116 root 1.110
2117     # dirty hack because... archetypes are not yet loaded
2118     Event->timer (
2119     after => 2,
2120     cb => sub {
2121     $_[0]->w->cancel;
2122    
2123     # provide some exits "home"
2124     my $exit = cf::object::new "exit";
2125    
2126     $exit->slaying ($EMERGENCY_POSITION->[0]);
2127     $exit->stats->hp ($EMERGENCY_POSITION->[1]);
2128     $exit->stats->sp ($EMERGENCY_POSITION->[2]);
2129    
2130     $LINK_MAP->insert ($exit->clone, 19, 19);
2131     $LINK_MAP->insert ($exit->clone, 19, 20);
2132     $LINK_MAP->insert ($exit->clone, 19, 21);
2133     $LINK_MAP->insert ($exit->clone, 20, 19);
2134     $LINK_MAP->insert ($exit->clone, 20, 21);
2135     $LINK_MAP->insert ($exit->clone, 21, 19);
2136     $LINK_MAP->insert ($exit->clone, 21, 20);
2137     $LINK_MAP->insert ($exit->clone, 21, 21);
2138    
2139     $exit->destroy;
2140     });
2141    
2142     $LINK_MAP->{deny_save} = 1;
2143     $LINK_MAP->{deny_reset} = 1;
2144    
2145     $cf::MAP{$LINK_MAP->path} = $LINK_MAP;
2146 root 1.108 }
2147    
2148 root 1.85 register "<global>", __PACKAGE__;
2149    
2150 root 1.111 register_command "reload" => sub {
2151 root 1.65 my ($who, $arg) = @_;
2152    
2153     if ($who->flag (FLAG_WIZ)) {
2154 root 1.107 $who->message ("start of reload.");
2155 root 1.111 reload;
2156 root 1.107 $who->message ("end of reload.");
2157 root 1.65 }
2158     };
2159    
2160 root 1.27 unshift @INC, $LIBDIR;
2161 root 1.17
2162 root 1.35 $TICK_WATCHER = Event->timer (
2163 root 1.104 reentrant => 0,
2164     prio => 0,
2165     at => $NEXT_TICK || $TICK,
2166     data => WF_AUTOCANCEL,
2167     cb => sub {
2168 root 1.103 unless ($FREEZE) {
2169     cf::server_tick; # one server iteration
2170     $RUNTIME += $TICK;
2171     }
2172 root 1.35
2173     $NEXT_TICK += $TICK;
2174    
2175 root 1.78 # if we are delayed by four ticks or more, skip them all
2176 root 1.103 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4;
2177 root 1.35
2178     $TICK_WATCHER->at ($NEXT_TICK);
2179     $TICK_WATCHER->start;
2180     },
2181     );
2182    
2183 root 1.80 IO::AIO::max_poll_time $TICK * 0.2;
2184 root 1.77
2185 root 1.108 Event->io (
2186     fd => IO::AIO::poll_fileno,
2187     poll => 'r',
2188     prio => 5,
2189     data => WF_AUTOCANCEL,
2190     cb => \&IO::AIO::poll_cb,
2191     );
2192    
2193     Event->timer (
2194     data => WF_AUTOCANCEL,
2195     after => 0,
2196     interval => 10,
2197     cb => sub {
2198     (Coro::unblock_sub {
2199     write_runtime
2200     or warn "ERROR: unable to write runtime file: $!";
2201     })->();
2202     },
2203     );
2204 root 1.103
2205 root 1.125 END { cf::emergency_save }
2206    
2207 root 1.1 1
2208