ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.133
Committed: Thu Jan 4 16:19:32 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.132: +33 -29 lines
Log Message:
- word of recall activated the player indirectly
- implement maptile->xy_find and xy_load
- separate find and load, even on C level
- generate map_leave/enter and map_change events even for tiled map changes
  (experimental)
- implement mainloop freezeing by start/stop, not skipping ticks
- no map updates when player !active

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