ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.137
Committed: Fri Jan 5 10:23:05 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.136: +6 -0 lines
Log Message:
implement locking for map data

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