ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.135
Committed: Thu Jan 4 20:29:46 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.134: +6 -25 lines
Log Message:
- use new Coro::guard
- removed cf::guard
- better map loading error messages
- more robust map header parsing

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 root 1.108 our $RANDOM_MAPS = cf::localdir . "/random";
55     our %EXT_CORO;
56 root 1.103
57     binmode STDOUT;
58     binmode STDERR;
59    
60     # read virtual server time, if available
61     unless ($RUNTIME || !-e cf::localdir . "/runtime") {
62     open my $fh, "<", cf::localdir . "/runtime"
63     or die "unable to read runtime file: $!";
64     $RUNTIME = <$fh> + 0.;
65     }
66    
67     mkdir cf::localdir;
68     mkdir cf::localdir . "/" . cf::playerdir;
69     mkdir cf::localdir . "/" . cf::tmpdir;
70     mkdir cf::localdir . "/" . cf::uniquedir;
71 root 1.108 mkdir $RANDOM_MAPS;
72 root 1.103
73 root 1.108 # a special map that is always available
74     our $LINK_MAP;
75 root 1.131 our $EMERGENCY_POSITION;
76 root 1.110
77 root 1.70 #############################################################################
78    
79     =head2 GLOBAL VARIABLES
80    
81     =over 4
82    
83 root 1.83 =item $cf::UPTIME
84    
85     The timestamp of the server start (so not actually an uptime).
86    
87 root 1.103 =item $cf::RUNTIME
88    
89     The time this server has run, starts at 0 and is increased by $cf::TICK on
90     every server tick.
91    
92 root 1.70 =item $cf::LIBDIR
93    
94     The perl library directory, where extensions and cf-specific modules can
95     be found. It will be added to C<@INC> automatically.
96    
97 root 1.103 =item $cf::NOW
98    
99     The time of the last (current) server tick.
100    
101 root 1.70 =item $cf::TICK
102    
103     The interval between server ticks, in seconds.
104    
105     =item %cf::CFG
106    
107     Configuration for the server, loaded from C</etc/crossfire/config>, or
108     from wherever your confdir points to.
109    
110     =back
111    
112     =cut
113    
114 root 1.1 BEGIN {
115     *CORE::GLOBAL::warn = sub {
116     my $msg = join "", @_;
117 root 1.103 utf8::encode $msg;
118    
119 root 1.1 $msg .= "\n"
120     unless $msg =~ /\n$/;
121    
122     LOG llevError, "cfperl: $msg";
123     };
124     }
125    
126 root 1.93 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
127     @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
128     @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
129     @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
130     @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
131 root 1.41 @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object';
132 root 1.25
133 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
134 root 1.25 # within the Safe compartment.
135 root 1.86 for my $pkg (qw(
136 root 1.100 cf::global cf::attachable
137 root 1.86 cf::object cf::object::player
138 root 1.89 cf::client cf::player
139 root 1.86 cf::arch cf::living
140     cf::map cf::party cf::region
141     )) {
142 root 1.25 no strict 'refs';
143 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
144 root 1.25 }
145 root 1.1
146 root 1.18 $Event::DIED = sub {
147     warn "error in event callback: @_";
148     };
149    
150 root 1.5 my %ext_pkg;
151 root 1.1 my @exts;
152     my @hook;
153    
154 root 1.70 =head2 UTILITY FUNCTIONS
155    
156     =over 4
157    
158     =cut
159 root 1.44
160 root 1.45 use JSON::Syck (); # TODO# replace by JSON::PC once working
161 root 1.44
162 root 1.70 =item $ref = cf::from_json $json
163    
164     Converts a JSON string into the corresponding perl data structure.
165    
166     =cut
167    
168 root 1.45 sub from_json($) {
169     $JSON::Syck::ImplicitUnicode = 1; # work around JSON::Syck bugs
170     JSON::Syck::Load $_[0]
171 root 1.44 }
172    
173 root 1.70 =item $json = cf::to_json $ref
174    
175     Converts a perl data structure into its JSON representation.
176    
177     =cut
178    
179 root 1.45 sub to_json($) {
180     $JSON::Syck::ImplicitUnicode = 0; # work around JSON::Syck bugs
181     JSON::Syck::Dump $_[0]
182 root 1.44 }
183    
184 root 1.120 =item cf::lock_wait $string
185    
186     Wait until the given lock is available. See cf::lock_acquire.
187    
188     =item my $lock = cf::lock_acquire $string
189    
190     Wait until the given lock is available and then acquires it and returns
191 root 1.135 a Coro::guard object. If the guard object gets destroyed (goes out of scope,
192 root 1.120 for example when the coroutine gets canceled), the lock is automatically
193     returned.
194    
195 root 1.133 Lock names should begin with a unique identifier (for example, cf::map::find
196     uses map_find and cf::map::load uses map_load).
197 root 1.120
198     =cut
199    
200     our %LOCK;
201    
202     sub lock_wait($) {
203     my ($key) = @_;
204    
205     # wait for lock, if any
206     while ($LOCK{$key}) {
207     push @{ $LOCK{$key} }, $Coro::current;
208     Coro::schedule;
209     }
210     }
211    
212     sub lock_acquire($) {
213     my ($key) = @_;
214    
215     # wait, to be sure we are not locked
216     lock_wait $key;
217    
218     $LOCK{$key} = [];
219    
220 root 1.135 Coro::guard {
221 root 1.120 # wake up all waiters, to be on the safe side
222     $_->ready for @{ delete $LOCK{$key} };
223     }
224     }
225    
226 root 1.127 =item cf::async { BLOCK }
227    
228     Like C<Coro::async>, but runs the given BLOCK in an eval and only logs the
229     error instead of exiting the server in case of a problem.
230    
231     =cut
232    
233     sub async(&) {
234     my ($cb) = @_;
235    
236     Coro::async {
237     eval { $cb->() };
238     warn $@ if $@;
239     }
240     }
241    
242 root 1.133 sub freeze_mainloop {
243     return unless $TICK_WATCHER->is_active;
244    
245 root 1.135 my $guard = Coro::guard { $TICK_WATCHER->start };
246 root 1.133 $TICK_WATCHER->stop;
247     $guard
248     }
249    
250 root 1.106 =item cf::sync_job { BLOCK }
251    
252     The design of crossfire+ requires that the main coro ($Coro::main) is
253     always able to handle events or runnable, as crossfire+ is only partly
254     reentrant. Thus "blocking" it by e.g. waiting for I/O is not acceptable.
255    
256     If it must be done, put the blocking parts into C<sync_job>. This will run
257     the given BLOCK in another coroutine while waiting for the result. The
258     server will be frozen during this time, so the block should either finish
259     fast or be very important.
260    
261     =cut
262    
263 root 1.105 sub sync_job(&) {
264     my ($job) = @_;
265    
266     if ($Coro::current == $Coro::main) {
267 root 1.112 # this is the main coro, too bad, we have to block
268     # till the operation succeeds, freezing the server :/
269    
270 root 1.110 # TODO: use suspend/resume instead
271 root 1.112 # (but this is cancel-safe)
272 root 1.133 my $freeze_guard = freeze_mainloop;
273 root 1.112
274     my $busy = 1;
275     my @res;
276    
277     (Coro::async {
278     @res = eval { $job->() };
279     warn $@ if $@;
280     undef $busy;
281     })->prio (Coro::PRIO_MAX);
282    
283 root 1.105 while ($busy) {
284     Coro::cede_notself;
285     Event::one_event unless Coro::nready;
286     }
287 root 1.112
288     wantarray ? @res : $res[0]
289 root 1.105 } else {
290 root 1.112 # we are in another coroutine, how wonderful, everything just works
291    
292     $job->()
293 root 1.105 }
294     }
295    
296 root 1.103 =item $coro = cf::coro { BLOCK }
297    
298     Creates and returns a new coro. This coro is automcatially being canceled
299     when the extension calling this is being unloaded.
300    
301     =cut
302    
303     sub coro(&) {
304     my $cb = shift;
305    
306 root 1.127 my $coro = &cf::async ($cb);
307 root 1.103
308     $coro->on_destroy (sub {
309     delete $EXT_CORO{$coro+0};
310     });
311     $EXT_CORO{$coro+0} = $coro;
312    
313     $coro
314     }
315    
316 root 1.108 sub write_runtime {
317     my $runtime = cf::localdir . "/runtime";
318    
319     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
320     or return;
321    
322 root 1.112 my $value = $cf::RUNTIME + 1 + 10; # 10 is the runtime save interval, for a monotonic clock
323 root 1.108 (aio_write $fh, 0, (length $value), $value, 0) <= 0
324     and return;
325    
326     aio_fsync $fh
327     and return;
328    
329     close $fh
330     or return;
331    
332     aio_rename "$runtime~", $runtime
333     and return;
334    
335     1
336     }
337    
338 root 1.70 =back
339    
340 root 1.71 =cut
341    
342 root 1.44 #############################################################################
343 root 1.39
344 root 1.108 package cf::path;
345    
346     sub new {
347     my ($class, $path, $base) = @_;
348    
349 root 1.110 $path = $path->as_string if ref $path;
350    
351 root 1.108 my $self = bless { }, $class;
352    
353 root 1.114 # {... are special paths that are not touched
354     # ?xxx/... are special absolute paths
355     # ?random/... random maps
356     # /! non-realised random map exit
357     # /... normal maps
358     # ~/... per-player maps without a specific player (DO NOT USE)
359     # ~user/... per-player map of a specific user
360    
361     if ($path =~ /^{/) {
362     # fine as it is
363     } elsif ($path =~ s{^\?random/}{}) {
364 root 1.109 Coro::AIO::aio_load "$cf::RANDOM_MAPS/$path.meta", my $data;
365     $self->{random} = cf::from_json $data;
366 root 1.108 } else {
367     if ($path =~ s{^~([^/]+)?}{}) {
368     $self->{user_rel} = 1;
369    
370     if (defined $1) {
371     $self->{user} = $1;
372     } elsif ($base =~ m{^~([^/]+)/}) {
373     $self->{user} = $1;
374     } else {
375     warn "cannot resolve user-relative path without user <$path,$base>\n";
376     }
377     } elsif ($path =~ /^\//) {
378     # already absolute
379     } else {
380     $base =~ s{[^/]+/?$}{};
381     return $class->new ("$base/$path");
382     }
383    
384     for ($path) {
385     redo if s{/\.?/}{/};
386     redo if s{/[^/]+/\.\./}{/};
387     }
388     }
389    
390     $self->{path} = $path;
391    
392     $self
393     }
394    
395     # the name / primary key / in-game path
396     sub as_string {
397     my ($self) = @_;
398    
399     $self->{user_rel} ? "~$self->{user}$self->{path}"
400     : $self->{random} ? "?random/$self->{path}"
401     : $self->{path}
402     }
403    
404     # the displayed name, this is a one way mapping
405     sub visible_name {
406     my ($self) = @_;
407    
408 root 1.109 # if (my $rmp = $self->{random}) {
409     # # todo: be more intelligent about this
410     # "?random/$rmp->{origin_map}+$rmp->{origin_x}+$rmp->{origin_y}/$rmp->{dungeon_level}"
411     # } else {
412     $self->as_string
413     # }
414 root 1.108 }
415    
416     # escape the /'s in the path
417     sub _escaped_path {
418     # ∕ is U+2215
419     (my $path = $_[0]{path}) =~ s/\//∕/g;
420     $path
421     }
422    
423     # the original (read-only) location
424     sub load_path {
425     my ($self) = @_;
426    
427     sprintf "%s/%s/%s", cf::datadir, cf::mapdir, $self->{path}
428     }
429    
430     # the temporary/swap location
431     sub save_path {
432     my ($self) = @_;
433    
434     $self->{user_rel} ? sprintf "%s/%s/%s/%s", cf::localdir, cf::playerdir, $self->{user}, $self->_escaped_path
435 root 1.109 : $self->{random} ? sprintf "%s/%s", $RANDOM_MAPS, $self->{path}
436 root 1.108 : sprintf "%s/%s/%s", cf::localdir, cf::tmpdir, $self->_escaped_path
437     }
438    
439     # the unique path, might be eq to save_path
440     sub uniq_path {
441     my ($self) = @_;
442    
443     $self->{user_rel} || $self->{random}
444     ? undef
445     : sprintf "%s/%s/%s", cf::localdir, cf::uniquedir, $self->_escaped_path
446     }
447    
448     # return random map parameters, or undef
449     sub random_map_params {
450     my ($self) = @_;
451    
452     $self->{random}
453     }
454    
455     # this is somewhat ugly, but style maps do need special treatment
456     sub is_style_map {
457     $_[0]{path} =~ m{^/styles/}
458     }
459    
460     package cf;
461    
462     #############################################################################
463    
464 root 1.93 =head2 ATTACHABLE OBJECTS
465    
466 root 1.94 Many objects in crossfire are so-called attachable objects. That means you can
467     attach callbacks/event handlers (a collection of which is called an "attachment")
468     to it. All such attachable objects support the following methods.
469    
470     In the following description, CLASS can be any of C<global>, C<object>
471     C<player>, C<client> or C<map> (i.e. the attachable objects in
472     crossfire+).
473 root 1.55
474     =over 4
475    
476 root 1.94 =item $attachable->attach ($attachment, key => $value...)
477    
478     =item $attachable->detach ($attachment)
479    
480     Attach/detach a pre-registered attachment to a specific object and give it
481     the specified key/value pairs as arguments.
482    
483     Example, attach a minesweeper attachment to the given object, making it a
484     10x10 minesweeper game:
485 root 1.46
486 root 1.94 $obj->attach (minesweeper => width => 10, height => 10);
487 root 1.53
488 root 1.93 =item $bool = $attachable->attached ($name)
489 root 1.46
490 root 1.93 Checks wether the named attachment is currently attached to the object.
491 root 1.46
492 root 1.94 =item cf::CLASS->attach ...
493 root 1.46
494 root 1.94 =item cf::CLASS->detach ...
495 root 1.92
496 root 1.94 Define an anonymous attachment and attach it to all objects of the given
497     CLASS. See the next function for an explanation of its arguments.
498 root 1.92
499 root 1.93 You can attach to global events by using the C<cf::global> class.
500 root 1.92
501 root 1.94 Example, log all player logins:
502    
503     cf::player->attach (
504     on_login => sub {
505     my ($pl) = @_;
506     ...
507     },
508     );
509    
510     Example, attach to the jeweler skill:
511    
512     cf::object->attach (
513     type => cf::SKILL,
514     subtype => cf::SK_JEWELER,
515     on_use_skill => sub {
516     my ($sk, $ob, $part, $dir, $msg) = @_;
517     ...
518     },
519     );
520    
521     =item cf::CLASS::attachment $name, ...
522    
523     Register an attachment by C<$name> through which attachable objects of the
524     given CLASS can refer to this attachment.
525    
526     Some classes such as crossfire maps and objects can specify attachments
527     that are attached at load/instantiate time, thus the need for a name.
528    
529     These calls expect any number of the following handler/hook descriptions:
530 root 1.46
531     =over 4
532    
533     =item prio => $number
534    
535     Set the priority for all following handlers/hooks (unless overwritten
536     by another C<prio> setting). Lower priority handlers get executed
537     earlier. The default priority is C<0>, and many built-in handlers are
538     registered at priority C<-1000>, so lower priorities should not be used
539     unless you know what you are doing.
540    
541 root 1.93 =item type => $type
542    
543     (Only for C<< cf::object->attach >> calls), limits the attachment to the
544     given type of objects only (the additional parameter C<subtype> can be
545     used to further limit to the given subtype).
546    
547 root 1.46 =item on_I<event> => \&cb
548    
549     Call the given code reference whenever the named event happens (event is
550     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
551     handlers are recognised generally depends on the type of object these
552     handlers attach to).
553    
554     See F<include/eventinc.h> for the full list of events supported, and their
555     class.
556    
557     =item package => package::
558    
559     Look for sub functions of the name C<< on_I<event> >> in the given
560     package and register them. Only handlers for eevents supported by the
561     object/class are recognised.
562    
563     =back
564    
565 root 1.94 Example, define an attachment called "sockpuppet" that calls the given
566     event handler when a monster attacks:
567    
568     cf::object::attachment sockpuppet =>
569     on_skill_attack => sub {
570     my ($self, $victim) = @_;
571     ...
572     }
573     }
574    
575 root 1.96 =item $attachable->valid
576    
577     Just because you have a perl object does not mean that the corresponding
578     C-level object still exists. If you try to access an object that has no
579     valid C counterpart anymore you get an exception at runtime. This method
580     can be used to test for existence of the C object part without causing an
581     exception.
582    
583 root 1.39 =cut
584    
585 root 1.40 # the following variables are defined in .xs and must not be re-created
586 root 1.100 our @CB_GLOBAL = (); # registry for all global events
587     our @CB_ATTACHABLE = (); # registry for all attachables
588     our @CB_OBJECT = (); # all objects (should not be used except in emergency)
589     our @CB_PLAYER = ();
590     our @CB_CLIENT = ();
591     our @CB_TYPE = (); # registry for type (cf-object class) based events
592     our @CB_MAP = ();
593 root 1.39
594 root 1.45 my %attachment;
595    
596 root 1.93 sub _attach_cb($$$$) {
597     my ($registry, $event, $prio, $cb) = @_;
598 root 1.39
599     use sort 'stable';
600    
601     $cb = [$prio, $cb];
602    
603     @{$registry->[$event]} = sort
604     { $a->[0] cmp $b->[0] }
605     @{$registry->[$event] || []}, $cb;
606     }
607    
608 root 1.100 # hack
609     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
610    
611 root 1.39 # attach handles attaching event callbacks
612     # the only thing the caller has to do is pass the correct
613     # registry (== where the callback attaches to).
614 root 1.93 sub _attach {
615 root 1.45 my ($registry, $klass, @arg) = @_;
616 root 1.39
617 root 1.93 my $object_type;
618 root 1.39 my $prio = 0;
619     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
620    
621 root 1.100 #TODO: get rid of this hack
622     if ($attachable_klass{$klass}) {
623     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
624     }
625    
626 root 1.45 while (@arg) {
627     my $type = shift @arg;
628 root 1.39
629     if ($type eq "prio") {
630 root 1.45 $prio = shift @arg;
631 root 1.39
632 root 1.93 } elsif ($type eq "type") {
633     $object_type = shift @arg;
634     $registry = $CB_TYPE[$object_type] ||= [];
635    
636     } elsif ($type eq "subtype") {
637     defined $object_type or Carp::croak "subtype specified without type";
638     my $object_subtype = shift @arg;
639     $registry = $CB_TYPE[$object_type + $object_subtype * NUM_SUBTYPES] ||= [];
640    
641 root 1.39 } elsif ($type eq "package") {
642 root 1.45 my $pkg = shift @arg;
643 root 1.39
644     while (my ($name, $id) = each %cb_id) {
645     if (my $cb = $pkg->can ($name)) {
646 root 1.93 _attach_cb $registry, $id, $prio, $cb;
647 root 1.39 }
648     }
649    
650     } elsif (exists $cb_id{$type}) {
651 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
652 root 1.39
653     } elsif (ref $type) {
654     warn "attaching objects not supported, ignoring.\n";
655    
656     } else {
657 root 1.45 shift @arg;
658 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
659     }
660     }
661     }
662    
663 root 1.93 sub _object_attach {
664 root 1.48 my ($obj, $name, %arg) = @_;
665 root 1.46
666 root 1.55 return if exists $obj->{_attachment}{$name};
667    
668 root 1.46 if (my $attach = $attachment{$name}) {
669     my $registry = $obj->registry;
670    
671 root 1.47 for (@$attach) {
672     my ($klass, @attach) = @$_;
673 root 1.93 _attach $registry, $klass, @attach;
674 root 1.47 }
675 root 1.46
676 root 1.48 $obj->{$name} = \%arg;
677 root 1.46 } else {
678     warn "object uses attachment '$name' that is not available, postponing.\n";
679     }
680    
681 root 1.50 $obj->{_attachment}{$name} = undef;
682 root 1.46 }
683    
684 root 1.93 sub cf::attachable::attach {
685     if (ref $_[0]) {
686     _object_attach @_;
687     } else {
688     _attach shift->_attach_registry, @_;
689     }
690 root 1.55 };
691 root 1.46
692 root 1.54 # all those should be optimised
693 root 1.93 sub cf::attachable::detach {
694 root 1.54 my ($obj, $name) = @_;
695 root 1.46
696 root 1.93 if (ref $obj) {
697     delete $obj->{_attachment}{$name};
698     reattach ($obj);
699     } else {
700     Carp::croak "cannot, currently, detach class attachments";
701     }
702 root 1.55 };
703    
704 root 1.93 sub cf::attachable::attached {
705 root 1.55 my ($obj, $name) = @_;
706    
707     exists $obj->{_attachment}{$name}
708 root 1.39 }
709    
710 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
711 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
712     sub cf::\L$klass\E::_attach_registry {
713     (\\\@CB_$klass, KLASS_$klass)
714     }
715 root 1.45
716 root 1.93 sub cf::\L$klass\E::attachment {
717     my \$name = shift;
718 root 1.39
719 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
720     }
721     ";
722     die if $@;
723 root 1.52 }
724    
725 root 1.39 our $override;
726 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
727 root 1.39
728 root 1.45 sub override {
729     $override = 1;
730     @invoke_results = ();
731 root 1.39 }
732    
733 root 1.45 sub do_invoke {
734 root 1.39 my $event = shift;
735 root 1.40 my $callbacks = shift;
736 root 1.39
737 root 1.45 @invoke_results = ();
738    
739 root 1.39 local $override;
740    
741 root 1.40 for (@$callbacks) {
742 root 1.39 eval { &{$_->[1]} };
743    
744     if ($@) {
745     warn "$@";
746 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
747 root 1.39 override;
748     }
749    
750     return 1 if $override;
751     }
752    
753     0
754     }
755    
756 root 1.96 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
757 root 1.55
758 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
759 root 1.55
760 root 1.96 Generate an object-specific event with the given arguments.
761 root 1.55
762 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
763 root 1.55 removed in future versions), and there is no public API to access override
764     results (if you must, access C<@cf::invoke_results> directly).
765    
766     =back
767    
768 root 1.71 =cut
769    
770 root 1.70 #############################################################################
771 root 1.45 # object support
772    
773 root 1.102 sub reattach {
774     # basically do the same as instantiate, without calling instantiate
775     my ($obj) = @_;
776    
777     my $registry = $obj->registry;
778    
779     @$registry = ();
780    
781     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
782    
783     for my $name (keys %{ $obj->{_attachment} || {} }) {
784     if (my $attach = $attachment{$name}) {
785     for (@$attach) {
786     my ($klass, @attach) = @$_;
787     _attach $registry, $klass, @attach;
788     }
789     } else {
790     warn "object uses attachment '$name' that is not available, postponing.\n";
791     }
792     }
793     }
794    
795 root 1.100 cf::attachable->attach (
796     prio => -1000000,
797     on_instantiate => sub {
798     my ($obj, $data) = @_;
799 root 1.45
800 root 1.100 $data = from_json $data;
801 root 1.45
802 root 1.100 for (@$data) {
803     my ($name, $args) = @$_;
804 root 1.49
805 root 1.100 $obj->attach ($name, %{$args || {} });
806     }
807     },
808 root 1.102 on_reattach => \&reattach,
809 root 1.100 on_clone => sub {
810     my ($src, $dst) = @_;
811    
812     @{$dst->registry} = @{$src->registry};
813    
814     %$dst = %$src;
815    
816     %{$dst->{_attachment}} = %{$src->{_attachment}}
817     if exists $src->{_attachment};
818     },
819     );
820 root 1.45
821 root 1.46 sub object_freezer_save {
822 root 1.59 my ($filename, $rdata, $objs) = @_;
823 root 1.46
824 root 1.105 sync_job {
825     if (length $$rdata) {
826     warn sprintf "saving %s (%d,%d)\n",
827     $filename, length $$rdata, scalar @$objs;
828 root 1.60
829 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
830 root 1.60 chmod SAVE_MODE, $fh;
831 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
832     aio_fsync $fh;
833 root 1.60 close $fh;
834 root 1.105
835     if (@$objs) {
836     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
837     chmod SAVE_MODE, $fh;
838     my $data = Storable::nfreeze { version => 1, objs => $objs };
839     aio_write $fh, 0, (length $data), $data, 0;
840     aio_fsync $fh;
841     close $fh;
842     aio_rename "$filename.pst~", "$filename.pst";
843     }
844     } else {
845     aio_unlink "$filename.pst";
846     }
847    
848     aio_rename "$filename~", $filename;
849 root 1.60 } else {
850 root 1.105 warn "FATAL: $filename~: $!\n";
851 root 1.60 }
852 root 1.59 } else {
853 root 1.105 aio_unlink $filename;
854     aio_unlink "$filename.pst";
855 root 1.59 }
856 root 1.45 }
857     }
858    
859 root 1.80 sub object_freezer_as_string {
860     my ($rdata, $objs) = @_;
861    
862     use Data::Dumper;
863    
864 root 1.81 $$rdata . Dumper $objs
865 root 1.80 }
866    
867 root 1.46 sub object_thawer_load {
868     my ($filename) = @_;
869    
870 root 1.105 my ($data, $av);
871 root 1.61
872 root 1.105 (aio_load $filename, $data) >= 0
873     or return;
874 root 1.61
875 root 1.105 unless (aio_stat "$filename.pst") {
876     (aio_load "$filename.pst", $av) >= 0
877     or return;
878 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
879 root 1.61 }
880 root 1.45
881 root 1.118 warn sprintf "loading %s (%d)\n",
882     $filename, length $data, scalar @{$av || []};#d#
883 root 1.105 return ($data, $av);
884 root 1.45 }
885    
886     #############################################################################
887 root 1.85 # command handling &c
888 root 1.39
889 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
890 root 1.1
891 root 1.85 Register a callback for execution when the client sends the user command
892     $name.
893 root 1.5
894 root 1.85 =cut
895 root 1.5
896 root 1.85 sub register_command {
897     my ($name, $cb) = @_;
898 root 1.5
899 root 1.85 my $caller = caller;
900     #warn "registering command '$name/$time' to '$caller'";
901 root 1.1
902 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
903 root 1.1 }
904    
905 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
906 root 1.1
907 root 1.85 Register a callbackf ro execution when the client sends an extcmd packet.
908 root 1.1
909 root 1.85 If the callback returns something, it is sent back as if reply was being
910     called.
911 root 1.1
912 root 1.85 =cut
913 root 1.1
914 root 1.16 sub register_extcmd {
915     my ($name, $cb) = @_;
916    
917     my $caller = caller;
918     #warn "registering extcmd '$name' to '$caller'";
919    
920 root 1.85 $EXTCMD{$name} = [$cb, $caller];
921 root 1.16 }
922    
923 root 1.93 cf::player->attach (
924 root 1.85 on_command => sub {
925     my ($pl, $name, $params) = @_;
926    
927     my $cb = $COMMAND{$name}
928     or return;
929    
930     for my $cmd (@$cb) {
931     $cmd->[1]->($pl->ob, $params);
932     }
933    
934     cf::override;
935     },
936     on_extcmd => sub {
937     my ($pl, $buf) = @_;
938    
939     my $msg = eval { from_json $buf };
940    
941     if (ref $msg) {
942     if (my $cb = $EXTCMD{$msg->{msgtype}}) {
943     if (my %reply = $cb->[0]->($pl, $msg)) {
944     $pl->ext_reply ($msg->{msgid}, %reply);
945     }
946     }
947     } else {
948     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
949     }
950    
951     cf::override;
952     },
953 root 1.93 );
954 root 1.85
955 root 1.6 sub register {
956     my ($base, $pkg) = @_;
957    
958 root 1.45 #TODO
959 root 1.6 }
960    
961 root 1.1 sub load_extension {
962     my ($path) = @_;
963    
964     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
965 root 1.5 my $base = $1;
966 root 1.1 my $pkg = $1;
967     $pkg =~ s/[^[:word:]]/_/g;
968 root 1.41 $pkg = "ext::$pkg";
969 root 1.1
970     warn "loading '$path' into '$pkg'\n";
971    
972     open my $fh, "<:utf8", $path
973     or die "$path: $!";
974    
975     my $source =
976     "package $pkg; use strict; use utf8;\n"
977     . "#line 1 \"$path\"\n{\n"
978     . (do { local $/; <$fh> })
979     . "\n};\n1";
980    
981     eval $source
982 root 1.82 or die $@ ? "$path: $@\n"
983     : "extension disabled.\n";
984 root 1.1
985     push @exts, $pkg;
986 root 1.5 $ext_pkg{$base} = $pkg;
987 root 1.1
988 root 1.6 # no strict 'refs';
989 root 1.23 # @{"$pkg\::ISA"} = ext::;
990 root 1.1
991 root 1.6 register $base, $pkg;
992 root 1.1 }
993    
994     sub unload_extension {
995     my ($pkg) = @_;
996    
997     warn "removing extension $pkg\n";
998    
999     # remove hooks
1000 root 1.45 #TODO
1001     # for my $idx (0 .. $#PLUGIN_EVENT) {
1002     # delete $hook[$idx]{$pkg};
1003     # }
1004 root 1.1
1005     # remove commands
1006 root 1.85 for my $name (keys %COMMAND) {
1007     my @cb = grep $_->[0] ne $pkg, @{ $COMMAND{$name} };
1008 root 1.1
1009     if (@cb) {
1010 root 1.85 $COMMAND{$name} = \@cb;
1011 root 1.1 } else {
1012 root 1.85 delete $COMMAND{$name};
1013 root 1.1 }
1014     }
1015    
1016 root 1.15 # remove extcmds
1017 root 1.85 for my $name (grep $EXTCMD{$_}[1] eq $pkg, keys %EXTCMD) {
1018     delete $EXTCMD{$name};
1019 root 1.15 }
1020    
1021 root 1.43 if (my $cb = $pkg->can ("unload")) {
1022 elmex 1.31 eval {
1023     $cb->($pkg);
1024     1
1025     } or warn "$pkg unloaded, but with errors: $@";
1026     }
1027    
1028 root 1.1 Symbol::delete_package $pkg;
1029     }
1030    
1031     sub load_extensions {
1032     for my $ext (<$LIBDIR/*.ext>) {
1033 root 1.3 next unless -r $ext;
1034 root 1.2 eval {
1035     load_extension $ext;
1036     1
1037     } or warn "$ext not loaded: $@";
1038 root 1.1 }
1039     }
1040    
1041 root 1.8 #############################################################################
1042     # load/save/clean perl data associated with a map
1043    
1044 root 1.39 *cf::mapsupport::on_clean = sub {
1045 root 1.13 my ($map) = @_;
1046 root 1.7
1047     my $path = $map->tmpname;
1048     defined $path or return;
1049    
1050 root 1.46 unlink "$path.pst";
1051 root 1.7 };
1052    
1053 root 1.93 cf::map->attach (prio => -10000, package => cf::mapsupport::);
1054 root 1.39
1055 root 1.8 #############################################################################
1056     # load/save perl data associated with player->ob objects
1057    
1058 root 1.33 sub all_objects(@) {
1059     @_, map all_objects ($_->inv), @_
1060     }
1061    
1062 root 1.60 # TODO: compatibility cruft, remove when no longer needed
1063 root 1.93 cf::player->attach (
1064 root 1.39 on_load => sub {
1065     my ($pl, $path) = @_;
1066    
1067     for my $o (all_objects $pl->ob) {
1068     if (my $value = $o->get_ob_key_value ("_perl_data")) {
1069     $o->set_ob_key_value ("_perl_data");
1070 root 1.8
1071 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
1072     }
1073 root 1.11 }
1074 root 1.39 },
1075 root 1.93 );
1076 root 1.6
1077 root 1.22 #############################################################################
1078 root 1.70
1079     =head2 CORE EXTENSIONS
1080    
1081     Functions and methods that extend core crossfire objects.
1082    
1083 root 1.95 =head3 cf::player
1084    
1085 root 1.70 =over 4
1086 root 1.22
1087 root 1.23 =item cf::player::exists $login
1088    
1089     Returns true when the given account exists.
1090    
1091     =cut
1092    
1093     sub cf::player::exists($) {
1094     cf::player::find $_[0]
1095     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
1096     }
1097    
1098 root 1.95 =item $player->ext_reply ($msgid, $msgtype, %msg)
1099    
1100     Sends an ext reply to the player.
1101    
1102     =cut
1103    
1104     sub cf::player::ext_reply($$$%) {
1105     my ($self, $id, %msg) = @_;
1106    
1107     $msg{msgid} = $id;
1108    
1109     $self->send ("ext " . to_json \%msg);
1110     }
1111    
1112     =back
1113    
1114 root 1.110
1115     =head3 cf::map
1116    
1117     =over 4
1118    
1119     =cut
1120    
1121     package cf::map;
1122    
1123     use Fcntl;
1124     use Coro::AIO;
1125    
1126 root 1.133 our $MAX_RESET = 3600;
1127     our $DEFAULT_RESET = 3000;
1128 root 1.110
1129     sub generate_random_map {
1130     my ($path, $rmp) = @_;
1131    
1132     # mit "rum" bekleckern, nicht
1133     cf::map::_create_random_map
1134     $path,
1135     $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1136     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1137     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1138     $rmp->{exit_on_final_map},
1139     $rmp->{xsize}, $rmp->{ysize},
1140     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1141     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1142     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1143     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1144     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1145     (cf::region::find $rmp->{region})
1146     }
1147    
1148     # and all this just because we cannot iterate over
1149     # all maps in C++...
1150     sub change_all_map_light {
1151     my ($change) = @_;
1152    
1153 root 1.122 $_->change_map_light ($change)
1154     for grep $_->outdoor, values %cf::MAP;
1155 root 1.110 }
1156    
1157     sub try_load_header($) {
1158     my ($path) = @_;
1159    
1160     utf8::encode $path;
1161     aio_open $path, O_RDONLY, 0
1162     or return;
1163    
1164     my $map = cf::map::new
1165     or return;
1166    
1167 root 1.135 # for better error messages only, will be overwritten
1168     $map->path ($path);
1169    
1170 root 1.110 $map->load_header ($path)
1171     or return;
1172    
1173     $map->{load_path} = $path;
1174    
1175     $map
1176     }
1177    
1178 root 1.133 sub find;
1179     sub find {
1180 root 1.110 my ($path, $origin) = @_;
1181    
1182 root 1.133 #warn "find<$path,$origin>\n";#d#
1183 root 1.110
1184 root 1.112 $path = new cf::path $path, $origin && $origin->path;
1185 root 1.110 my $key = $path->as_string;
1186    
1187 root 1.120 cf::lock_wait "map_find:$key";
1188    
1189 root 1.110 $cf::MAP{$key} || do {
1190 root 1.120 my $guard = cf::lock_acquire "map_find:$key";
1191    
1192 root 1.110 # do it the slow way
1193     my $map = try_load_header $path->save_path;
1194    
1195 root 1.134 Coro::cede;
1196    
1197 root 1.110 if ($map) {
1198 root 1.132 $map->last_access ((delete $map->{last_access})
1199     || $cf::RUNTIME); #d#
1200 root 1.110 # safety
1201     $map->{instantiate_time} = $cf::RUNTIME
1202     if $map->{instantiate_time} > $cf::RUNTIME;
1203     } else {
1204     if (my $rmp = $path->random_map_params) {
1205     $map = generate_random_map $key, $rmp;
1206     } else {
1207     $map = try_load_header $path->load_path;
1208     }
1209    
1210     $map or return;
1211    
1212 root 1.111 $map->{load_original} = 1;
1213 root 1.110 $map->{instantiate_time} = $cf::RUNTIME;
1214 root 1.132 $map->last_access ($cf::RUNTIME);
1215 root 1.110 $map->instantiate;
1216    
1217     # per-player maps become, after loading, normal maps
1218     $map->per_player (0) if $path->{user_rel};
1219     }
1220    
1221     $map->path ($key);
1222     $map->{path} = $path;
1223 root 1.116 $map->{last_save} = $cf::RUNTIME;
1224 root 1.110
1225 root 1.134 Coro::cede;
1226    
1227 root 1.112 if ($map->should_reset) {
1228     $map->reset;
1229 root 1.123 undef $guard;
1230 root 1.133 $map = find $path
1231 root 1.124 or return;
1232 root 1.112 }
1233 root 1.110
1234     $cf::MAP{$key} = $map
1235     }
1236     }
1237    
1238     sub load {
1239     my ($self) = @_;
1240    
1241 root 1.120 my $path = $self->{path};
1242     my $guard = cf::lock_acquire "map_load:" . $path->as_string;
1243    
1244 root 1.110 return if $self->in_memory != cf::MAP_SWAPPED;
1245    
1246     $self->in_memory (cf::MAP_LOADING);
1247    
1248     $self->alloc;
1249     $self->load_objects ($self->{load_path}, 1)
1250     or return;
1251    
1252 root 1.112 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1253     if delete $self->{load_original};
1254 root 1.111
1255 root 1.110 if (my $uniq = $path->uniq_path) {
1256     utf8::encode $uniq;
1257     if (aio_open $uniq, O_RDONLY, 0) {
1258     $self->clear_unique_items;
1259     $self->load_objects ($uniq, 0);
1260     }
1261     }
1262    
1263 root 1.134 Coro::cede;
1264    
1265 root 1.110 # now do the right thing for maps
1266     $self->link_multipart_objects;
1267    
1268     if ($self->{path}->is_style_map) {
1269     $self->{deny_save} = 1;
1270     $self->{deny_reset} = 1;
1271     } else {
1272     $self->fix_auto_apply;
1273     $self->decay_objects;
1274     $self->update_buttons;
1275     $self->set_darkness_map;
1276     $self->difficulty ($self->estimate_difficulty)
1277     unless $self->difficulty;
1278     $self->activate;
1279     }
1280    
1281 root 1.134 Coro::cede;
1282    
1283 root 1.110 $self->in_memory (cf::MAP_IN_MEMORY);
1284     }
1285    
1286 root 1.133 sub find_sync {
1287 root 1.110 my ($path, $origin) = @_;
1288    
1289 root 1.133 cf::sync_job { cf::map::find $path, $origin }
1290     }
1291    
1292     sub do_load_sync {
1293     my ($map) = @_;
1294 root 1.110
1295 root 1.133 cf::sync_job { $map->load };
1296 root 1.110 }
1297    
1298     sub save {
1299     my ($self) = @_;
1300    
1301     $self->{last_save} = $cf::RUNTIME;
1302    
1303     return unless $self->dirty;
1304    
1305 root 1.117 my $save = $self->{path}->save_path; utf8::encode $save;
1306     my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1307    
1308 root 1.110 $self->{load_path} = $save;
1309    
1310     return if $self->{deny_save};
1311    
1312 root 1.132 local $self->{last_access} = $self->last_access;#d#
1313    
1314 root 1.110 if ($uniq) {
1315     $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1316     $self->save_objects ($uniq, cf::IO_UNIQUES);
1317     } else {
1318     $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1319     }
1320     }
1321    
1322     sub swap_out {
1323     my ($self) = @_;
1324    
1325 root 1.130 # save first because save cedes
1326     $self->save;
1327    
1328 root 1.110 return if $self->players;
1329     return if $self->in_memory != cf::MAP_IN_MEMORY;
1330     return if $self->{deny_save};
1331    
1332     $self->clear;
1333     $self->in_memory (cf::MAP_SWAPPED);
1334     }
1335    
1336 root 1.112 sub reset_at {
1337     my ($self) = @_;
1338 root 1.110
1339     # TODO: safety, remove and allow resettable per-player maps
1340 root 1.114 return 1e99 if $self->{path}{user_rel};
1341     return 1e99 if $self->{deny_reset};
1342 root 1.110
1343 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1344 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1345 root 1.110
1346 root 1.112 $time + $to
1347     }
1348    
1349     sub should_reset {
1350     my ($self) = @_;
1351    
1352     $self->reset_at <= $cf::RUNTIME
1353 root 1.111 }
1354    
1355     sub unlink_save {
1356     my ($self) = @_;
1357    
1358     utf8::encode (my $save = $self->{path}->save_path);
1359     aioreq_pri 3; IO::AIO::aio_unlink $save;
1360     aioreq_pri 3; IO::AIO::aio_unlink "$save.pst";
1361 root 1.110 }
1362    
1363 root 1.113 sub rename {
1364     my ($self, $new_path) = @_;
1365    
1366     $self->unlink_save;
1367    
1368     delete $cf::MAP{$self->path};
1369     $self->{path} = new cf::path $new_path;
1370 root 1.114 $self->path ($self->{path}->as_string);
1371 root 1.113 $cf::MAP{$self->path} = $self;
1372    
1373     $self->save;
1374     }
1375    
1376 root 1.110 sub reset {
1377     my ($self) = @_;
1378    
1379     return if $self->players;
1380     return if $self->{path}{user_rel};#d#
1381    
1382     warn "resetting map ", $self->path;#d#
1383    
1384 root 1.111 delete $cf::MAP{$self->path};
1385 root 1.110
1386     $_->clear_links_to ($self) for values %cf::MAP;
1387    
1388 root 1.111 $self->unlink_save;
1389     $self->destroy;
1390 root 1.110 }
1391    
1392 root 1.114 my $nuke_counter = "aaaa";
1393    
1394     sub nuke {
1395     my ($self) = @_;
1396    
1397     $self->{deny_save} = 1;
1398     $self->reset_timeout (1);
1399     $self->rename ("{nuke}/" . ($nuke_counter++));
1400     $self->reset; # polite request, might not happen
1401     }
1402    
1403 root 1.110 sub customise_for {
1404     my ($map, $ob) = @_;
1405    
1406     if ($map->per_player) {
1407 root 1.133 return cf::map::find "~" . $ob->name . "/" . $map->{path}{path};
1408 root 1.110 }
1409    
1410     $map
1411     }
1412    
1413     sub emergency_save {
1414 root 1.133 my $freeze_guard = cf::freeze_mainloop;
1415 root 1.110
1416     warn "enter emergency map save\n";
1417    
1418     cf::sync_job {
1419     warn "begin emergency map save\n";
1420     $_->save for values %cf::MAP;
1421     };
1422    
1423     warn "end emergency map save\n";
1424     }
1425    
1426     package cf;
1427    
1428     =back
1429    
1430    
1431 root 1.95 =head3 cf::object::player
1432    
1433     =over 4
1434    
1435 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
1436 root 1.28
1437     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
1438     can be C<undef>. Does the right thing when the player is currently in a
1439     dialogue with the given NPC character.
1440    
1441     =cut
1442    
1443 root 1.22 # rough implementation of a future "reply" method that works
1444     # with dialog boxes.
1445 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
1446 root 1.23 sub cf::object::player::reply($$$;$) {
1447     my ($self, $npc, $msg, $flags) = @_;
1448    
1449     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1450 root 1.22
1451 root 1.24 if ($self->{record_replies}) {
1452     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
1453     } else {
1454     $msg = $npc->name . " says: $msg" if $npc;
1455     $self->message ($msg, $flags);
1456     }
1457 root 1.22 }
1458    
1459 root 1.79 =item $player_object->may ("access")
1460    
1461     Returns wether the given player is authorized to access resource "access"
1462     (e.g. "command_wizcast").
1463    
1464     =cut
1465    
1466     sub cf::object::player::may {
1467     my ($self, $access) = @_;
1468    
1469     $self->flag (cf::FLAG_WIZ) ||
1470     (ref $cf::CFG{"may_$access"}
1471     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
1472     : $cf::CFG{"may_$access"})
1473     }
1474 root 1.70
1475 root 1.115 =item $player_object->enter_link
1476    
1477     Freezes the player and moves him/her to a special map (C<{link}>).
1478    
1479     The player should be reaosnably safe there for short amounts of time. You
1480     I<MUST> call C<leave_link> as soon as possible, though.
1481    
1482     =item $player_object->leave_link ($map, $x, $y)
1483    
1484     Moves the player out of the specila link map onto the given map. If the
1485     map is not valid (or omitted), the player will be moved back to the
1486     location he/she was before the call to C<enter_link>, or, if that fails,
1487     to the emergency map position.
1488    
1489     Might block.
1490    
1491     =cut
1492    
1493 root 1.110 sub cf::object::player::enter_link {
1494     my ($self) = @_;
1495    
1496 root 1.120 $self->deactivate_recursive;
1497    
1498 root 1.110 return if $self->map == $LINK_MAP;
1499    
1500 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
1501 root 1.110 if $self->map;
1502    
1503     $self->enter_map ($LINK_MAP, 20, 20);
1504     }
1505    
1506     sub cf::object::player::leave_link {
1507     my ($self, $map, $x, $y) = @_;
1508    
1509     my $link_pos = delete $self->{_link_pos};
1510    
1511     unless ($map) {
1512     # restore original map position
1513     ($map, $x, $y) = @{ $link_pos || [] };
1514 root 1.133 $map = cf::map::find $map;
1515 root 1.110
1516     unless ($map) {
1517     ($map, $x, $y) = @$EMERGENCY_POSITION;
1518 root 1.133 $map = cf::map::find $map
1519 root 1.110 or die "FATAL: cannot load emergency map\n";
1520     }
1521     }
1522    
1523     ($x, $y) = (-1, -1)
1524     unless (defined $x) && (defined $y);
1525    
1526     # use -1 or undef as default coordinates, not 0, 0
1527     ($x, $y) = ($map->enter_x, $map->enter_y)
1528     if $x <=0 && $y <= 0;
1529    
1530     $map->load;
1531    
1532     $self->activate_recursive;
1533     $self->enter_map ($map, $x, $y);
1534     }
1535    
1536 root 1.120 cf::player->attach (
1537     on_logout => sub {
1538     my ($pl) = @_;
1539    
1540     # abort map switching before logout
1541     if ($pl->ob->{_link_pos}) {
1542     cf::sync_job {
1543     $pl->ob->leave_link
1544     };
1545     }
1546     },
1547     on_login => sub {
1548     my ($pl) = @_;
1549    
1550     # try to abort aborted map switching on player login :)
1551     # should happen only on crashes
1552     if ($pl->ob->{_link_pos}) {
1553     $pl->ob->enter_link;
1554 root 1.127 cf::async {
1555 root 1.120 # we need this sleep as the login has a concurrent enter_exit running
1556     # and this sleep increases chances of the player not ending up in scorn
1557     Coro::Timer::sleep 1;
1558     $pl->ob->leave_link;
1559     };
1560     }
1561     },
1562     );
1563    
1564 root 1.118 =item $player_object->goto_map ($path, $x, $y)
1565 root 1.110
1566     =cut
1567    
1568     sub cf::object::player::goto_map {
1569     my ($self, $path, $x, $y) = @_;
1570    
1571     $self->enter_link;
1572    
1573 root 1.127 (cf::async {
1574 root 1.110 $path = new cf::path $path;
1575    
1576 root 1.133 my $map = cf::map::find $path->as_string;
1577 root 1.110 $map = $map->customise_for ($self) if $map;
1578    
1579 root 1.119 # warn "entering ", $map->path, " at ($x, $y)\n"
1580     # if $map;
1581 root 1.110
1582 root 1.115 $map or $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1583    
1584 root 1.110 $self->leave_link ($map, $x, $y);
1585     })->prio (1);
1586     }
1587    
1588     =item $player_object->enter_exit ($exit_object)
1589    
1590     =cut
1591    
1592     sub parse_random_map_params {
1593     my ($spec) = @_;
1594    
1595     my $rmp = { # defaults
1596     xsize => 10,
1597     ysize => 10,
1598     };
1599    
1600     for (split /\n/, $spec) {
1601     my ($k, $v) = split /\s+/, $_, 2;
1602    
1603     $rmp->{lc $k} = $v if (length $k) && (length $v);
1604     }
1605    
1606     $rmp
1607     }
1608    
1609     sub prepare_random_map {
1610     my ($exit) = @_;
1611    
1612     # all this does is basically replace the /! path by
1613     # a new random map path (?random/...) with a seed
1614     # that depends on the exit object
1615    
1616     my $rmp = parse_random_map_params $exit->msg;
1617    
1618     if ($exit->map) {
1619     $rmp->{region} = $exit->map->region_name;
1620     $rmp->{origin_map} = $exit->map->path;
1621     $rmp->{origin_x} = $exit->x;
1622     $rmp->{origin_y} = $exit->y;
1623     }
1624    
1625     $rmp->{random_seed} ||= $exit->random_seed;
1626    
1627     my $data = cf::to_json $rmp;
1628     my $md5 = Digest::MD5::md5_hex $data;
1629    
1630     if (my $fh = aio_open "$cf::RANDOM_MAPS/$md5.meta", O_WRONLY | O_CREAT, 0666) {
1631     aio_write $fh, 0, (length $data), $data, 0;
1632    
1633     $exit->slaying ("?random/$md5");
1634     $exit->msg (undef);
1635     }
1636     }
1637    
1638     sub cf::object::player::enter_exit {
1639     my ($self, $exit) = @_;
1640    
1641     return unless $self->type == cf::PLAYER;
1642    
1643     $self->enter_link;
1644    
1645 root 1.127 (cf::async {
1646 root 1.133 $self->deactivate_recursive; # just to be sure
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.133 my $guard = freeze_mainloop;
2032 root 1.106 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.133 cf::server_tick; # one server iteration
2178     $RUNTIME += $TICK;
2179 root 1.35 $NEXT_TICK += $TICK;
2180    
2181 root 1.78 # if we are delayed by four ticks or more, skip them all
2182 root 1.103 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4;
2183 root 1.35
2184     $TICK_WATCHER->at ($NEXT_TICK);
2185     $TICK_WATCHER->start;
2186     },
2187     );
2188    
2189 root 1.80 IO::AIO::max_poll_time $TICK * 0.2;
2190 root 1.77
2191 root 1.108 Event->io (
2192     fd => IO::AIO::poll_fileno,
2193     poll => 'r',
2194     prio => 5,
2195     data => WF_AUTOCANCEL,
2196     cb => \&IO::AIO::poll_cb,
2197     );
2198    
2199     Event->timer (
2200     data => WF_AUTOCANCEL,
2201     after => 0,
2202     interval => 10,
2203     cb => sub {
2204     (Coro::unblock_sub {
2205     write_runtime
2206     or warn "ERROR: unable to write runtime file: $!";
2207     })->();
2208     },
2209     );
2210 root 1.103
2211 root 1.125 END { cf::emergency_save }
2212    
2213 root 1.1 1
2214