ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.110
Committed: Mon Jan 1 11:21:55 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.109: +435 -3 lines
Log Message:
- integrated most of the map/exit handling into cf.pm
  (it grows too large, should be split 'somehow', but thats not easy)
- moved the swap/reste scheduler into an extension
- imrpoved exit/sync logic

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