ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.138
Committed: Fri Jan 5 17:07:17 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.137: +8 -24 lines
Log Message:
use coros coropool

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