ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.115
Committed: Mon Jan 1 16:50:10 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.114: +21 -3 lines
Log Message:
implemented reste command, maps command etc.

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.105 return ($data, $av);
823 root 1.45 }
824    
825     #############################################################################
826 root 1.85 # command handling &c
827 root 1.39
828 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
829 root 1.1
830 root 1.85 Register a callback for execution when the client sends the user command
831     $name.
832 root 1.5
833 root 1.85 =cut
834 root 1.5
835 root 1.85 sub register_command {
836     my ($name, $cb) = @_;
837 root 1.5
838 root 1.85 my $caller = caller;
839     #warn "registering command '$name/$time' to '$caller'";
840 root 1.1
841 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
842 root 1.1 }
843    
844 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
845 root 1.1
846 root 1.85 Register a callbackf ro execution when the client sends an extcmd packet.
847 root 1.1
848 root 1.85 If the callback returns something, it is sent back as if reply was being
849     called.
850 root 1.1
851 root 1.85 =cut
852 root 1.1
853 root 1.16 sub register_extcmd {
854     my ($name, $cb) = @_;
855    
856     my $caller = caller;
857     #warn "registering extcmd '$name' to '$caller'";
858    
859 root 1.85 $EXTCMD{$name} = [$cb, $caller];
860 root 1.16 }
861    
862 root 1.93 cf::player->attach (
863 root 1.85 on_command => sub {
864     my ($pl, $name, $params) = @_;
865    
866     my $cb = $COMMAND{$name}
867     or return;
868    
869     for my $cmd (@$cb) {
870     $cmd->[1]->($pl->ob, $params);
871     }
872    
873     cf::override;
874     },
875     on_extcmd => sub {
876     my ($pl, $buf) = @_;
877    
878     my $msg = eval { from_json $buf };
879    
880     if (ref $msg) {
881     if (my $cb = $EXTCMD{$msg->{msgtype}}) {
882     if (my %reply = $cb->[0]->($pl, $msg)) {
883     $pl->ext_reply ($msg->{msgid}, %reply);
884     }
885     }
886     } else {
887     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
888     }
889    
890     cf::override;
891     },
892 root 1.93 );
893 root 1.85
894 root 1.6 sub register {
895     my ($base, $pkg) = @_;
896    
897 root 1.45 #TODO
898 root 1.6 }
899    
900 root 1.1 sub load_extension {
901     my ($path) = @_;
902    
903     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
904 root 1.5 my $base = $1;
905 root 1.1 my $pkg = $1;
906     $pkg =~ s/[^[:word:]]/_/g;
907 root 1.41 $pkg = "ext::$pkg";
908 root 1.1
909     warn "loading '$path' into '$pkg'\n";
910    
911     open my $fh, "<:utf8", $path
912     or die "$path: $!";
913    
914     my $source =
915     "package $pkg; use strict; use utf8;\n"
916     . "#line 1 \"$path\"\n{\n"
917     . (do { local $/; <$fh> })
918     . "\n};\n1";
919    
920     eval $source
921 root 1.82 or die $@ ? "$path: $@\n"
922     : "extension disabled.\n";
923 root 1.1
924     push @exts, $pkg;
925 root 1.5 $ext_pkg{$base} = $pkg;
926 root 1.1
927 root 1.6 # no strict 'refs';
928 root 1.23 # @{"$pkg\::ISA"} = ext::;
929 root 1.1
930 root 1.6 register $base, $pkg;
931 root 1.1 }
932    
933     sub unload_extension {
934     my ($pkg) = @_;
935    
936     warn "removing extension $pkg\n";
937    
938     # remove hooks
939 root 1.45 #TODO
940     # for my $idx (0 .. $#PLUGIN_EVENT) {
941     # delete $hook[$idx]{$pkg};
942     # }
943 root 1.1
944     # remove commands
945 root 1.85 for my $name (keys %COMMAND) {
946     my @cb = grep $_->[0] ne $pkg, @{ $COMMAND{$name} };
947 root 1.1
948     if (@cb) {
949 root 1.85 $COMMAND{$name} = \@cb;
950 root 1.1 } else {
951 root 1.85 delete $COMMAND{$name};
952 root 1.1 }
953     }
954    
955 root 1.15 # remove extcmds
956 root 1.85 for my $name (grep $EXTCMD{$_}[1] eq $pkg, keys %EXTCMD) {
957     delete $EXTCMD{$name};
958 root 1.15 }
959    
960 root 1.43 if (my $cb = $pkg->can ("unload")) {
961 elmex 1.31 eval {
962     $cb->($pkg);
963     1
964     } or warn "$pkg unloaded, but with errors: $@";
965     }
966    
967 root 1.1 Symbol::delete_package $pkg;
968     }
969    
970     sub load_extensions {
971     for my $ext (<$LIBDIR/*.ext>) {
972 root 1.3 next unless -r $ext;
973 root 1.2 eval {
974     load_extension $ext;
975     1
976     } or warn "$ext not loaded: $@";
977 root 1.1 }
978     }
979    
980 root 1.8 #############################################################################
981     # load/save/clean perl data associated with a map
982    
983 root 1.39 *cf::mapsupport::on_clean = sub {
984 root 1.13 my ($map) = @_;
985 root 1.7
986     my $path = $map->tmpname;
987     defined $path or return;
988    
989 root 1.46 unlink "$path.pst";
990 root 1.7 };
991    
992 root 1.93 cf::map->attach (prio => -10000, package => cf::mapsupport::);
993 root 1.39
994 root 1.8 #############################################################################
995     # load/save perl data associated with player->ob objects
996    
997 root 1.33 sub all_objects(@) {
998     @_, map all_objects ($_->inv), @_
999     }
1000    
1001 root 1.60 # TODO: compatibility cruft, remove when no longer needed
1002 root 1.93 cf::player->attach (
1003 root 1.39 on_load => sub {
1004     my ($pl, $path) = @_;
1005    
1006     for my $o (all_objects $pl->ob) {
1007     if (my $value = $o->get_ob_key_value ("_perl_data")) {
1008     $o->set_ob_key_value ("_perl_data");
1009 root 1.8
1010 root 1.39 %$o = %{ Storable::thaw pack "H*", $value };
1011     }
1012 root 1.11 }
1013 root 1.39 },
1014 root 1.93 );
1015 root 1.6
1016 root 1.22 #############################################################################
1017 root 1.70
1018     =head2 CORE EXTENSIONS
1019    
1020     Functions and methods that extend core crossfire objects.
1021    
1022 root 1.95 =head3 cf::player
1023    
1024 root 1.70 =over 4
1025 root 1.22
1026 root 1.23 =item cf::player::exists $login
1027    
1028     Returns true when the given account exists.
1029    
1030     =cut
1031    
1032     sub cf::player::exists($) {
1033     cf::player::find $_[0]
1034     or -f sprintf "%s/%s/%s/%s.pl", cf::localdir, cf::playerdir, ($_[0]) x 2;
1035     }
1036    
1037 root 1.95 =item $player->ext_reply ($msgid, $msgtype, %msg)
1038    
1039     Sends an ext reply to the player.
1040    
1041     =cut
1042    
1043     sub cf::player::ext_reply($$$%) {
1044     my ($self, $id, %msg) = @_;
1045    
1046     $msg{msgid} = $id;
1047    
1048     $self->send ("ext " . to_json \%msg);
1049     }
1050    
1051     =back
1052    
1053 root 1.110
1054     =head3 cf::map
1055    
1056     =over 4
1057    
1058     =cut
1059    
1060     package cf::map;
1061    
1062     use Fcntl;
1063     use Coro::AIO;
1064    
1065     our $MAX_RESET = 7200;
1066     our $DEFAULT_RESET = 3600;
1067    
1068     sub generate_random_map {
1069     my ($path, $rmp) = @_;
1070    
1071     # mit "rum" bekleckern, nicht
1072     cf::map::_create_random_map
1073     $path,
1074     $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1075     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1076     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1077     $rmp->{exit_on_final_map},
1078     $rmp->{xsize}, $rmp->{ysize},
1079     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1080     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1081     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1082     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1083     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1084     (cf::region::find $rmp->{region})
1085     }
1086    
1087     # and all this just because we cannot iterate over
1088     # all maps in C++...
1089     sub change_all_map_light {
1090     my ($change) = @_;
1091    
1092     $_->change_map_light ($change) for values %cf::MAP;
1093     }
1094    
1095     sub try_load_header($) {
1096     my ($path) = @_;
1097    
1098     utf8::encode $path;
1099     aio_open $path, O_RDONLY, 0
1100     or return;
1101    
1102     my $map = cf::map::new
1103     or return;
1104    
1105     $map->load_header ($path)
1106     or return;
1107    
1108     $map->{load_path} = $path;
1109    
1110     $map
1111     }
1112    
1113     sub find_map {
1114     my ($path, $origin) = @_;
1115    
1116     #warn "find_map<$path,$origin>\n";#d#
1117    
1118 root 1.112 $path = new cf::path $path, $origin && $origin->path;
1119 root 1.110 my $key = $path->as_string;
1120    
1121     $cf::MAP{$key} || do {
1122     # do it the slow way
1123     my $map = try_load_header $path->save_path;
1124    
1125     if ($map) {
1126     # safety
1127     $map->{instantiate_time} = $cf::RUNTIME
1128     if $map->{instantiate_time} > $cf::RUNTIME;
1129     } else {
1130     if (my $rmp = $path->random_map_params) {
1131     $map = generate_random_map $key, $rmp;
1132     } else {
1133     $map = try_load_header $path->load_path;
1134     }
1135    
1136     $map or return;
1137    
1138 root 1.111 $map->{load_original} = 1;
1139 root 1.110 $map->{instantiate_time} = $cf::RUNTIME;
1140     $map->instantiate;
1141    
1142     # per-player maps become, after loading, normal maps
1143     $map->per_player (0) if $path->{user_rel};
1144     }
1145    
1146     $map->path ($key);
1147     $map->{path} = $path;
1148     $map->last_access ($cf::RUNTIME);
1149    
1150 root 1.112 if ($map->should_reset) {
1151     $map->reset;
1152     $map = find_map $path;
1153     }
1154 root 1.110
1155     $cf::MAP{$key} = $map
1156     }
1157     }
1158    
1159     sub load {
1160     my ($self) = @_;
1161    
1162     return if $self->in_memory != cf::MAP_SWAPPED;
1163    
1164     $self->in_memory (cf::MAP_LOADING);
1165    
1166     my $path = $self->{path};
1167    
1168     $self->alloc;
1169     $self->load_objects ($self->{load_path}, 1)
1170     or return;
1171    
1172 root 1.112 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1173     if delete $self->{load_original};
1174 root 1.111
1175 root 1.110 if (my $uniq = $path->uniq_path) {
1176     utf8::encode $uniq;
1177     if (aio_open $uniq, O_RDONLY, 0) {
1178     $self->clear_unique_items;
1179     $self->load_objects ($uniq, 0);
1180     }
1181     }
1182    
1183     # now do the right thing for maps
1184     $self->link_multipart_objects;
1185    
1186     if ($self->{path}->is_style_map) {
1187     $self->{deny_save} = 1;
1188     $self->{deny_reset} = 1;
1189     } else {
1190     $self->fix_auto_apply;
1191     $self->decay_objects;
1192     $self->update_buttons;
1193     $self->set_darkness_map;
1194     $self->difficulty ($self->estimate_difficulty)
1195     unless $self->difficulty;
1196     $self->activate;
1197     }
1198    
1199     $self->in_memory (cf::MAP_IN_MEMORY);
1200     }
1201    
1202     sub load_map_sync {
1203     my ($path, $origin) = @_;
1204    
1205     #warn "load_map_sync<$path, $origin>\n";#d#
1206    
1207     cf::sync_job {
1208     my $map = cf::map::find_map $path, $origin
1209     or return;
1210     $map->load;
1211     $map
1212     }
1213     }
1214    
1215     sub save {
1216     my ($self) = @_;
1217    
1218     my $save = $self->{path}->save_path; utf8::encode $save;
1219     my $uniq = $self->{path}->uniq_path; utf8::encode $uniq;
1220    
1221     $self->{last_save} = $cf::RUNTIME;
1222    
1223     return unless $self->dirty;
1224    
1225     $self->{load_path} = $save;
1226    
1227     return if $self->{deny_save};
1228    
1229     if ($uniq) {
1230     $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1231     $self->save_objects ($uniq, cf::IO_UNIQUES);
1232     } else {
1233     $self->save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1234     }
1235     }
1236    
1237     sub swap_out {
1238     my ($self) = @_;
1239    
1240     return if $self->players;
1241     return if $self->in_memory != cf::MAP_IN_MEMORY;
1242     return if $self->{deny_save};
1243    
1244     $self->save;
1245     $self->clear;
1246     $self->in_memory (cf::MAP_SWAPPED);
1247     }
1248    
1249 root 1.112 sub reset_at {
1250     my ($self) = @_;
1251 root 1.110
1252     # TODO: safety, remove and allow resettable per-player maps
1253 root 1.114 return 1e99 if $self->{path}{user_rel};
1254     return 1e99 if $self->{deny_reset};
1255 root 1.110
1256 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1257 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1258 root 1.110
1259 root 1.112 $time + $to
1260     }
1261    
1262     sub should_reset {
1263     my ($self) = @_;
1264    
1265     $self->reset_at <= $cf::RUNTIME
1266 root 1.111 }
1267    
1268     sub unlink_save {
1269     my ($self) = @_;
1270    
1271     utf8::encode (my $save = $self->{path}->save_path);
1272     aioreq_pri 3; IO::AIO::aio_unlink $save;
1273     aioreq_pri 3; IO::AIO::aio_unlink "$save.pst";
1274 root 1.110 }
1275    
1276 root 1.113 sub rename {
1277     my ($self, $new_path) = @_;
1278    
1279     $self->unlink_save;
1280    
1281     delete $cf::MAP{$self->path};
1282     $self->{path} = new cf::path $new_path;
1283 root 1.114 $self->path ($self->{path}->as_string);
1284 root 1.113 $cf::MAP{$self->path} = $self;
1285    
1286     $self->save;
1287     }
1288    
1289 root 1.110 sub reset {
1290     my ($self) = @_;
1291    
1292     return if $self->players;
1293     return if $self->{path}{user_rel};#d#
1294    
1295     warn "resetting map ", $self->path;#d#
1296    
1297 root 1.111 delete $cf::MAP{$self->path};
1298 root 1.110
1299     $_->clear_links_to ($self) for values %cf::MAP;
1300    
1301 root 1.111 $self->unlink_save;
1302     $self->destroy;
1303 root 1.110 }
1304    
1305 root 1.114 my $nuke_counter = "aaaa";
1306    
1307     sub nuke {
1308     my ($self) = @_;
1309    
1310     $self->{deny_save} = 1;
1311     $self->reset_timeout (1);
1312     $self->rename ("{nuke}/" . ($nuke_counter++));
1313     $self->reset; # polite request, might not happen
1314     }
1315    
1316 root 1.110 sub customise_for {
1317     my ($map, $ob) = @_;
1318    
1319     if ($map->per_player) {
1320     return cf::map::find_map "~" . $ob->name . "/" . $map->{path}{path};
1321     }
1322    
1323     $map
1324     }
1325    
1326     sub emergency_save {
1327     local $cf::FREEZE = 1;
1328    
1329     warn "enter emergency map save\n";
1330    
1331     cf::sync_job {
1332     warn "begin emergency map save\n";
1333     $_->save for values %cf::MAP;
1334     };
1335    
1336     warn "end emergency map save\n";
1337     }
1338    
1339     package cf;
1340    
1341     =back
1342    
1343    
1344 root 1.95 =head3 cf::object::player
1345    
1346     =over 4
1347    
1348 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
1349 root 1.28
1350     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
1351     can be C<undef>. Does the right thing when the player is currently in a
1352     dialogue with the given NPC character.
1353    
1354     =cut
1355    
1356 root 1.22 # rough implementation of a future "reply" method that works
1357     # with dialog boxes.
1358 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
1359 root 1.23 sub cf::object::player::reply($$$;$) {
1360     my ($self, $npc, $msg, $flags) = @_;
1361    
1362     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1363 root 1.22
1364 root 1.24 if ($self->{record_replies}) {
1365     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
1366     } else {
1367     $msg = $npc->name . " says: $msg" if $npc;
1368     $self->message ($msg, $flags);
1369     }
1370 root 1.22 }
1371    
1372 root 1.79 =item $player_object->may ("access")
1373    
1374     Returns wether the given player is authorized to access resource "access"
1375     (e.g. "command_wizcast").
1376    
1377     =cut
1378    
1379     sub cf::object::player::may {
1380     my ($self, $access) = @_;
1381    
1382     $self->flag (cf::FLAG_WIZ) ||
1383     (ref $cf::CFG{"may_$access"}
1384     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
1385     : $cf::CFG{"may_$access"})
1386     }
1387 root 1.70
1388 root 1.115 =item $player_object->enter_link
1389    
1390     Freezes the player and moves him/her to a special map (C<{link}>).
1391    
1392     The player should be reaosnably safe there for short amounts of time. You
1393     I<MUST> call C<leave_link> as soon as possible, though.
1394    
1395     =item $player_object->leave_link ($map, $x, $y)
1396    
1397     Moves the player out of the specila link map onto the given map. If the
1398     map is not valid (or omitted), the player will be moved back to the
1399     location he/she was before the call to C<enter_link>, or, if that fails,
1400     to the emergency map position.
1401    
1402     Might block.
1403    
1404     =cut
1405    
1406 root 1.110 sub cf::object::player::enter_link {
1407     my ($self) = @_;
1408    
1409     return if $self->map == $LINK_MAP;
1410    
1411     $self->{_link_pos} = [$self->map->{path}, $self->x, $self->y]
1412     if $self->map;
1413    
1414     $self->enter_map ($LINK_MAP, 20, 20);
1415     $self->deactivate_recursive;
1416     }
1417    
1418     sub cf::object::player::leave_link {
1419     my ($self, $map, $x, $y) = @_;
1420    
1421     my $link_pos = delete $self->{_link_pos};
1422    
1423     unless ($map) {
1424     # restore original map position
1425     ($map, $x, $y) = @{ $link_pos || [] };
1426     $map = cf::map::find_map $map;
1427    
1428     unless ($map) {
1429     ($map, $x, $y) = @$EMERGENCY_POSITION;
1430     $map = cf::map::find_map $map
1431     or die "FATAL: cannot load emergency map\n";
1432     }
1433     }
1434    
1435     ($x, $y) = (-1, -1)
1436     unless (defined $x) && (defined $y);
1437    
1438     # use -1 or undef as default coordinates, not 0, 0
1439     ($x, $y) = ($map->enter_x, $map->enter_y)
1440     if $x <=0 && $y <= 0;
1441    
1442     $map->load;
1443    
1444     $self->activate_recursive;
1445     $self->enter_map ($map, $x, $y);
1446     }
1447    
1448     =item $player_object->goto_map ($map, $x, $y)
1449    
1450     =cut
1451    
1452     sub cf::object::player::goto_map {
1453     my ($self, $path, $x, $y) = @_;
1454    
1455     $self->enter_link;
1456    
1457     (Coro::async {
1458     $path = new cf::path $path;
1459    
1460     my $map = cf::map::find_map $path->as_string;
1461     $map = $map->customise_for ($self) if $map;
1462    
1463     warn "entering ", $map->path, " at ($x, $y)\n"
1464     if $map;
1465    
1466 root 1.115 $map or $self->message ("The exit is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1467    
1468 root 1.110 $self->leave_link ($map, $x, $y);
1469     })->prio (1);
1470     }
1471    
1472     =item $player_object->enter_exit ($exit_object)
1473    
1474     =cut
1475    
1476     sub parse_random_map_params {
1477     my ($spec) = @_;
1478    
1479     my $rmp = { # defaults
1480     xsize => 10,
1481     ysize => 10,
1482     };
1483    
1484     for (split /\n/, $spec) {
1485     my ($k, $v) = split /\s+/, $_, 2;
1486    
1487     $rmp->{lc $k} = $v if (length $k) && (length $v);
1488     }
1489    
1490     $rmp
1491     }
1492    
1493     sub prepare_random_map {
1494     my ($exit) = @_;
1495    
1496     # all this does is basically replace the /! path by
1497     # a new random map path (?random/...) with a seed
1498     # that depends on the exit object
1499    
1500     my $rmp = parse_random_map_params $exit->msg;
1501    
1502     if ($exit->map) {
1503     $rmp->{region} = $exit->map->region_name;
1504     $rmp->{origin_map} = $exit->map->path;
1505     $rmp->{origin_x} = $exit->x;
1506     $rmp->{origin_y} = $exit->y;
1507     }
1508    
1509     $rmp->{random_seed} ||= $exit->random_seed;
1510    
1511     my $data = cf::to_json $rmp;
1512     my $md5 = Digest::MD5::md5_hex $data;
1513    
1514     if (my $fh = aio_open "$cf::RANDOM_MAPS/$md5.meta", O_WRONLY | O_CREAT, 0666) {
1515     aio_write $fh, 0, (length $data), $data, 0;
1516    
1517     $exit->slaying ("?random/$md5");
1518     $exit->msg (undef);
1519     }
1520     }
1521    
1522     sub cf::object::player::enter_exit {
1523     my ($self, $exit) = @_;
1524    
1525     return unless $self->type == cf::PLAYER;
1526    
1527     $self->enter_link;
1528    
1529     (Coro::async {
1530     unless (eval {
1531    
1532     prepare_random_map $exit
1533     if $exit->slaying eq "/!";
1534    
1535     my $path = new cf::path $exit->slaying, $exit->map && $exit->map->path;
1536     $self->goto_map ($path, $exit->stats->hp, $exit->stats->sp);
1537    
1538     1;
1539     }) {
1540     $self->message ("Something went wrong deep within the crossfire server. "
1541     . "I'll try to bring you back to the map you were before. "
1542     . "Please report this to the dungeon master",
1543     cf::NDI_UNIQUE | cf::NDI_RED);
1544    
1545     warn "ERROR in enter_exit: $@";
1546     $self->leave_link;
1547     }
1548     })->prio (1);
1549     }
1550    
1551 root 1.95 =head3 cf::client
1552    
1553     =over 4
1554    
1555     =item $client->send_drawinfo ($text, $flags)
1556    
1557     Sends a drawinfo packet to the client. Circumvents output buffering so
1558     should not be used under normal circumstances.
1559    
1560 root 1.70 =cut
1561    
1562 root 1.95 sub cf::client::send_drawinfo {
1563     my ($self, $text, $flags) = @_;
1564    
1565     utf8::encode $text;
1566     $self->send_packet (sprintf "drawinfo %d %s", $flags, $text);
1567     }
1568    
1569    
1570     =item $success = $client->query ($flags, "text", \&cb)
1571    
1572     Queues a query to the client, calling the given callback with
1573     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
1574     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
1575    
1576     Queries can fail, so check the return code. Or don't, as queries will become
1577     reliable at some point in the future.
1578    
1579     =cut
1580    
1581     sub cf::client::query {
1582     my ($self, $flags, $text, $cb) = @_;
1583    
1584     return unless $self->state == ST_PLAYING
1585     || $self->state == ST_SETUP
1586     || $self->state == ST_CUSTOM;
1587    
1588     $self->state (ST_CUSTOM);
1589    
1590     utf8::encode $text;
1591     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
1592    
1593     $self->send_packet ($self->{query_queue}[0][0])
1594     if @{ $self->{query_queue} } == 1;
1595     }
1596    
1597     cf::client->attach (
1598     on_reply => sub {
1599     my ($ns, $msg) = @_;
1600    
1601     # this weird shuffling is so that direct followup queries
1602     # get handled first
1603     my $queue = delete $ns->{query_queue};
1604    
1605     (shift @$queue)->[1]->($msg);
1606    
1607     push @{ $ns->{query_queue} }, @$queue;
1608    
1609     if (@{ $ns->{query_queue} } == @$queue) {
1610     if (@$queue) {
1611     $ns->send_packet ($ns->{query_queue}[0][0]);
1612     } else {
1613 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
1614 root 1.95 }
1615     }
1616     },
1617     );
1618    
1619 root 1.96 =item $client->coro (\&cb)
1620    
1621     Create a new coroutine, running the specified callback. The coroutine will
1622     be automatically cancelled when the client gets destroyed (e.g. on logout,
1623     or loss of connection).
1624    
1625     =cut
1626    
1627     sub cf::client::coro {
1628     my ($self, $cb) = @_;
1629    
1630     my $coro; $coro = async {
1631     eval {
1632     $cb->();
1633     };
1634     warn $@ if $@;
1635 root 1.103 };
1636    
1637     $coro->on_destroy (sub {
1638 root 1.96 delete $self->{_coro}{$coro+0};
1639 root 1.103 });
1640 root 1.96
1641     $self->{_coro}{$coro+0} = $coro;
1642 root 1.103
1643     $coro
1644 root 1.96 }
1645    
1646     cf::client->attach (
1647     on_destroy => sub {
1648     my ($ns) = @_;
1649    
1650 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
1651 root 1.96 },
1652     );
1653    
1654 root 1.95 =back
1655    
1656 root 1.70
1657     =head2 SAFE SCRIPTING
1658    
1659     Functions that provide a safe environment to compile and execute
1660     snippets of perl code without them endangering the safety of the server
1661     itself. Looping constructs, I/O operators and other built-in functionality
1662     is not available in the safe scripting environment, and the number of
1663 root 1.79 functions and methods that can be called is greatly reduced.
1664 root 1.70
1665     =cut
1666 root 1.23
1667 root 1.42 our $safe = new Safe "safe";
1668 root 1.23 our $safe_hole = new Safe::Hole;
1669    
1670     $SIG{FPE} = 'IGNORE';
1671    
1672     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
1673    
1674 root 1.25 # here we export the classes and methods available to script code
1675    
1676 root 1.70 =pod
1677    
1678     The following fucntions and emthods are available within a safe environment:
1679    
1680 elmex 1.91 cf::object contr pay_amount pay_player map
1681 root 1.70 cf::object::player player
1682     cf::player peaceful
1683 elmex 1.91 cf::map trigger
1684 root 1.70
1685     =cut
1686    
1687 root 1.25 for (
1688 elmex 1.91 ["cf::object" => qw(contr pay_amount pay_player map)],
1689 root 1.25 ["cf::object::player" => qw(player)],
1690     ["cf::player" => qw(peaceful)],
1691 elmex 1.91 ["cf::map" => qw(trigger)],
1692 root 1.25 ) {
1693     no strict 'refs';
1694     my ($pkg, @funs) = @$_;
1695 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
1696 root 1.25 for @funs;
1697     }
1698 root 1.23
1699 root 1.70 =over 4
1700    
1701     =item @retval = safe_eval $code, [var => value, ...]
1702    
1703     Compiled and executes the given perl code snippet. additional var/value
1704     pairs result in temporary local (my) scalar variables of the given name
1705     that are available in the code snippet. Example:
1706    
1707     my $five = safe_eval '$first + $second', first => 1, second => 4;
1708    
1709     =cut
1710    
1711 root 1.23 sub safe_eval($;@) {
1712     my ($code, %vars) = @_;
1713    
1714     my $qcode = $code;
1715     $qcode =~ s/"/‟/g; # not allowed in #line filenames
1716     $qcode =~ s/\n/\\n/g;
1717    
1718     local $_;
1719 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
1720 root 1.23
1721 root 1.42 my $eval =
1722 root 1.23 "do {\n"
1723     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
1724     . "#line 0 \"{$qcode}\"\n"
1725     . $code
1726     . "\n}"
1727 root 1.25 ;
1728    
1729     sub_generation_inc;
1730 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
1731 root 1.25 sub_generation_inc;
1732    
1733 root 1.42 if ($@) {
1734     warn "$@";
1735     warn "while executing safe code '$code'\n";
1736     warn "with arguments " . (join " ", %vars) . "\n";
1737     }
1738    
1739 root 1.25 wantarray ? @res : $res[0]
1740 root 1.23 }
1741    
1742 root 1.69 =item cf::register_script_function $function => $cb
1743    
1744     Register a function that can be called from within map/npc scripts. The
1745     function should be reasonably secure and should be put into a package name
1746     like the extension.
1747    
1748     Example: register a function that gets called whenever a map script calls
1749     C<rent::overview>, as used by the C<rent> extension.
1750    
1751     cf::register_script_function "rent::overview" => sub {
1752     ...
1753     };
1754    
1755     =cut
1756    
1757 root 1.23 sub register_script_function {
1758     my ($fun, $cb) = @_;
1759    
1760     no strict 'refs';
1761 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
1762 root 1.23 }
1763    
1764 root 1.70 =back
1765    
1766 root 1.71 =cut
1767    
1768 root 1.23 #############################################################################
1769 root 1.65
1770     =head2 EXTENSION DATABASE SUPPORT
1771    
1772     Crossfire maintains a very simple database for extension use. It can
1773     currently store anything that can be serialised using Storable, which
1774     excludes objects.
1775    
1776     The parameter C<$family> should best start with the name of the extension
1777     using it, it should be unique.
1778    
1779     =over 4
1780    
1781     =item $hashref = cf::db_get $family
1782    
1783     Return a hashref for use by the extension C<$family>, which can be
1784     modified. After modifications, you have to call C<cf::db_dirty> or
1785     C<cf::db_sync>.
1786    
1787     =item $value = cf::db_get $family => $key
1788    
1789     Returns a single value from the database
1790    
1791     =item cf::db_put $family => $hashref
1792    
1793     Stores the given family hashref into the database. Updates are delayed, if
1794     you want the data to be synced to disk immediately, use C<cf::db_sync>.
1795    
1796     =item cf::db_put $family => $key => $value
1797    
1798     Stores the given C<$value> in the family hash. Updates are delayed, if you
1799     want the data to be synced to disk immediately, use C<cf::db_sync>.
1800    
1801     =item cf::db_dirty
1802    
1803     Marks the database as dirty, to be updated at a later time.
1804    
1805     =item cf::db_sync
1806    
1807     Immediately write the database to disk I<if it is dirty>.
1808    
1809     =cut
1810    
1811 root 1.78 our $DB;
1812    
1813 root 1.65 {
1814 root 1.66 my $path = cf::localdir . "/database.pst";
1815 root 1.65
1816     sub db_load() {
1817     warn "loading database $path\n";#d# remove later
1818 root 1.78 $DB = stat $path ? Storable::retrieve $path : { };
1819 root 1.65 }
1820    
1821     my $pid;
1822    
1823     sub db_save() {
1824     warn "saving database $path\n";#d# remove later
1825     waitpid $pid, 0 if $pid;
1826 root 1.67 if (0 == ($pid = fork)) {
1827 root 1.78 $DB->{_meta}{version} = 1;
1828     Storable::nstore $DB, "$path~";
1829 root 1.65 rename "$path~", $path;
1830     cf::_exit 0 if defined $pid;
1831     }
1832     }
1833    
1834     my $dirty;
1835    
1836     sub db_sync() {
1837     db_save if $dirty;
1838     undef $dirty;
1839     }
1840    
1841 root 1.87 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, data => WF_AUTOCANCEL, cb => sub {
1842 root 1.65 db_sync;
1843     });
1844    
1845     sub db_dirty() {
1846     $dirty = 1;
1847     $idle->start;
1848     }
1849    
1850     sub db_get($;$) {
1851     @_ >= 2
1852 root 1.78 ? $DB->{$_[0]}{$_[1]}
1853     : ($DB->{$_[0]} ||= { })
1854 root 1.65 }
1855    
1856     sub db_put($$;$) {
1857     if (@_ >= 3) {
1858 root 1.78 $DB->{$_[0]}{$_[1]} = $_[2];
1859 root 1.65 } else {
1860 root 1.78 $DB->{$_[0]} = $_[1];
1861 root 1.65 }
1862     db_dirty;
1863     }
1864 root 1.67
1865 root 1.93 cf::global->attach (
1866     prio => 10000,
1867 root 1.67 on_cleanup => sub {
1868     db_sync;
1869     },
1870 root 1.93 );
1871 root 1.65 }
1872    
1873     #############################################################################
1874 root 1.34 # the server's main()
1875    
1876 root 1.73 sub cfg_load {
1877 root 1.72 open my $fh, "<:utf8", cf::confdir . "/config"
1878     or return;
1879    
1880     local $/;
1881     *CFG = YAML::Syck::Load <$fh>;
1882     }
1883    
1884 root 1.39 sub main {
1885 root 1.108 # we must not ever block the main coroutine
1886     local $Coro::idle = sub {
1887 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
1888 root 1.108 (Coro::unblock_sub {
1889     Event::one_event;
1890     })->();
1891     };
1892    
1893 root 1.73 cfg_load;
1894 root 1.65 db_load;
1895 root 1.61 load_extensions;
1896 root 1.34 Event::loop;
1897     }
1898    
1899     #############################################################################
1900 root 1.22 # initialisation
1901    
1902 root 1.111 sub reload() {
1903 root 1.106 # can/must only be called in main
1904     if ($Coro::current != $Coro::main) {
1905     warn "can only reload from main coroutine\n";
1906     return;
1907     }
1908    
1909 root 1.103 warn "reloading...";
1910    
1911 root 1.106 local $FREEZE = 1;
1912     cf::emergency_save;
1913    
1914 root 1.103 eval {
1915 root 1.106 # if anything goes wrong in here, we should simply crash as we already saved
1916 root 1.65
1917     # cancel all watchers
1918 root 1.87 for (Event::all_watchers) {
1919     $_->cancel if $_->data & WF_AUTOCANCEL;
1920     }
1921 root 1.65
1922 root 1.103 # cancel all extension coros
1923     $_->cancel for values %EXT_CORO;
1924     %EXT_CORO = ();
1925    
1926 root 1.65 # unload all extensions
1927     for (@exts) {
1928 root 1.103 warn "unloading <$_>";
1929 root 1.65 unload_extension $_;
1930     }
1931    
1932     # unload all modules loaded from $LIBDIR
1933     while (my ($k, $v) = each %INC) {
1934     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
1935    
1936 root 1.103 warn "removing <$k>";
1937 root 1.65 delete $INC{$k};
1938    
1939     $k =~ s/\.pm$//;
1940     $k =~ s/\//::/g;
1941    
1942     if (my $cb = $k->can ("unload_module")) {
1943     $cb->();
1944     }
1945    
1946     Symbol::delete_package $k;
1947     }
1948    
1949     # sync database to disk
1950     cf::db_sync;
1951 root 1.103 IO::AIO::flush;
1952 root 1.65
1953     # get rid of safe::, as good as possible
1954     Symbol::delete_package "safe::$_"
1955 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
1956 root 1.65
1957     # remove register_script_function callbacks
1958     # TODO
1959    
1960     # unload cf.pm "a bit"
1961     delete $INC{"cf.pm"};
1962    
1963     # don't, removes xs symbols, too,
1964     # and global variables created in xs
1965     #Symbol::delete_package __PACKAGE__;
1966    
1967     # reload cf.pm
1968 root 1.103 warn "reloading cf.pm";
1969 root 1.65 require cf;
1970 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
1971    
1972 root 1.73 # load config and database again
1973     cf::cfg_load;
1974 root 1.65 cf::db_load;
1975    
1976     # load extensions
1977 root 1.103 warn "load extensions";
1978 root 1.65 cf::load_extensions;
1979    
1980     # reattach attachments to objects
1981 root 1.103 warn "reattach";
1982 root 1.65 _global_reattach;
1983     };
1984    
1985 root 1.106 if ($@) {
1986     warn $@;
1987     warn "error while reloading, exiting.";
1988     exit 1;
1989     }
1990    
1991     warn "reloaded successfully";
1992 root 1.65 };
1993    
1994 root 1.108 #############################################################################
1995    
1996     unless ($LINK_MAP) {
1997     $LINK_MAP = cf::map::new;
1998    
1999     $LINK_MAP->width (41);
2000     $LINK_MAP->height (41);
2001     $LINK_MAP->alloc;
2002     $LINK_MAP->path ("{link}");
2003     $LINK_MAP->{path} = bless { path => "{link}" }, "cf::path";
2004     $LINK_MAP->in_memory (MAP_IN_MEMORY);
2005 root 1.110
2006     # dirty hack because... archetypes are not yet loaded
2007     Event->timer (
2008     after => 2,
2009     cb => sub {
2010     $_[0]->w->cancel;
2011    
2012     # provide some exits "home"
2013     my $exit = cf::object::new "exit";
2014    
2015     $exit->slaying ($EMERGENCY_POSITION->[0]);
2016     $exit->stats->hp ($EMERGENCY_POSITION->[1]);
2017     $exit->stats->sp ($EMERGENCY_POSITION->[2]);
2018    
2019     $LINK_MAP->insert ($exit->clone, 19, 19);
2020     $LINK_MAP->insert ($exit->clone, 19, 20);
2021     $LINK_MAP->insert ($exit->clone, 19, 21);
2022     $LINK_MAP->insert ($exit->clone, 20, 19);
2023     $LINK_MAP->insert ($exit->clone, 20, 21);
2024     $LINK_MAP->insert ($exit->clone, 21, 19);
2025     $LINK_MAP->insert ($exit->clone, 21, 20);
2026     $LINK_MAP->insert ($exit->clone, 21, 21);
2027    
2028     $exit->destroy;
2029     });
2030    
2031     $LINK_MAP->{deny_save} = 1;
2032     $LINK_MAP->{deny_reset} = 1;
2033    
2034     $cf::MAP{$LINK_MAP->path} = $LINK_MAP;
2035 root 1.108 }
2036    
2037 root 1.85 register "<global>", __PACKAGE__;
2038    
2039 root 1.111 register_command "reload" => sub {
2040 root 1.65 my ($who, $arg) = @_;
2041    
2042     if ($who->flag (FLAG_WIZ)) {
2043 root 1.107 $who->message ("start of reload.");
2044 root 1.111 reload;
2045 root 1.107 $who->message ("end of reload.");
2046 root 1.65 }
2047     };
2048    
2049 root 1.27 unshift @INC, $LIBDIR;
2050 root 1.17
2051 root 1.35 $TICK_WATCHER = Event->timer (
2052 root 1.104 reentrant => 0,
2053     prio => 0,
2054     at => $NEXT_TICK || $TICK,
2055     data => WF_AUTOCANCEL,
2056     cb => sub {
2057 root 1.103 unless ($FREEZE) {
2058     cf::server_tick; # one server iteration
2059     $RUNTIME += $TICK;
2060     }
2061 root 1.35
2062     $NEXT_TICK += $TICK;
2063    
2064 root 1.78 # if we are delayed by four ticks or more, skip them all
2065 root 1.103 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4;
2066 root 1.35
2067     $TICK_WATCHER->at ($NEXT_TICK);
2068     $TICK_WATCHER->start;
2069     },
2070     );
2071    
2072 root 1.80 IO::AIO::max_poll_time $TICK * 0.2;
2073 root 1.77
2074 root 1.108 Event->io (
2075     fd => IO::AIO::poll_fileno,
2076     poll => 'r',
2077     prio => 5,
2078     data => WF_AUTOCANCEL,
2079     cb => \&IO::AIO::poll_cb,
2080     );
2081    
2082     Event->timer (
2083     data => WF_AUTOCANCEL,
2084     after => 0,
2085     interval => 10,
2086     cb => sub {
2087     (Coro::unblock_sub {
2088     write_runtime
2089     or warn "ERROR: unable to write runtime file: $!";
2090     })->();
2091     },
2092     );
2093 root 1.103
2094 root 1.1 1
2095