ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.142
Committed: Fri Jan 5 21:51:42 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.141: +1 -1 lines
Log Message:
improve the hack

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