ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.131
Committed: Thu Jan 4 00:53:54 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.130: +11 -2 lines
Log Message:
- implement mlockall option
- expanded config file documentation
- make perl-weapon boni mandatory

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