ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.132
Committed: Thu Jan 4 01:35:56 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.131: +5 -1 lines
Log Message:
tuning, and hopefully apply last access time more correctly

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