ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.185
Committed: Fri Jan 19 23:32:52 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.184: +2 -1 lines
Log Message:
wait_for_tick in synchronously executed code. very, very bad idea

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