ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.139
Committed: Fri Jan 5 19:12:03 2007 UTC (17 years, 5 months ago) by root
Branch: MAIN
Changes since 1.138: +16 -9 lines
Log Message:
emergency

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