ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.140
Committed: Fri Jan 5 20:04:02 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.139: +28 -17 lines
Log Message:
fix the bug: on_destroy is obviously not being called on pooled coroutines, aslo use more sensible names than 'coro'

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