ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.129
Committed: Wed Jan 3 00:41:19 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.128: +1 -1 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.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     return if $self->players;
1328     return if $self->in_memory != cf::MAP_IN_MEMORY;
1329     return if $self->{deny_save};
1330    
1331     $self->save;
1332     $self->clear;
1333     $self->in_memory (cf::MAP_SWAPPED);
1334     }
1335    
1336 root 1.112 sub reset_at {
1337     my ($self) = @_;
1338 root 1.110
1339     # TODO: safety, remove and allow resettable per-player maps
1340 root 1.114 return 1e99 if $self->{path}{user_rel};
1341     return 1e99 if $self->{deny_reset};
1342 root 1.110
1343 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1344 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1345 root 1.110
1346 root 1.112 $time + $to
1347     }
1348    
1349     sub should_reset {
1350     my ($self) = @_;
1351    
1352     $self->reset_at <= $cf::RUNTIME
1353 root 1.111 }
1354    
1355     sub unlink_save {
1356     my ($self) = @_;
1357    
1358     utf8::encode (my $save = $self->{path}->save_path);
1359     aioreq_pri 3; IO::AIO::aio_unlink $save;
1360     aioreq_pri 3; IO::AIO::aio_unlink "$save.pst";
1361 root 1.110 }
1362    
1363 root 1.113 sub rename {
1364     my ($self, $new_path) = @_;
1365    
1366     $self->unlink_save;
1367    
1368     delete $cf::MAP{$self->path};
1369     $self->{path} = new cf::path $new_path;
1370 root 1.114 $self->path ($self->{path}->as_string);
1371 root 1.113 $cf::MAP{$self->path} = $self;
1372    
1373     $self->save;
1374     }
1375    
1376 root 1.110 sub reset {
1377     my ($self) = @_;
1378    
1379     return if $self->players;
1380     return if $self->{path}{user_rel};#d#
1381    
1382     warn "resetting map ", $self->path;#d#
1383    
1384 root 1.111 delete $cf::MAP{$self->path};
1385 root 1.110
1386     $_->clear_links_to ($self) for values %cf::MAP;
1387    
1388 root 1.111 $self->unlink_save;
1389     $self->destroy;
1390 root 1.110 }
1391    
1392 root 1.114 my $nuke_counter = "aaaa";
1393    
1394     sub nuke {
1395     my ($self) = @_;
1396    
1397     $self->{deny_save} = 1;
1398     $self->reset_timeout (1);
1399     $self->rename ("{nuke}/" . ($nuke_counter++));
1400     $self->reset; # polite request, might not happen
1401     }
1402    
1403 root 1.110 sub customise_for {
1404     my ($map, $ob) = @_;
1405    
1406     if ($map->per_player) {
1407     return cf::map::find_map "~" . $ob->name . "/" . $map->{path}{path};
1408     }
1409    
1410     $map
1411     }
1412    
1413     sub emergency_save {
1414     local $cf::FREEZE = 1;
1415    
1416     warn "enter emergency map save\n";
1417    
1418     cf::sync_job {
1419     warn "begin emergency map save\n";
1420     $_->save for values %cf::MAP;
1421     };
1422    
1423     warn "end emergency map save\n";
1424     }
1425    
1426     package cf;
1427    
1428     =back
1429    
1430    
1431 root 1.95 =head3 cf::object::player
1432    
1433     =over 4
1434    
1435 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
1436 root 1.28
1437     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
1438     can be C<undef>. Does the right thing when the player is currently in a
1439     dialogue with the given NPC character.
1440    
1441     =cut
1442    
1443 root 1.22 # rough implementation of a future "reply" method that works
1444     # with dialog boxes.
1445 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
1446 root 1.23 sub cf::object::player::reply($$$;$) {
1447     my ($self, $npc, $msg, $flags) = @_;
1448    
1449     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1450 root 1.22
1451 root 1.24 if ($self->{record_replies}) {
1452     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
1453     } else {
1454     $msg = $npc->name . " says: $msg" if $npc;
1455     $self->message ($msg, $flags);
1456     }
1457 root 1.22 }
1458    
1459 root 1.79 =item $player_object->may ("access")
1460    
1461     Returns wether the given player is authorized to access resource "access"
1462     (e.g. "command_wizcast").
1463    
1464     =cut
1465    
1466     sub cf::object::player::may {
1467     my ($self, $access) = @_;
1468    
1469     $self->flag (cf::FLAG_WIZ) ||
1470     (ref $cf::CFG{"may_$access"}
1471     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
1472     : $cf::CFG{"may_$access"})
1473     }
1474 root 1.70
1475 root 1.115 =item $player_object->enter_link
1476    
1477     Freezes the player and moves him/her to a special map (C<{link}>).
1478    
1479     The player should be reaosnably safe there for short amounts of time. You
1480     I<MUST> call C<leave_link> as soon as possible, though.
1481    
1482     =item $player_object->leave_link ($map, $x, $y)
1483    
1484     Moves the player out of the specila link map onto the given map. If the
1485     map is not valid (or omitted), the player will be moved back to the
1486     location he/she was before the call to C<enter_link>, or, if that fails,
1487     to the emergency map position.
1488    
1489     Might block.
1490    
1491     =cut
1492    
1493 root 1.110 sub cf::object::player::enter_link {
1494     my ($self) = @_;
1495    
1496 root 1.120 $self->deactivate_recursive;
1497    
1498 root 1.110 return if $self->map == $LINK_MAP;
1499    
1500 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
1501 root 1.110 if $self->map;
1502    
1503     $self->enter_map ($LINK_MAP, 20, 20);
1504     }
1505    
1506     sub cf::object::player::leave_link {
1507     my ($self, $map, $x, $y) = @_;
1508    
1509     my $link_pos = delete $self->{_link_pos};
1510    
1511     unless ($map) {
1512     # restore original map position
1513     ($map, $x, $y) = @{ $link_pos || [] };
1514     $map = cf::map::find_map $map;
1515    
1516     unless ($map) {
1517     ($map, $x, $y) = @$EMERGENCY_POSITION;
1518     $map = cf::map::find_map $map
1519     or die "FATAL: cannot load emergency map\n";
1520     }
1521     }
1522    
1523     ($x, $y) = (-1, -1)
1524     unless (defined $x) && (defined $y);
1525    
1526     # use -1 or undef as default coordinates, not 0, 0
1527     ($x, $y) = ($map->enter_x, $map->enter_y)
1528     if $x <=0 && $y <= 0;
1529    
1530     $map->load;
1531    
1532     $self->activate_recursive;
1533     $self->enter_map ($map, $x, $y);
1534     }
1535    
1536 root 1.120 cf::player->attach (
1537     on_logout => sub {
1538     my ($pl) = @_;
1539    
1540     # abort map switching before logout
1541     if ($pl->ob->{_link_pos}) {
1542     cf::sync_job {
1543     $pl->ob->leave_link
1544     };
1545     }
1546     },
1547     on_login => sub {
1548     my ($pl) = @_;
1549    
1550     # try to abort aborted map switching on player login :)
1551     # should happen only on crashes
1552     if ($pl->ob->{_link_pos}) {
1553     $pl->ob->enter_link;
1554 root 1.127 cf::async {
1555 root 1.120 # we need this sleep as the login has a concurrent enter_exit running
1556     # and this sleep increases chances of the player not ending up in scorn
1557     Coro::Timer::sleep 1;
1558     $pl->ob->leave_link;
1559     };
1560     }
1561     },
1562     );
1563    
1564 root 1.118 =item $player_object->goto_map ($path, $x, $y)
1565 root 1.110
1566     =cut
1567    
1568     sub cf::object::player::goto_map {
1569     my ($self, $path, $x, $y) = @_;
1570    
1571     $self->enter_link;
1572    
1573 root 1.127 (cf::async {
1574 root 1.110 $path = new cf::path $path;
1575    
1576     my $map = cf::map::find_map $path->as_string;
1577     $map = $map->customise_for ($self) if $map;
1578    
1579 root 1.119 # warn "entering ", $map->path, " at ($x, $y)\n"
1580     # if $map;
1581 root 1.110
1582 root 1.115 $map or $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1583    
1584 root 1.110 $self->leave_link ($map, $x, $y);
1585     })->prio (1);
1586     }
1587    
1588     =item $player_object->enter_exit ($exit_object)
1589    
1590     =cut
1591    
1592     sub parse_random_map_params {
1593     my ($spec) = @_;
1594    
1595     my $rmp = { # defaults
1596     xsize => 10,
1597     ysize => 10,
1598     };
1599    
1600     for (split /\n/, $spec) {
1601     my ($k, $v) = split /\s+/, $_, 2;
1602    
1603     $rmp->{lc $k} = $v if (length $k) && (length $v);
1604     }
1605    
1606     $rmp
1607     }
1608    
1609     sub prepare_random_map {
1610     my ($exit) = @_;
1611    
1612     # all this does is basically replace the /! path by
1613     # a new random map path (?random/...) with a seed
1614     # that depends on the exit object
1615    
1616     my $rmp = parse_random_map_params $exit->msg;
1617    
1618     if ($exit->map) {
1619     $rmp->{region} = $exit->map->region_name;
1620     $rmp->{origin_map} = $exit->map->path;
1621     $rmp->{origin_x} = $exit->x;
1622     $rmp->{origin_y} = $exit->y;
1623     }
1624    
1625     $rmp->{random_seed} ||= $exit->random_seed;
1626    
1627     my $data = cf::to_json $rmp;
1628     my $md5 = Digest::MD5::md5_hex $data;
1629    
1630     if (my $fh = aio_open "$cf::RANDOM_MAPS/$md5.meta", O_WRONLY | O_CREAT, 0666) {
1631     aio_write $fh, 0, (length $data), $data, 0;
1632    
1633     $exit->slaying ("?random/$md5");
1634     $exit->msg (undef);
1635     }
1636     }
1637    
1638     sub cf::object::player::enter_exit {
1639     my ($self, $exit) = @_;
1640    
1641     return unless $self->type == cf::PLAYER;
1642    
1643     $self->enter_link;
1644    
1645 root 1.127 (cf::async {
1646 root 1.110 unless (eval {
1647     prepare_random_map $exit
1648     if $exit->slaying eq "/!";
1649    
1650     my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1651     $self->goto_map ($path, $exit->stats->hp, $exit->stats->sp);
1652    
1653     1;
1654     }) {
1655     $self->message ("Something went wrong deep within the crossfire server. "
1656     . "I'll try to bring you back to the map you were before. "
1657     . "Please report this to the dungeon master",
1658     cf::NDI_UNIQUE | cf::NDI_RED);
1659    
1660     warn "ERROR in enter_exit: $@";
1661     $self->leave_link;
1662     }
1663     })->prio (1);
1664     }
1665    
1666 root 1.95 =head3 cf::client
1667    
1668     =over 4
1669    
1670     =item $client->send_drawinfo ($text, $flags)
1671    
1672     Sends a drawinfo packet to the client. Circumvents output buffering so
1673     should not be used under normal circumstances.
1674    
1675 root 1.70 =cut
1676    
1677 root 1.95 sub cf::client::send_drawinfo {
1678     my ($self, $text, $flags) = @_;
1679    
1680     utf8::encode $text;
1681     $self->send_packet (sprintf "drawinfo %d %s", $flags, $text);
1682     }
1683    
1684    
1685     =item $success = $client->query ($flags, "text", \&cb)
1686    
1687     Queues a query to the client, calling the given callback with
1688     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
1689     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
1690    
1691     Queries can fail, so check the return code. Or don't, as queries will become
1692     reliable at some point in the future.
1693    
1694     =cut
1695    
1696     sub cf::client::query {
1697     my ($self, $flags, $text, $cb) = @_;
1698    
1699     return unless $self->state == ST_PLAYING
1700     || $self->state == ST_SETUP
1701     || $self->state == ST_CUSTOM;
1702    
1703     $self->state (ST_CUSTOM);
1704    
1705     utf8::encode $text;
1706     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
1707    
1708     $self->send_packet ($self->{query_queue}[0][0])
1709     if @{ $self->{query_queue} } == 1;
1710     }
1711    
1712     cf::client->attach (
1713     on_reply => sub {
1714     my ($ns, $msg) = @_;
1715    
1716     # this weird shuffling is so that direct followup queries
1717     # get handled first
1718 root 1.128 my $queue = delete $ns->{query_queue}
1719 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
1720 root 1.95
1721     (shift @$queue)->[1]->($msg);
1722    
1723     push @{ $ns->{query_queue} }, @$queue;
1724    
1725     if (@{ $ns->{query_queue} } == @$queue) {
1726     if (@$queue) {
1727     $ns->send_packet ($ns->{query_queue}[0][0]);
1728     } else {
1729 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
1730 root 1.95 }
1731     }
1732     },
1733     );
1734    
1735 root 1.96 =item $client->coro (\&cb)
1736    
1737     Create a new coroutine, running the specified callback. The coroutine will
1738     be automatically cancelled when the client gets destroyed (e.g. on logout,
1739     or loss of connection).
1740    
1741     =cut
1742    
1743     sub cf::client::coro {
1744     my ($self, $cb) = @_;
1745    
1746 root 1.127 my $coro = &cf::async ($cb);
1747 root 1.103
1748     $coro->on_destroy (sub {
1749 root 1.96 delete $self->{_coro}{$coro+0};
1750 root 1.103 });
1751 root 1.96
1752     $self->{_coro}{$coro+0} = $coro;
1753 root 1.103
1754     $coro
1755 root 1.96 }
1756    
1757     cf::client->attach (
1758     on_destroy => sub {
1759     my ($ns) = @_;
1760    
1761 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
1762 root 1.96 },
1763     );
1764    
1765 root 1.95 =back
1766    
1767 root 1.70
1768     =head2 SAFE SCRIPTING
1769    
1770     Functions that provide a safe environment to compile and execute
1771     snippets of perl code without them endangering the safety of the server
1772     itself. Looping constructs, I/O operators and other built-in functionality
1773     is not available in the safe scripting environment, and the number of
1774 root 1.79 functions and methods that can be called is greatly reduced.
1775 root 1.70
1776     =cut
1777 root 1.23
1778 root 1.42 our $safe = new Safe "safe";
1779 root 1.23 our $safe_hole = new Safe::Hole;
1780    
1781     $SIG{FPE} = 'IGNORE';
1782    
1783     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
1784    
1785 root 1.25 # here we export the classes and methods available to script code
1786    
1787 root 1.70 =pod
1788    
1789     The following fucntions and emthods are available within a safe environment:
1790    
1791 elmex 1.91 cf::object contr pay_amount pay_player map
1792 root 1.70 cf::object::player player
1793     cf::player peaceful
1794 elmex 1.91 cf::map trigger
1795 root 1.70
1796     =cut
1797    
1798 root 1.25 for (
1799 elmex 1.91 ["cf::object" => qw(contr pay_amount pay_player map)],
1800 root 1.25 ["cf::object::player" => qw(player)],
1801     ["cf::player" => qw(peaceful)],
1802 elmex 1.91 ["cf::map" => qw(trigger)],
1803 root 1.25 ) {
1804     no strict 'refs';
1805     my ($pkg, @funs) = @$_;
1806 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
1807 root 1.25 for @funs;
1808     }
1809 root 1.23
1810 root 1.70 =over 4
1811    
1812     =item @retval = safe_eval $code, [var => value, ...]
1813    
1814     Compiled and executes the given perl code snippet. additional var/value
1815     pairs result in temporary local (my) scalar variables of the given name
1816     that are available in the code snippet. Example:
1817    
1818     my $five = safe_eval '$first + $second', first => 1, second => 4;
1819    
1820     =cut
1821    
1822 root 1.23 sub safe_eval($;@) {
1823     my ($code, %vars) = @_;
1824    
1825     my $qcode = $code;
1826     $qcode =~ s/"/‟/g; # not allowed in #line filenames
1827     $qcode =~ s/\n/\\n/g;
1828    
1829     local $_;
1830 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
1831 root 1.23
1832 root 1.42 my $eval =
1833 root 1.23 "do {\n"
1834     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
1835     . "#line 0 \"{$qcode}\"\n"
1836     . $code
1837     . "\n}"
1838 root 1.25 ;
1839    
1840     sub_generation_inc;
1841 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
1842 root 1.25 sub_generation_inc;
1843    
1844 root 1.42 if ($@) {
1845     warn "$@";
1846     warn "while executing safe code '$code'\n";
1847     warn "with arguments " . (join " ", %vars) . "\n";
1848     }
1849    
1850 root 1.25 wantarray ? @res : $res[0]
1851 root 1.23 }
1852    
1853 root 1.69 =item cf::register_script_function $function => $cb
1854    
1855     Register a function that can be called from within map/npc scripts. The
1856     function should be reasonably secure and should be put into a package name
1857     like the extension.
1858    
1859     Example: register a function that gets called whenever a map script calls
1860     C<rent::overview>, as used by the C<rent> extension.
1861    
1862     cf::register_script_function "rent::overview" => sub {
1863     ...
1864     };
1865    
1866     =cut
1867    
1868 root 1.23 sub register_script_function {
1869     my ($fun, $cb) = @_;
1870    
1871     no strict 'refs';
1872 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
1873 root 1.23 }
1874    
1875 root 1.70 =back
1876    
1877 root 1.71 =cut
1878    
1879 root 1.23 #############################################################################
1880 root 1.65
1881     =head2 EXTENSION DATABASE SUPPORT
1882    
1883     Crossfire maintains a very simple database for extension use. It can
1884     currently store anything that can be serialised using Storable, which
1885     excludes objects.
1886    
1887     The parameter C<$family> should best start with the name of the extension
1888     using it, it should be unique.
1889    
1890     =over 4
1891    
1892     =item $hashref = cf::db_get $family
1893    
1894     Return a hashref for use by the extension C<$family>, which can be
1895     modified. After modifications, you have to call C<cf::db_dirty> or
1896     C<cf::db_sync>.
1897    
1898     =item $value = cf::db_get $family => $key
1899    
1900     Returns a single value from the database
1901    
1902     =item cf::db_put $family => $hashref
1903    
1904     Stores the given family hashref into the database. Updates are delayed, if
1905     you want the data to be synced to disk immediately, use C<cf::db_sync>.
1906    
1907     =item cf::db_put $family => $key => $value
1908    
1909     Stores the given C<$value> in the family hash. Updates are delayed, if you
1910     want the data to be synced to disk immediately, use C<cf::db_sync>.
1911    
1912     =item cf::db_dirty
1913    
1914     Marks the database as dirty, to be updated at a later time.
1915    
1916     =item cf::db_sync
1917    
1918     Immediately write the database to disk I<if it is dirty>.
1919    
1920     =cut
1921    
1922 root 1.78 our $DB;
1923    
1924 root 1.65 {
1925 root 1.66 my $path = cf::localdir . "/database.pst";
1926 root 1.65
1927     sub db_load() {
1928 root 1.78 $DB = stat $path ? Storable::retrieve $path : { };
1929 root 1.65 }
1930    
1931     my $pid;
1932    
1933     sub db_save() {
1934     waitpid $pid, 0 if $pid;
1935 root 1.67 if (0 == ($pid = fork)) {
1936 root 1.78 $DB->{_meta}{version} = 1;
1937     Storable::nstore $DB, "$path~";
1938 root 1.65 rename "$path~", $path;
1939     cf::_exit 0 if defined $pid;
1940     }
1941     }
1942    
1943     my $dirty;
1944    
1945     sub db_sync() {
1946     db_save if $dirty;
1947     undef $dirty;
1948     }
1949    
1950 root 1.87 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, data => WF_AUTOCANCEL, cb => sub {
1951 root 1.65 db_sync;
1952     });
1953    
1954     sub db_dirty() {
1955     $dirty = 1;
1956     $idle->start;
1957     }
1958    
1959     sub db_get($;$) {
1960     @_ >= 2
1961 root 1.78 ? $DB->{$_[0]}{$_[1]}
1962     : ($DB->{$_[0]} ||= { })
1963 root 1.65 }
1964    
1965     sub db_put($$;$) {
1966     if (@_ >= 3) {
1967 root 1.78 $DB->{$_[0]}{$_[1]} = $_[2];
1968 root 1.65 } else {
1969 root 1.78 $DB->{$_[0]} = $_[1];
1970 root 1.65 }
1971     db_dirty;
1972     }
1973 root 1.67
1974 root 1.93 cf::global->attach (
1975     prio => 10000,
1976 root 1.67 on_cleanup => sub {
1977     db_sync;
1978     },
1979 root 1.93 );
1980 root 1.65 }
1981    
1982     #############################################################################
1983 root 1.34 # the server's main()
1984    
1985 root 1.73 sub cfg_load {
1986 root 1.72 open my $fh, "<:utf8", cf::confdir . "/config"
1987     or return;
1988    
1989     local $/;
1990     *CFG = YAML::Syck::Load <$fh>;
1991     }
1992    
1993 root 1.39 sub main {
1994 root 1.108 # we must not ever block the main coroutine
1995     local $Coro::idle = sub {
1996 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
1997 root 1.108 (Coro::unblock_sub {
1998     Event::one_event;
1999     })->();
2000     };
2001    
2002 root 1.73 cfg_load;
2003 root 1.65 db_load;
2004 root 1.61 load_extensions;
2005 root 1.34 Event::loop;
2006     }
2007    
2008     #############################################################################
2009 root 1.22 # initialisation
2010    
2011 root 1.111 sub reload() {
2012 root 1.106 # can/must only be called in main
2013     if ($Coro::current != $Coro::main) {
2014     warn "can only reload from main coroutine\n";
2015     return;
2016     }
2017    
2018 root 1.103 warn "reloading...";
2019    
2020 root 1.106 local $FREEZE = 1;
2021     cf::emergency_save;
2022    
2023 root 1.103 eval {
2024 root 1.106 # if anything goes wrong in here, we should simply crash as we already saved
2025 root 1.65
2026     # cancel all watchers
2027 root 1.87 for (Event::all_watchers) {
2028     $_->cancel if $_->data & WF_AUTOCANCEL;
2029     }
2030 root 1.65
2031 root 1.103 # cancel all extension coros
2032     $_->cancel for values %EXT_CORO;
2033     %EXT_CORO = ();
2034    
2035 root 1.65 # unload all extensions
2036     for (@exts) {
2037 root 1.103 warn "unloading <$_>";
2038 root 1.65 unload_extension $_;
2039     }
2040    
2041     # unload all modules loaded from $LIBDIR
2042     while (my ($k, $v) = each %INC) {
2043     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
2044    
2045 root 1.103 warn "removing <$k>";
2046 root 1.65 delete $INC{$k};
2047    
2048     $k =~ s/\.pm$//;
2049     $k =~ s/\//::/g;
2050    
2051     if (my $cb = $k->can ("unload_module")) {
2052     $cb->();
2053     }
2054    
2055     Symbol::delete_package $k;
2056     }
2057    
2058     # sync database to disk
2059     cf::db_sync;
2060 root 1.103 IO::AIO::flush;
2061 root 1.65
2062     # get rid of safe::, as good as possible
2063     Symbol::delete_package "safe::$_"
2064 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
2065 root 1.65
2066     # remove register_script_function callbacks
2067     # TODO
2068    
2069     # unload cf.pm "a bit"
2070     delete $INC{"cf.pm"};
2071    
2072     # don't, removes xs symbols, too,
2073     # and global variables created in xs
2074     #Symbol::delete_package __PACKAGE__;
2075    
2076     # reload cf.pm
2077 root 1.103 warn "reloading cf.pm";
2078 root 1.65 require cf;
2079 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
2080    
2081 root 1.73 # load config and database again
2082     cf::cfg_load;
2083 root 1.65 cf::db_load;
2084    
2085     # load extensions
2086 root 1.103 warn "load extensions";
2087 root 1.65 cf::load_extensions;
2088    
2089     # reattach attachments to objects
2090 root 1.103 warn "reattach";
2091 root 1.65 _global_reattach;
2092     };
2093    
2094 root 1.106 if ($@) {
2095     warn $@;
2096     warn "error while reloading, exiting.";
2097     exit 1;
2098     }
2099    
2100     warn "reloaded successfully";
2101 root 1.65 };
2102    
2103 root 1.108 #############################################################################
2104    
2105     unless ($LINK_MAP) {
2106     $LINK_MAP = cf::map::new;
2107    
2108     $LINK_MAP->width (41);
2109     $LINK_MAP->height (41);
2110     $LINK_MAP->alloc;
2111     $LINK_MAP->path ("{link}");
2112     $LINK_MAP->{path} = bless { path => "{link}" }, "cf::path";
2113     $LINK_MAP->in_memory (MAP_IN_MEMORY);
2114 root 1.110
2115     # dirty hack because... archetypes are not yet loaded
2116     Event->timer (
2117     after => 2,
2118     cb => sub {
2119     $_[0]->w->cancel;
2120    
2121     # provide some exits "home"
2122     my $exit = cf::object::new "exit";
2123    
2124     $exit->slaying ($EMERGENCY_POSITION->[0]);
2125     $exit->stats->hp ($EMERGENCY_POSITION->[1]);
2126     $exit->stats->sp ($EMERGENCY_POSITION->[2]);
2127    
2128     $LINK_MAP->insert ($exit->clone, 19, 19);
2129     $LINK_MAP->insert ($exit->clone, 19, 20);
2130     $LINK_MAP->insert ($exit->clone, 19, 21);
2131     $LINK_MAP->insert ($exit->clone, 20, 19);
2132     $LINK_MAP->insert ($exit->clone, 20, 21);
2133     $LINK_MAP->insert ($exit->clone, 21, 19);
2134     $LINK_MAP->insert ($exit->clone, 21, 20);
2135     $LINK_MAP->insert ($exit->clone, 21, 21);
2136    
2137     $exit->destroy;
2138     });
2139    
2140     $LINK_MAP->{deny_save} = 1;
2141     $LINK_MAP->{deny_reset} = 1;
2142    
2143     $cf::MAP{$LINK_MAP->path} = $LINK_MAP;
2144 root 1.108 }
2145    
2146 root 1.85 register "<global>", __PACKAGE__;
2147    
2148 root 1.111 register_command "reload" => sub {
2149 root 1.65 my ($who, $arg) = @_;
2150    
2151     if ($who->flag (FLAG_WIZ)) {
2152 root 1.107 $who->message ("start of reload.");
2153 root 1.111 reload;
2154 root 1.107 $who->message ("end of reload.");
2155 root 1.65 }
2156     };
2157    
2158 root 1.27 unshift @INC, $LIBDIR;
2159 root 1.17
2160 root 1.35 $TICK_WATCHER = Event->timer (
2161 root 1.104 reentrant => 0,
2162     prio => 0,
2163     at => $NEXT_TICK || $TICK,
2164     data => WF_AUTOCANCEL,
2165     cb => sub {
2166 root 1.103 unless ($FREEZE) {
2167     cf::server_tick; # one server iteration
2168     $RUNTIME += $TICK;
2169     }
2170 root 1.35
2171     $NEXT_TICK += $TICK;
2172    
2173 root 1.78 # if we are delayed by four ticks or more, skip them all
2174 root 1.103 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4;
2175 root 1.35
2176     $TICK_WATCHER->at ($NEXT_TICK);
2177     $TICK_WATCHER->start;
2178     },
2179     );
2180    
2181 root 1.80 IO::AIO::max_poll_time $TICK * 0.2;
2182 root 1.77
2183 root 1.108 Event->io (
2184     fd => IO::AIO::poll_fileno,
2185     poll => 'r',
2186     prio => 5,
2187     data => WF_AUTOCANCEL,
2188     cb => \&IO::AIO::poll_cb,
2189     );
2190    
2191     Event->timer (
2192     data => WF_AUTOCANCEL,
2193     after => 0,
2194     interval => 10,
2195     cb => sub {
2196     (Coro::unblock_sub {
2197     write_runtime
2198     or warn "ERROR: unable to write runtime file: $!";
2199     })->();
2200     },
2201     );
2202 root 1.103
2203 root 1.125 END { cf::emergency_save }
2204    
2205 root 1.1 1
2206