ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.119
Committed: Tue Jan 2 08:26:42 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.118: +2 -2 lines
Log Message:
fix follow (but should be cleanly rewritten)

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