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