ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.169
Committed: Sun Jan 14 01:09:46 2007 UTC (17 years, 5 months ago) by root
Branch: MAIN
Changes since 1.168: +3 -1 lines
Log Message:
re-bless in case extensions have been reloaded

File Contents

# User Rev Content
1 root 1.1 package cf;
2    
3 root 1.96 use utf8;
4     use strict;
5    
6 root 1.1 use Symbol;
7     use List::Util;
8 root 1.6 use Storable;
9 root 1.23 use Opcode;
10     use Safe;
11     use Safe::Hole;
12 root 1.19
13 root 1.127 use Coro 3.3 ();
14 root 1.96 use Coro::Event;
15     use Coro::Timer;
16     use Coro::Signal;
17     use Coro::Semaphore;
18 root 1.105 use Coro::AIO;
19 root 1.96
20 root 1.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.93 sub _attach_cb($$$$) {
509     my ($registry, $event, $prio, $cb) = @_;
510 root 1.39
511     use sort 'stable';
512    
513     $cb = [$prio, $cb];
514    
515     @{$registry->[$event]} = sort
516     { $a->[0] cmp $b->[0] }
517     @{$registry->[$event] || []}, $cb;
518     }
519    
520 root 1.100 # hack
521     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
522    
523 root 1.39 # attach handles attaching event callbacks
524     # the only thing the caller has to do is pass the correct
525     # registry (== where the callback attaches to).
526 root 1.93 sub _attach {
527 root 1.45 my ($registry, $klass, @arg) = @_;
528 root 1.39
529 root 1.93 my $object_type;
530 root 1.39 my $prio = 0;
531     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
532    
533 root 1.100 #TODO: get rid of this hack
534     if ($attachable_klass{$klass}) {
535     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
536     }
537    
538 root 1.45 while (@arg) {
539     my $type = shift @arg;
540 root 1.39
541     if ($type eq "prio") {
542 root 1.45 $prio = shift @arg;
543 root 1.39
544 root 1.93 } elsif ($type eq "type") {
545     $object_type = shift @arg;
546     $registry = $CB_TYPE[$object_type] ||= [];
547    
548     } elsif ($type eq "subtype") {
549     defined $object_type or Carp::croak "subtype specified without type";
550     my $object_subtype = shift @arg;
551     $registry = $CB_TYPE[$object_type + $object_subtype * NUM_SUBTYPES] ||= [];
552    
553 root 1.39 } elsif ($type eq "package") {
554 root 1.45 my $pkg = shift @arg;
555 root 1.39
556     while (my ($name, $id) = each %cb_id) {
557     if (my $cb = $pkg->can ($name)) {
558 root 1.93 _attach_cb $registry, $id, $prio, $cb;
559 root 1.39 }
560     }
561    
562     } elsif (exists $cb_id{$type}) {
563 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
564 root 1.39
565     } elsif (ref $type) {
566     warn "attaching objects not supported, ignoring.\n";
567    
568     } else {
569 root 1.45 shift @arg;
570 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
571     }
572     }
573     }
574    
575 root 1.93 sub _object_attach {
576 root 1.48 my ($obj, $name, %arg) = @_;
577 root 1.46
578 root 1.55 return if exists $obj->{_attachment}{$name};
579    
580 root 1.46 if (my $attach = $attachment{$name}) {
581     my $registry = $obj->registry;
582    
583 root 1.47 for (@$attach) {
584     my ($klass, @attach) = @$_;
585 root 1.93 _attach $registry, $klass, @attach;
586 root 1.47 }
587 root 1.46
588 root 1.48 $obj->{$name} = \%arg;
589 root 1.46 } else {
590     warn "object uses attachment '$name' that is not available, postponing.\n";
591     }
592    
593 root 1.50 $obj->{_attachment}{$name} = undef;
594 root 1.46 }
595    
596 root 1.93 sub cf::attachable::attach {
597     if (ref $_[0]) {
598     _object_attach @_;
599     } else {
600     _attach shift->_attach_registry, @_;
601     }
602 root 1.55 };
603 root 1.46
604 root 1.54 # all those should be optimised
605 root 1.93 sub cf::attachable::detach {
606 root 1.54 my ($obj, $name) = @_;
607 root 1.46
608 root 1.93 if (ref $obj) {
609     delete $obj->{_attachment}{$name};
610     reattach ($obj);
611     } else {
612     Carp::croak "cannot, currently, detach class attachments";
613     }
614 root 1.55 };
615    
616 root 1.93 sub cf::attachable::attached {
617 root 1.55 my ($obj, $name) = @_;
618    
619     exists $obj->{_attachment}{$name}
620 root 1.39 }
621    
622 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
623 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
624     sub cf::\L$klass\E::_attach_registry {
625     (\\\@CB_$klass, KLASS_$klass)
626     }
627 root 1.45
628 root 1.93 sub cf::\L$klass\E::attachment {
629     my \$name = shift;
630 root 1.39
631 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
632     }
633     ";
634     die if $@;
635 root 1.52 }
636    
637 root 1.39 our $override;
638 root 1.45 our @invoke_results = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
639 root 1.39
640 root 1.45 sub override {
641     $override = 1;
642     @invoke_results = ();
643 root 1.39 }
644    
645 root 1.45 sub do_invoke {
646 root 1.39 my $event = shift;
647 root 1.40 my $callbacks = shift;
648 root 1.39
649 root 1.45 @invoke_results = ();
650    
651 root 1.39 local $override;
652    
653 root 1.40 for (@$callbacks) {
654 root 1.39 eval { &{$_->[1]} };
655    
656     if ($@) {
657     warn "$@";
658 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
659 root 1.39 override;
660     }
661    
662     return 1 if $override;
663     }
664    
665     0
666     }
667    
668 root 1.96 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
669 root 1.55
670 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
671 root 1.55
672 root 1.96 Generate an object-specific event with the given arguments.
673 root 1.55
674 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
675 root 1.55 removed in future versions), and there is no public API to access override
676     results (if you must, access C<@cf::invoke_results> directly).
677    
678     =back
679    
680 root 1.71 =cut
681    
682 root 1.70 #############################################################################
683 root 1.45 # object support
684    
685 root 1.102 sub reattach {
686     # basically do the same as instantiate, without calling instantiate
687     my ($obj) = @_;
688    
689 root 1.169 bless $obj, ref $obj; # re-bless in case extensions have been reloaded
690    
691 root 1.102 my $registry = $obj->registry;
692    
693     @$registry = ();
694    
695     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
696    
697     for my $name (keys %{ $obj->{_attachment} || {} }) {
698     if (my $attach = $attachment{$name}) {
699     for (@$attach) {
700     my ($klass, @attach) = @$_;
701     _attach $registry, $klass, @attach;
702     }
703     } else {
704     warn "object uses attachment '$name' that is not available, postponing.\n";
705     }
706     }
707     }
708    
709 root 1.100 cf::attachable->attach (
710     prio => -1000000,
711     on_instantiate => sub {
712     my ($obj, $data) = @_;
713 root 1.45
714 root 1.100 $data = from_json $data;
715 root 1.45
716 root 1.100 for (@$data) {
717     my ($name, $args) = @$_;
718 root 1.49
719 root 1.100 $obj->attach ($name, %{$args || {} });
720     }
721     },
722 root 1.102 on_reattach => \&reattach,
723 root 1.100 on_clone => sub {
724     my ($src, $dst) = @_;
725    
726     @{$dst->registry} = @{$src->registry};
727    
728     %$dst = %$src;
729    
730     %{$dst->{_attachment}} = %{$src->{_attachment}}
731     if exists $src->{_attachment};
732     },
733     );
734 root 1.45
735 root 1.46 sub object_freezer_save {
736 root 1.59 my ($filename, $rdata, $objs) = @_;
737 root 1.46
738 root 1.105 sync_job {
739     if (length $$rdata) {
740     warn sprintf "saving %s (%d,%d)\n",
741     $filename, length $$rdata, scalar @$objs;
742 root 1.60
743 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
744 root 1.60 chmod SAVE_MODE, $fh;
745 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
746     aio_fsync $fh;
747 root 1.60 close $fh;
748 root 1.105
749     if (@$objs) {
750     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
751     chmod SAVE_MODE, $fh;
752     my $data = Storable::nfreeze { version => 1, objs => $objs };
753     aio_write $fh, 0, (length $data), $data, 0;
754     aio_fsync $fh;
755     close $fh;
756     aio_rename "$filename.pst~", "$filename.pst";
757     }
758     } else {
759     aio_unlink "$filename.pst";
760     }
761    
762     aio_rename "$filename~", $filename;
763 root 1.60 } else {
764 root 1.105 warn "FATAL: $filename~: $!\n";
765 root 1.60 }
766 root 1.59 } else {
767 root 1.105 aio_unlink $filename;
768     aio_unlink "$filename.pst";
769 root 1.59 }
770 root 1.45 }
771     }
772    
773 root 1.80 sub object_freezer_as_string {
774     my ($rdata, $objs) = @_;
775    
776     use Data::Dumper;
777    
778 root 1.81 $$rdata . Dumper $objs
779 root 1.80 }
780    
781 root 1.46 sub object_thawer_load {
782     my ($filename) = @_;
783    
784 root 1.105 my ($data, $av);
785 root 1.61
786 root 1.105 (aio_load $filename, $data) >= 0
787     or return;
788 root 1.61
789 root 1.105 unless (aio_stat "$filename.pst") {
790     (aio_load "$filename.pst", $av) >= 0
791     or return;
792 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
793 root 1.61 }
794 root 1.45
795 root 1.118 warn sprintf "loading %s (%d)\n",
796     $filename, length $data, scalar @{$av || []};#d#
797 root 1.105 return ($data, $av);
798 root 1.45 }
799    
800     #############################################################################
801 root 1.85 # command handling &c
802 root 1.39
803 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
804 root 1.1
805 root 1.85 Register a callback for execution when the client sends the user command
806     $name.
807 root 1.5
808 root 1.85 =cut
809 root 1.5
810 root 1.85 sub register_command {
811     my ($name, $cb) = @_;
812 root 1.5
813 root 1.85 my $caller = caller;
814     #warn "registering command '$name/$time' to '$caller'";
815 root 1.1
816 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
817 root 1.1 }
818    
819 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
820 root 1.1
821 root 1.85 Register a callbackf ro execution when the client sends an extcmd packet.
822 root 1.1
823 root 1.85 If the callback returns something, it is sent back as if reply was being
824     called.
825 root 1.1
826 root 1.85 =cut
827 root 1.1
828 root 1.16 sub register_extcmd {
829     my ($name, $cb) = @_;
830    
831 root 1.159 $EXTCMD{$name} = $cb;
832 root 1.16 }
833    
834 root 1.93 cf::player->attach (
835 root 1.85 on_command => sub {
836     my ($pl, $name, $params) = @_;
837    
838     my $cb = $COMMAND{$name}
839     or return;
840    
841     for my $cmd (@$cb) {
842     $cmd->[1]->($pl->ob, $params);
843     }
844    
845     cf::override;
846     },
847     on_extcmd => sub {
848     my ($pl, $buf) = @_;
849    
850     my $msg = eval { from_json $buf };
851    
852     if (ref $msg) {
853     if (my $cb = $EXTCMD{$msg->{msgtype}}) {
854 root 1.159 if (my %reply = $cb->($pl, $msg)) {
855 root 1.85 $pl->ext_reply ($msg->{msgid}, %reply);
856     }
857     }
858     } else {
859     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
860     }
861    
862     cf::override;
863     },
864 root 1.93 );
865 root 1.85
866 root 1.1 sub load_extension {
867     my ($path) = @_;
868    
869     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
870 root 1.5 my $base = $1;
871 root 1.1 my $pkg = $1;
872     $pkg =~ s/[^[:word:]]/_/g;
873 root 1.41 $pkg = "ext::$pkg";
874 root 1.1
875 root 1.160 warn "... loading '$path' into '$pkg'\n";
876 root 1.1
877     open my $fh, "<:utf8", $path
878     or die "$path: $!";
879    
880     my $source =
881     "package $pkg; use strict; use utf8;\n"
882     . "#line 1 \"$path\"\n{\n"
883     . (do { local $/; <$fh> })
884     . "\n};\n1";
885    
886 root 1.166 unless (eval $source) {
887     my $msg = $@ ? "$path: $@\n"
888     : "extension disabled.\n";
889     if ($source =~ /^#!.*perl.*#.*MANDATORY/m) { # ugly match
890     warn $@;
891     warn "mandatory extension failed to load, exiting.\n";
892     exit 1;
893     }
894     die $@;
895     }
896 root 1.1
897 root 1.159 push @EXTS, $pkg;
898 root 1.1 }
899    
900     sub load_extensions {
901     for my $ext (<$LIBDIR/*.ext>) {
902 root 1.3 next unless -r $ext;
903 root 1.2 eval {
904     load_extension $ext;
905     1
906     } or warn "$ext not loaded: $@";
907 root 1.1 }
908     }
909    
910 root 1.8 #############################################################################
911 root 1.70
912     =head2 CORE EXTENSIONS
913    
914     Functions and methods that extend core crossfire objects.
915    
916 root 1.143 =cut
917    
918     package cf::player;
919    
920 root 1.154 use Coro::AIO;
921    
922 root 1.95 =head3 cf::player
923    
924 root 1.70 =over 4
925 root 1.22
926 root 1.143 =item cf::player::find $login
927 root 1.23
928 root 1.143 Returns the given player object, loading it if necessary (might block).
929 root 1.23
930     =cut
931    
932 root 1.145 sub playerdir($) {
933     cf::localdir
934     . "/"
935     . cf::playerdir
936     . "/"
937     . (ref $_[0] ? $_[0]->ob->name : $_[0])
938     }
939    
940 root 1.143 sub path($) {
941 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
942    
943     (playerdir $login) . "/$login.pl"
944 root 1.143 }
945    
946     sub find_active($) {
947     $cf::PLAYER{$_[0]}
948     and $cf::PLAYER{$_[0]}->active
949     and $cf::PLAYER{$_[0]}
950     }
951    
952     sub exists($) {
953     my ($login) = @_;
954    
955     $cf::PLAYER{$login}
956     or cf::sync_job { !aio_stat $login }
957     }
958    
959     sub find($) {
960     return $cf::PLAYER{$_[0]} || do {
961     my $login = $_[0];
962    
963     my $guard = cf::lock_acquire "user_find:$login";
964    
965 root 1.151 $cf::PLAYER{$_[0]} || do {
966     my $pl = load_pl path $login
967     or return;
968     $cf::PLAYER{$login} = $pl
969     }
970     }
971 root 1.143 }
972    
973     sub save($) {
974     my ($pl) = @_;
975    
976     return if $pl->{deny_save};
977    
978     my $path = path $pl;
979     my $guard = cf::lock_acquire "user_save:$path";
980    
981     return if $pl->{deny_save};
982 root 1.146
983 root 1.154 aio_mkdir playerdir $pl, 0770;
984 root 1.143 $pl->{last_save} = $cf::RUNTIME;
985    
986     $pl->save_pl ($path);
987     Coro::cede;
988     }
989    
990     sub new($) {
991     my ($login) = @_;
992    
993     my $self = create;
994    
995     $self->ob->name ($login);
996     $self->{deny_save} = 1;
997    
998     $cf::PLAYER{$login} = $self;
999    
1000     $self
1001 root 1.23 }
1002    
1003 root 1.154 =item $pl->quit_character
1004    
1005     Nukes the player without looking back. If logged in, the connection will
1006     be destroyed. May block for a long time.
1007    
1008     =cut
1009    
1010 root 1.145 sub quit_character {
1011     my ($pl) = @_;
1012    
1013     $pl->{deny_save} = 1;
1014     $pl->password ("*"); # this should lock out the player until we nuked the dir
1015    
1016     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1017     $pl->deactivate;
1018     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1019     $pl->ns->destroy if $pl->ns;
1020    
1021     my $path = playerdir $pl;
1022     my $temp = "$path~$cf::RUNTIME~deleting~";
1023 root 1.154 aio_rename $path, $temp;
1024 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1025     $pl->destroy;
1026     IO::AIO::aio_rmtree $temp;
1027 root 1.145 }
1028    
1029 root 1.154 =item cf::player::list_logins
1030    
1031     Returns am arrayref of all valid playernames in the system, can take a
1032     while and may block, so not sync_job-capable, ever.
1033    
1034     =cut
1035    
1036     sub list_logins {
1037     my $dirs = aio_readdir cf::localdir . "/" . cf::playerdir
1038     or return [];
1039    
1040     my @logins;
1041    
1042     for my $login (@$dirs) {
1043     my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next;
1044     aio_read $fh, 0, 512, my $buf, 0 or next;
1045 root 1.155 $buf !~ /^password -------------$/m or next; # official not-valid tag
1046 root 1.154
1047     utf8::decode $login;
1048     push @logins, $login;
1049     }
1050    
1051     \@logins
1052     }
1053    
1054     =item $player->maps
1055    
1056 root 1.166 Returns an arrayref of map paths that are private for this
1057 root 1.154 player. May block.
1058    
1059     =cut
1060    
1061     sub maps($) {
1062     my ($pl) = @_;
1063    
1064     my $files = aio_readdir playerdir $pl
1065     or return;
1066    
1067     my @paths;
1068    
1069     for (@$files) {
1070     utf8::decode $_;
1071     next if /\.(?:pl|pst)$/;
1072 root 1.158 next unless /^$PATH_SEP/o;
1073 root 1.154
1074 root 1.167 s/\.map$//;
1075 root 1.166 push @paths, "~" . $pl->ob->name . "/" . $_;
1076 root 1.154 }
1077    
1078     \@paths
1079     }
1080    
1081 root 1.95 =item $player->ext_reply ($msgid, $msgtype, %msg)
1082    
1083     Sends an ext reply to the player.
1084    
1085     =cut
1086    
1087 root 1.143 sub ext_reply($$$%) {
1088 root 1.95 my ($self, $id, %msg) = @_;
1089    
1090     $msg{msgid} = $id;
1091    
1092 root 1.143 $self->send ("ext " . cf::to_json \%msg);
1093 root 1.95 }
1094    
1095 root 1.143 package cf;
1096    
1097 root 1.95 =back
1098    
1099 root 1.110
1100     =head3 cf::map
1101    
1102     =over 4
1103    
1104     =cut
1105    
1106     package cf::map;
1107    
1108     use Fcntl;
1109     use Coro::AIO;
1110    
1111 root 1.166 use overload
1112     '""' => \&as_string;
1113    
1114 root 1.133 our $MAX_RESET = 3600;
1115     our $DEFAULT_RESET = 3000;
1116 root 1.110
1117     sub generate_random_map {
1118 root 1.166 my ($self, $rmp) = @_;
1119 root 1.110
1120     # mit "rum" bekleckern, nicht
1121 root 1.166 $self->_create_random_map (
1122 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1123     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1124     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1125     $rmp->{exit_on_final_map},
1126     $rmp->{xsize}, $rmp->{ysize},
1127     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1128     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1129     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1130     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1131     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1132 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1133     )
1134 root 1.110 }
1135    
1136 root 1.166 sub register {
1137     my (undef, $regex) = @_;
1138     my $pkg = caller;
1139    
1140     no strict;
1141     push @{"$pkg\::ISA"}, __PACKAGE__;
1142    
1143     $EXT_MAP{$pkg} = qr<$regex>;
1144     }
1145    
1146     # also paths starting with '/'
1147     $EXT_MAP{"cf::map"} = qr{^(?=/)};
1148    
1149     sub normalise {
1150     my ($path, $base) = @_;
1151    
1152     # map plan:
1153     #
1154     # /! non-realised random map exit (special hack!)
1155     # {... are special paths that are not being touched
1156     # ?xxx/... are special absolute paths
1157     # ?random/... random maps
1158     # /... normal maps
1159     # ~user/... per-player map of a specific user
1160    
1161     $path =~ s/$PATH_SEP/\//go;
1162    
1163     # treat it as relative path if it starts with
1164     # something that looks reasonable
1165     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1166     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1167    
1168     $base =~ s{[^/]+/?$}{};
1169     $path = "$base/$path";
1170     }
1171    
1172     for ($path) {
1173     redo if s{//}{/};
1174     redo if s{/\.?/}{/};
1175     redo if s{/[^/]+/\.\./}{/};
1176     }
1177    
1178     $path
1179     }
1180    
1181     sub new_from_path {
1182     my (undef, $path, $base) = @_;
1183    
1184     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1185    
1186     $path = normalise $path, $base;
1187    
1188     for my $pkg (keys %EXT_MAP) {
1189     if ($path =~ $EXT_MAP{$pkg}) {
1190     my $self = bless cf::map::new, $pkg;
1191     $self->{path} = $path; $self->path ($path);
1192     $self->init; # pass $1 etc.
1193     return $self;
1194     }
1195     }
1196    
1197     Carp::carp "unable to resolve path '$path'.";
1198     ()
1199     }
1200    
1201     sub init {
1202     my ($self) = @_;
1203    
1204     $self
1205     }
1206    
1207     sub as_string {
1208     my ($self) = @_;
1209    
1210     "$self->{path}"
1211     }
1212    
1213     # the displayed name, this is a one way mapping
1214     sub visible_name {
1215     &as_string
1216     }
1217    
1218     # the original (read-only) location
1219     sub load_path {
1220     my ($self) = @_;
1221    
1222     sprintf "%s/%s/%s", cf::datadir, cf::mapdir, $self->{path}
1223     }
1224    
1225     # the temporary/swap location
1226     sub save_path {
1227     my ($self) = @_;
1228    
1229 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1230     sprintf "%s/%s/%s", cf::localdir, cf::tmpdir, $path
1231 root 1.166 }
1232    
1233     # the unique path, undef == no special unique path
1234     sub uniq_path {
1235     my ($self) = @_;
1236    
1237 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1238     sprintf "%s/%s/%s", cf::localdir, cf::uniquedir, $path
1239 root 1.166 }
1240    
1241 root 1.110 # and all this just because we cannot iterate over
1242     # all maps in C++...
1243     sub change_all_map_light {
1244     my ($change) = @_;
1245    
1246 root 1.122 $_->change_map_light ($change)
1247     for grep $_->outdoor, values %cf::MAP;
1248 root 1.110 }
1249    
1250 root 1.166 sub unlink_save {
1251     my ($self) = @_;
1252    
1253     utf8::encode (my $save = $self->save_path);
1254     IO::AIO::aioreq_pri 4; IO::AIO::aio_unlink $save;
1255     IO::AIO::aioreq_pri 4; IO::AIO::aio_unlink "$save.pst";
1256     }
1257    
1258     sub load_header_from($) {
1259     my ($self, $path) = @_;
1260 root 1.110
1261     utf8::encode $path;
1262     aio_open $path, O_RDONLY, 0
1263     or return;
1264    
1265 root 1.166 $self->_load_header ($path)
1266 root 1.110 or return;
1267    
1268 root 1.166 $self->{load_path} = $path;
1269 root 1.135
1270 root 1.166 1
1271     }
1272 root 1.110
1273 root 1.166 sub load_orig {
1274     my ($self) = @_;
1275 root 1.110
1276 root 1.166 $self->load_header_from ($self->load_path)
1277 root 1.110 }
1278    
1279 root 1.166 sub load_temp {
1280     my ($self) = @_;
1281 root 1.110
1282 root 1.166 $self->load_header_from ($self->save_path)
1283     }
1284 root 1.110
1285 root 1.166 sub load_header {
1286     my ($self) = @_;
1287 root 1.110
1288 root 1.166 if ($self->load_temp) {
1289     $self->last_access ((delete $self->{last_access})
1290     || $cf::RUNTIME); #d#
1291     # safety
1292     $self->{instantiate_time} = $cf::RUNTIME
1293     if $self->{instantiate_time} > $cf::RUNTIME;
1294     } else {
1295     $self->load_orig
1296     or return;
1297 root 1.120
1298 root 1.166 $self->{load_original} = 1;
1299     $self->{instantiate_time} = $cf::RUNTIME;
1300     $self->last_access ($cf::RUNTIME);
1301     $self->instantiate;
1302     }
1303 root 1.120
1304 root 1.166 1
1305     }
1306 root 1.110
1307 root 1.166 sub find;
1308     sub find {
1309     my ($path, $origin) = @_;
1310 root 1.134
1311 root 1.166 $path = normalise $path, $origin && $origin->path;
1312 root 1.110
1313 root 1.166 cf::lock_wait "map_find:$path";
1314 root 1.110
1315 root 1.166 $cf::MAP{$path} || do {
1316     my $guard = cf::lock_acquire "map_find:$path";
1317     my $map = new_from_path cf::map $path
1318     or return;
1319 root 1.110
1320 root 1.116 $map->{last_save} = $cf::RUNTIME;
1321 root 1.110
1322 root 1.166 $map->load_header
1323     or return;
1324 root 1.134
1325 root 1.112 if ($map->should_reset) {
1326     $map->reset;
1327 root 1.123 undef $guard;
1328 root 1.133 $map = find $path
1329 root 1.124 or return;
1330 root 1.112 }
1331 root 1.110
1332 root 1.166 $cf::MAP{$path} = $map
1333 root 1.110 }
1334     }
1335    
1336     sub load {
1337     my ($self) = @_;
1338    
1339 root 1.120 my $path = $self->{path};
1340 root 1.166 my $guard = cf::lock_acquire "map_load:$path";
1341 root 1.120
1342 root 1.110 return if $self->in_memory != cf::MAP_SWAPPED;
1343    
1344     $self->in_memory (cf::MAP_LOADING);
1345    
1346     $self->alloc;
1347 root 1.166 $self->_load_objects ($self->{load_path}, 1)
1348 root 1.110 or return;
1349    
1350 root 1.112 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1351     if delete $self->{load_original};
1352 root 1.111
1353 root 1.166 if (my $uniq = $self->uniq_path) {
1354 root 1.110 utf8::encode $uniq;
1355     if (aio_open $uniq, O_RDONLY, 0) {
1356     $self->clear_unique_items;
1357 root 1.166 $self->_load_objects ($uniq, 0);
1358 root 1.110 }
1359     }
1360    
1361 root 1.134 Coro::cede;
1362    
1363 root 1.110 # now do the right thing for maps
1364     $self->link_multipart_objects;
1365    
1366 root 1.166 unless ($self->{deny_activate}) {
1367 root 1.164 $self->decay_objects;
1368 root 1.110 $self->fix_auto_apply;
1369     $self->update_buttons;
1370 root 1.166 Coro::cede;
1371 root 1.110 $self->set_darkness_map;
1372     $self->difficulty ($self->estimate_difficulty)
1373     unless $self->difficulty;
1374 root 1.166 Coro::cede;
1375 root 1.110 $self->activate;
1376     }
1377    
1378 root 1.166 $self->in_memory (cf::MAP_IN_MEMORY);
1379     }
1380    
1381     sub customise_for {
1382     my ($self, $ob) = @_;
1383    
1384     return find "~" . $ob->name . "/" . $self->{path}
1385     if $self->per_player;
1386 root 1.134
1387 root 1.166 $self
1388 root 1.110 }
1389    
1390 root 1.157 # find and load all maps in the 3x3 area around a map
1391     sub load_diag {
1392     my ($map) = @_;
1393    
1394     my @diag; # diagonal neighbours
1395    
1396     for (0 .. 3) {
1397     my $neigh = $map->tile_path ($_)
1398     or next;
1399     $neigh = find $neigh, $map
1400     or next;
1401     $neigh->load;
1402    
1403     push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh],
1404     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1405     }
1406    
1407     for (@diag) {
1408     my $neigh = find @$_
1409     or next;
1410     $neigh->load;
1411     }
1412     }
1413    
1414 root 1.133 sub find_sync {
1415 root 1.110 my ($path, $origin) = @_;
1416    
1417 root 1.157 cf::sync_job { find $path, $origin }
1418 root 1.133 }
1419    
1420     sub do_load_sync {
1421     my ($map) = @_;
1422 root 1.110
1423 root 1.133 cf::sync_job { $map->load };
1424 root 1.110 }
1425    
1426 root 1.157 our %MAP_PREFETCH;
1427     our $MAP_PREFETCHER = Coro::async {
1428     while () {
1429 root 1.166 for my $path (keys %MAP_PREFETCH) {
1430 root 1.157 my $map = find $path
1431     or next;
1432     $map->load;
1433 root 1.166
1434     delete $MAP_PREFETCH{$path};
1435 root 1.157 }
1436     Coro::schedule;
1437     }
1438     };
1439    
1440     sub find_async {
1441     my ($path, $origin) = @_;
1442    
1443 root 1.166 $path = normalise $path, $origin && $origin->{path};
1444 root 1.157
1445 root 1.166 if (my $map = $cf::MAP{$path}) {
1446 root 1.157 return $map if $map->in_memory == cf::MAP_IN_MEMORY;
1447     }
1448    
1449 root 1.166 $MAP_PREFETCH{$path} = 0;
1450 root 1.157 $MAP_PREFETCHER->ready;
1451    
1452     ()
1453     }
1454    
1455 root 1.110 sub save {
1456     my ($self) = @_;
1457    
1458 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1459    
1460 root 1.110 $self->{last_save} = $cf::RUNTIME;
1461    
1462     return unless $self->dirty;
1463    
1464 root 1.166 my $save = $self->save_path; utf8::encode $save;
1465     my $uniq = $self->uniq_path; utf8::encode $uniq;
1466 root 1.117
1467 root 1.110 $self->{load_path} = $save;
1468    
1469     return if $self->{deny_save};
1470    
1471 root 1.132 local $self->{last_access} = $self->last_access;#d#
1472    
1473 root 1.143 cf::async {
1474     $_->contr->save for $self->players;
1475     };
1476    
1477 root 1.110 if ($uniq) {
1478 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1479     $self->_save_objects ($uniq, cf::IO_UNIQUES);
1480 root 1.110 } else {
1481 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1482 root 1.110 }
1483     }
1484    
1485     sub swap_out {
1486     my ($self) = @_;
1487    
1488 root 1.130 # save first because save cedes
1489     $self->save;
1490    
1491 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1492    
1493 root 1.110 return if $self->players;
1494     return if $self->in_memory != cf::MAP_IN_MEMORY;
1495     return if $self->{deny_save};
1496    
1497     $self->clear;
1498     $self->in_memory (cf::MAP_SWAPPED);
1499     }
1500    
1501 root 1.112 sub reset_at {
1502     my ($self) = @_;
1503 root 1.110
1504     # TODO: safety, remove and allow resettable per-player maps
1505 root 1.169 return 1e99 if $self->isa ("ext::map_per_player");#d#
1506 root 1.114 return 1e99 if $self->{deny_reset};
1507 root 1.110
1508 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1509 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1510 root 1.110
1511 root 1.112 $time + $to
1512     }
1513    
1514     sub should_reset {
1515     my ($self) = @_;
1516    
1517     $self->reset_at <= $cf::RUNTIME
1518 root 1.111 }
1519    
1520 root 1.113 sub rename {
1521     my ($self, $new_path) = @_;
1522    
1523 root 1.166 normalise $new_path;
1524    
1525     $self->unlink_save;
1526 root 1.113
1527     delete $cf::MAP{$self->path};
1528 root 1.166 $self->{path} = $new_path; $self->path ($self->{path});
1529 root 1.113 $cf::MAP{$self->path} = $self;
1530    
1531     $self->save;
1532     }
1533    
1534 root 1.110 sub reset {
1535     my ($self) = @_;
1536    
1537 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
1538 root 1.137
1539 root 1.110 return if $self->players;
1540 root 1.166 return if $self->isa ("ext::map_per_player");#d#
1541 root 1.110
1542     warn "resetting map ", $self->path;#d#
1543    
1544 root 1.111 delete $cf::MAP{$self->path};
1545 root 1.110
1546 root 1.167 $self->in_memory (cf::MAP_SWAPPED);
1547     $self->clear;
1548    
1549 root 1.110 $_->clear_links_to ($self) for values %cf::MAP;
1550    
1551 root 1.166 $self->unlink_save;
1552 root 1.111 $self->destroy;
1553 root 1.110 }
1554    
1555 root 1.114 my $nuke_counter = "aaaa";
1556    
1557     sub nuke {
1558     my ($self) = @_;
1559    
1560     $self->{deny_save} = 1;
1561     $self->reset_timeout (1);
1562     $self->rename ("{nuke}/" . ($nuke_counter++));
1563     $self->reset; # polite request, might not happen
1564     }
1565    
1566 root 1.158 =item cf::map::unique_maps
1567    
1568 root 1.166 Returns an arrayref of paths of all shared maps that have
1569 root 1.158 instantiated unique items. May block.
1570    
1571     =cut
1572    
1573     sub unique_maps() {
1574     my $files = aio_readdir cf::localdir . "/" . cf::uniquedir
1575     or return;
1576    
1577     my @paths;
1578    
1579     for (@$files) {
1580     utf8::decode $_;
1581     next if /\.pst$/;
1582     next unless /^$PATH_SEP/o;
1583    
1584 root 1.167 s/\.map$//;
1585 root 1.166 push @paths, $_;
1586 root 1.158 }
1587    
1588     \@paths
1589     }
1590    
1591 root 1.155 package cf;
1592    
1593     =back
1594    
1595     =head3 cf::object
1596    
1597     =cut
1598    
1599     package cf::object;
1600    
1601     =over 4
1602    
1603     =item $ob->inv_recursive
1604 root 1.110
1605 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
1606 root 1.110
1607 root 1.155 =cut
1608 root 1.144
1609 root 1.155 sub inv_recursive_;
1610     sub inv_recursive_ {
1611     map { $_, inv_recursive_ $_->inv } @_
1612     }
1613 root 1.110
1614 root 1.155 sub inv_recursive {
1615     inv_recursive_ inv $_[0]
1616 root 1.110 }
1617    
1618     package cf;
1619    
1620     =back
1621    
1622 root 1.95 =head3 cf::object::player
1623    
1624     =over 4
1625    
1626 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
1627 root 1.28
1628     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
1629     can be C<undef>. Does the right thing when the player is currently in a
1630     dialogue with the given NPC character.
1631    
1632     =cut
1633    
1634 root 1.22 # rough implementation of a future "reply" method that works
1635     # with dialog boxes.
1636 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
1637 root 1.23 sub cf::object::player::reply($$$;$) {
1638     my ($self, $npc, $msg, $flags) = @_;
1639    
1640     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
1641 root 1.22
1642 root 1.24 if ($self->{record_replies}) {
1643     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
1644     } else {
1645     $msg = $npc->name . " says: $msg" if $npc;
1646     $self->message ($msg, $flags);
1647     }
1648 root 1.22 }
1649    
1650 root 1.79 =item $player_object->may ("access")
1651    
1652     Returns wether the given player is authorized to access resource "access"
1653     (e.g. "command_wizcast").
1654    
1655     =cut
1656    
1657     sub cf::object::player::may {
1658     my ($self, $access) = @_;
1659    
1660     $self->flag (cf::FLAG_WIZ) ||
1661     (ref $cf::CFG{"may_$access"}
1662     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
1663     : $cf::CFG{"may_$access"})
1664     }
1665 root 1.70
1666 root 1.115 =item $player_object->enter_link
1667    
1668     Freezes the player and moves him/her to a special map (C<{link}>).
1669    
1670 root 1.166 The player should be reasonably safe there for short amounts of time. You
1671 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
1672    
1673 root 1.166 Will never block.
1674    
1675 root 1.115 =item $player_object->leave_link ($map, $x, $y)
1676    
1677 root 1.166 Moves the player out of the special C<{link}> map onto the specified
1678     map. If the map is not valid (or omitted), the player will be moved back
1679     to the location he/she was before the call to C<enter_link>, or, if that
1680     fails, to the emergency map position.
1681 root 1.115
1682     Might block.
1683    
1684     =cut
1685    
1686 root 1.166 sub link_map {
1687     unless ($LINK_MAP) {
1688     $LINK_MAP = cf::map::find "{link}"
1689     or do { warn "FATAL: unable to provide {link} map, exiting."; exit 1 };
1690     $LINK_MAP->load;
1691     }
1692    
1693     $LINK_MAP
1694     }
1695    
1696 root 1.110 sub cf::object::player::enter_link {
1697     my ($self) = @_;
1698    
1699 root 1.120 $self->deactivate_recursive;
1700    
1701 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
1702 root 1.110
1703 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
1704 root 1.110 if $self->map;
1705    
1706 root 1.166 $self->enter_map ($LINK_MAP || link_map, 20, 20);
1707 root 1.110 }
1708    
1709     sub cf::object::player::leave_link {
1710     my ($self, $map, $x, $y) = @_;
1711    
1712     my $link_pos = delete $self->{_link_pos};
1713    
1714     unless ($map) {
1715     # restore original map position
1716     ($map, $x, $y) = @{ $link_pos || [] };
1717 root 1.133 $map = cf::map::find $map;
1718 root 1.110
1719     unless ($map) {
1720     ($map, $x, $y) = @$EMERGENCY_POSITION;
1721 root 1.133 $map = cf::map::find $map
1722 root 1.110 or die "FATAL: cannot load emergency map\n";
1723     }
1724     }
1725    
1726     ($x, $y) = (-1, -1)
1727     unless (defined $x) && (defined $y);
1728    
1729     # use -1 or undef as default coordinates, not 0, 0
1730     ($x, $y) = ($map->enter_x, $map->enter_y)
1731     if $x <=0 && $y <= 0;
1732    
1733     $map->load;
1734 root 1.157 $map->load_diag;
1735 root 1.110
1736 root 1.143 return unless $self->contr->active;
1737 root 1.110 $self->activate_recursive;
1738     $self->enter_map ($map, $x, $y);
1739     }
1740    
1741 root 1.120 cf::player->attach (
1742     on_logout => sub {
1743     my ($pl) = @_;
1744    
1745     # abort map switching before logout
1746     if ($pl->ob->{_link_pos}) {
1747     cf::sync_job {
1748     $pl->ob->leave_link
1749     };
1750     }
1751     },
1752     on_login => sub {
1753     my ($pl) = @_;
1754    
1755     # try to abort aborted map switching on player login :)
1756     # should happen only on crashes
1757     if ($pl->ob->{_link_pos}) {
1758     $pl->ob->enter_link;
1759 root 1.140 (async {
1760 root 1.120 # we need this sleep as the login has a concurrent enter_exit running
1761     # and this sleep increases chances of the player not ending up in scorn
1762 root 1.140 $pl->ob->reply (undef,
1763     "There was an internal problem at your last logout, "
1764     . "the server will try to bring you to your intended destination in a second.",
1765     cf::NDI_RED);
1766 root 1.120 Coro::Timer::sleep 1;
1767     $pl->ob->leave_link;
1768 root 1.139 })->prio (2);
1769 root 1.120 }
1770     },
1771     );
1772    
1773 root 1.136 =item $player_object->goto ($path, $x, $y)
1774 root 1.110
1775     =cut
1776    
1777 root 1.136 sub cf::object::player::goto {
1778 root 1.110 my ($self, $path, $x, $y) = @_;
1779    
1780     $self->enter_link;
1781    
1782 root 1.140 (async {
1783 root 1.166 my $map = cf::map::find $path;
1784     $map = $map->customise_for ($self) if $map;
1785 root 1.110
1786 root 1.119 # warn "entering ", $map->path, " at ($x, $y)\n"
1787     # if $map;
1788 root 1.110
1789 root 1.149 $map or $self->message ("The exit to '" . ($path->visible_name) . "' is closed", cf::NDI_UNIQUE | cf::NDI_RED);
1790 root 1.115
1791 root 1.110 $self->leave_link ($map, $x, $y);
1792     })->prio (1);
1793     }
1794    
1795     =item $player_object->enter_exit ($exit_object)
1796    
1797     =cut
1798    
1799     sub parse_random_map_params {
1800     my ($spec) = @_;
1801    
1802     my $rmp = { # defaults
1803     xsize => 10,
1804     ysize => 10,
1805     };
1806    
1807     for (split /\n/, $spec) {
1808     my ($k, $v) = split /\s+/, $_, 2;
1809    
1810     $rmp->{lc $k} = $v if (length $k) && (length $v);
1811     }
1812    
1813     $rmp
1814     }
1815    
1816     sub prepare_random_map {
1817     my ($exit) = @_;
1818    
1819     # all this does is basically replace the /! path by
1820     # a new random map path (?random/...) with a seed
1821     # that depends on the exit object
1822    
1823     my $rmp = parse_random_map_params $exit->msg;
1824    
1825     if ($exit->map) {
1826     $rmp->{region} = $exit->map->region_name;
1827     $rmp->{origin_map} = $exit->map->path;
1828     $rmp->{origin_x} = $exit->x;
1829     $rmp->{origin_y} = $exit->y;
1830     }
1831    
1832     $rmp->{random_seed} ||= $exit->random_seed;
1833    
1834     my $data = cf::to_json $rmp;
1835     my $md5 = Digest::MD5::md5_hex $data;
1836    
1837     if (my $fh = aio_open "$cf::RANDOM_MAPS/$md5.meta", O_WRONLY | O_CREAT, 0666) {
1838     aio_write $fh, 0, (length $data), $data, 0;
1839    
1840     $exit->slaying ("?random/$md5");
1841     $exit->msg (undef);
1842     }
1843     }
1844    
1845     sub cf::object::player::enter_exit {
1846     my ($self, $exit) = @_;
1847    
1848     return unless $self->type == cf::PLAYER;
1849    
1850     $self->enter_link;
1851    
1852 root 1.140 (async {
1853 root 1.133 $self->deactivate_recursive; # just to be sure
1854 root 1.110 unless (eval {
1855     prepare_random_map $exit
1856     if $exit->slaying eq "/!";
1857    
1858 root 1.166 my $path = new_from_path cf::map $exit->slaying, $exit->map && $exit->map->path;
1859 root 1.136 $self->goto ($path, $exit->stats->hp, $exit->stats->sp);
1860 root 1.110
1861     1;
1862     }) {
1863     $self->message ("Something went wrong deep within the crossfire server. "
1864     . "I'll try to bring you back to the map you were before. "
1865 root 1.158 . "Please report this to the dungeon master!",
1866 root 1.110 cf::NDI_UNIQUE | cf::NDI_RED);
1867    
1868     warn "ERROR in enter_exit: $@";
1869     $self->leave_link;
1870     }
1871     })->prio (1);
1872     }
1873    
1874 root 1.95 =head3 cf::client
1875    
1876     =over 4
1877    
1878     =item $client->send_drawinfo ($text, $flags)
1879    
1880     Sends a drawinfo packet to the client. Circumvents output buffering so
1881     should not be used under normal circumstances.
1882    
1883 root 1.70 =cut
1884    
1885 root 1.95 sub cf::client::send_drawinfo {
1886     my ($self, $text, $flags) = @_;
1887    
1888     utf8::encode $text;
1889     $self->send_packet (sprintf "drawinfo %d %s", $flags, $text);
1890     }
1891    
1892    
1893     =item $success = $client->query ($flags, "text", \&cb)
1894    
1895     Queues a query to the client, calling the given callback with
1896     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
1897     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
1898    
1899     Queries can fail, so check the return code. Or don't, as queries will become
1900     reliable at some point in the future.
1901    
1902     =cut
1903    
1904     sub cf::client::query {
1905     my ($self, $flags, $text, $cb) = @_;
1906    
1907     return unless $self->state == ST_PLAYING
1908     || $self->state == ST_SETUP
1909     || $self->state == ST_CUSTOM;
1910    
1911     $self->state (ST_CUSTOM);
1912    
1913     utf8::encode $text;
1914     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
1915    
1916     $self->send_packet ($self->{query_queue}[0][0])
1917     if @{ $self->{query_queue} } == 1;
1918     }
1919    
1920     cf::client->attach (
1921     on_reply => sub {
1922     my ($ns, $msg) = @_;
1923    
1924     # this weird shuffling is so that direct followup queries
1925     # get handled first
1926 root 1.128 my $queue = delete $ns->{query_queue}
1927 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
1928 root 1.95
1929     (shift @$queue)->[1]->($msg);
1930    
1931     push @{ $ns->{query_queue} }, @$queue;
1932    
1933     if (@{ $ns->{query_queue} } == @$queue) {
1934     if (@$queue) {
1935     $ns->send_packet ($ns->{query_queue}[0][0]);
1936     } else {
1937 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
1938 root 1.95 }
1939     }
1940     },
1941     );
1942    
1943 root 1.140 =item $client->async (\&cb)
1944 root 1.96
1945     Create a new coroutine, running the specified callback. The coroutine will
1946     be automatically cancelled when the client gets destroyed (e.g. on logout,
1947     or loss of connection).
1948    
1949     =cut
1950    
1951 root 1.140 sub cf::client::async {
1952 root 1.96 my ($self, $cb) = @_;
1953    
1954 root 1.140 my $coro = &Coro::async ($cb);
1955 root 1.103
1956     $coro->on_destroy (sub {
1957 root 1.96 delete $self->{_coro}{$coro+0};
1958 root 1.103 });
1959 root 1.96
1960     $self->{_coro}{$coro+0} = $coro;
1961 root 1.103
1962     $coro
1963 root 1.96 }
1964    
1965     cf::client->attach (
1966     on_destroy => sub {
1967     my ($ns) = @_;
1968    
1969 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
1970 root 1.96 },
1971     );
1972    
1973 root 1.95 =back
1974    
1975 root 1.70
1976     =head2 SAFE SCRIPTING
1977    
1978     Functions that provide a safe environment to compile and execute
1979     snippets of perl code without them endangering the safety of the server
1980     itself. Looping constructs, I/O operators and other built-in functionality
1981     is not available in the safe scripting environment, and the number of
1982 root 1.79 functions and methods that can be called is greatly reduced.
1983 root 1.70
1984     =cut
1985 root 1.23
1986 root 1.42 our $safe = new Safe "safe";
1987 root 1.23 our $safe_hole = new Safe::Hole;
1988    
1989     $SIG{FPE} = 'IGNORE';
1990    
1991     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
1992    
1993 root 1.25 # here we export the classes and methods available to script code
1994    
1995 root 1.70 =pod
1996    
1997     The following fucntions and emthods are available within a safe environment:
1998    
1999 elmex 1.91 cf::object contr pay_amount pay_player map
2000 root 1.70 cf::object::player player
2001     cf::player peaceful
2002 elmex 1.91 cf::map trigger
2003 root 1.70
2004     =cut
2005    
2006 root 1.25 for (
2007 elmex 1.91 ["cf::object" => qw(contr pay_amount pay_player map)],
2008 root 1.25 ["cf::object::player" => qw(player)],
2009     ["cf::player" => qw(peaceful)],
2010 elmex 1.91 ["cf::map" => qw(trigger)],
2011 root 1.25 ) {
2012     no strict 'refs';
2013     my ($pkg, @funs) = @$_;
2014 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2015 root 1.25 for @funs;
2016     }
2017 root 1.23
2018 root 1.70 =over 4
2019    
2020     =item @retval = safe_eval $code, [var => value, ...]
2021    
2022     Compiled and executes the given perl code snippet. additional var/value
2023     pairs result in temporary local (my) scalar variables of the given name
2024     that are available in the code snippet. Example:
2025    
2026     my $five = safe_eval '$first + $second', first => 1, second => 4;
2027    
2028     =cut
2029    
2030 root 1.23 sub safe_eval($;@) {
2031     my ($code, %vars) = @_;
2032    
2033     my $qcode = $code;
2034     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2035     $qcode =~ s/\n/\\n/g;
2036    
2037     local $_;
2038 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2039 root 1.23
2040 root 1.42 my $eval =
2041 root 1.23 "do {\n"
2042     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2043     . "#line 0 \"{$qcode}\"\n"
2044     . $code
2045     . "\n}"
2046 root 1.25 ;
2047    
2048     sub_generation_inc;
2049 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2050 root 1.25 sub_generation_inc;
2051    
2052 root 1.42 if ($@) {
2053     warn "$@";
2054     warn "while executing safe code '$code'\n";
2055     warn "with arguments " . (join " ", %vars) . "\n";
2056     }
2057    
2058 root 1.25 wantarray ? @res : $res[0]
2059 root 1.23 }
2060    
2061 root 1.69 =item cf::register_script_function $function => $cb
2062    
2063     Register a function that can be called from within map/npc scripts. The
2064     function should be reasonably secure and should be put into a package name
2065     like the extension.
2066    
2067     Example: register a function that gets called whenever a map script calls
2068     C<rent::overview>, as used by the C<rent> extension.
2069    
2070     cf::register_script_function "rent::overview" => sub {
2071     ...
2072     };
2073    
2074     =cut
2075    
2076 root 1.23 sub register_script_function {
2077     my ($fun, $cb) = @_;
2078    
2079     no strict 'refs';
2080 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2081 root 1.23 }
2082    
2083 root 1.70 =back
2084    
2085 root 1.71 =cut
2086    
2087 root 1.23 #############################################################################
2088 root 1.65
2089     =head2 EXTENSION DATABASE SUPPORT
2090    
2091     Crossfire maintains a very simple database for extension use. It can
2092     currently store anything that can be serialised using Storable, which
2093     excludes objects.
2094    
2095     The parameter C<$family> should best start with the name of the extension
2096     using it, it should be unique.
2097    
2098     =over 4
2099    
2100     =item $hashref = cf::db_get $family
2101    
2102     Return a hashref for use by the extension C<$family>, which can be
2103     modified. After modifications, you have to call C<cf::db_dirty> or
2104     C<cf::db_sync>.
2105    
2106     =item $value = cf::db_get $family => $key
2107    
2108     Returns a single value from the database
2109    
2110     =item cf::db_put $family => $hashref
2111    
2112     Stores the given family hashref into the database. Updates are delayed, if
2113     you want the data to be synced to disk immediately, use C<cf::db_sync>.
2114    
2115     =item cf::db_put $family => $key => $value
2116    
2117     Stores the given C<$value> in the family hash. Updates are delayed, if you
2118     want the data to be synced to disk immediately, use C<cf::db_sync>.
2119    
2120     =item cf::db_dirty
2121    
2122     Marks the database as dirty, to be updated at a later time.
2123    
2124     =item cf::db_sync
2125    
2126     Immediately write the database to disk I<if it is dirty>.
2127    
2128     =cut
2129    
2130 root 1.78 our $DB;
2131    
2132 root 1.65 {
2133 root 1.66 my $path = cf::localdir . "/database.pst";
2134 root 1.65
2135     sub db_load() {
2136 root 1.78 $DB = stat $path ? Storable::retrieve $path : { };
2137 root 1.65 }
2138    
2139     my $pid;
2140    
2141     sub db_save() {
2142     waitpid $pid, 0 if $pid;
2143 root 1.67 if (0 == ($pid = fork)) {
2144 root 1.78 $DB->{_meta}{version} = 1;
2145     Storable::nstore $DB, "$path~";
2146 root 1.65 rename "$path~", $path;
2147     cf::_exit 0 if defined $pid;
2148     }
2149     }
2150    
2151     my $dirty;
2152    
2153     sub db_sync() {
2154     db_save if $dirty;
2155     undef $dirty;
2156     }
2157    
2158 root 1.87 my $idle = Event->idle (min => $TICK * 2.8, max => 10, repeat => 0, data => WF_AUTOCANCEL, cb => sub {
2159 root 1.65 db_sync;
2160     });
2161    
2162     sub db_dirty() {
2163     $dirty = 1;
2164     $idle->start;
2165     }
2166    
2167     sub db_get($;$) {
2168     @_ >= 2
2169 root 1.78 ? $DB->{$_[0]}{$_[1]}
2170     : ($DB->{$_[0]} ||= { })
2171 root 1.65 }
2172    
2173     sub db_put($$;$) {
2174     if (@_ >= 3) {
2175 root 1.78 $DB->{$_[0]}{$_[1]} = $_[2];
2176 root 1.65 } else {
2177 root 1.78 $DB->{$_[0]} = $_[1];
2178 root 1.65 }
2179     db_dirty;
2180     }
2181 root 1.67
2182 root 1.93 cf::global->attach (
2183     prio => 10000,
2184 root 1.67 on_cleanup => sub {
2185     db_sync;
2186     },
2187 root 1.93 );
2188 root 1.65 }
2189    
2190     #############################################################################
2191 root 1.34 # the server's main()
2192    
2193 root 1.73 sub cfg_load {
2194 root 1.72 open my $fh, "<:utf8", cf::confdir . "/config"
2195     or return;
2196    
2197     local $/;
2198     *CFG = YAML::Syck::Load <$fh>;
2199 root 1.131
2200     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
2201    
2202 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
2203     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
2204    
2205 root 1.131 if (exists $CFG{mlockall}) {
2206     eval {
2207 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
2208 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
2209     };
2210     warn $@ if $@;
2211     }
2212 root 1.72 }
2213    
2214 root 1.39 sub main {
2215 root 1.108 # we must not ever block the main coroutine
2216     local $Coro::idle = sub {
2217 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
2218 root 1.140 async { Event::one_event };
2219 root 1.108 };
2220    
2221 root 1.73 cfg_load;
2222 root 1.65 db_load;
2223 root 1.61 load_extensions;
2224 root 1.34 Event::loop;
2225     }
2226    
2227     #############################################################################
2228 root 1.155 # initialisation and cleanup
2229    
2230     # install some emergency cleanup handlers
2231     BEGIN {
2232     for my $signal (qw(INT HUP TERM)) {
2233     Event->signal (
2234     data => WF_AUTOCANCEL,
2235     signal => $signal,
2236     cb => sub {
2237     cf::cleanup "SIG$signal";
2238     },
2239     );
2240     }
2241     }
2242    
2243 root 1.156 sub emergency_save() {
2244 root 1.155 my $freeze_guard = cf::freeze_mainloop;
2245    
2246     warn "enter emergency perl save\n";
2247    
2248     cf::sync_job {
2249     # use a peculiar iteration method to avoid tripping on perl
2250     # refcount bugs in for. also avoids problems with players
2251 root 1.167 # and maps saved/destroyed asynchronously.
2252 root 1.155 warn "begin emergency player save\n";
2253     for my $login (keys %cf::PLAYER) {
2254     my $pl = $cf::PLAYER{$login} or next;
2255     $pl->valid or next;
2256     $pl->save;
2257     }
2258     warn "end emergency player save\n";
2259    
2260     warn "begin emergency map save\n";
2261     for my $path (keys %cf::MAP) {
2262     my $map = $cf::MAP{$path} or next;
2263     $map->valid or next;
2264     $map->save;
2265     }
2266     warn "end emergency map save\n";
2267     };
2268    
2269     warn "leave emergency perl save\n";
2270     }
2271 root 1.22
2272 root 1.111 sub reload() {
2273 root 1.106 # can/must only be called in main
2274     if ($Coro::current != $Coro::main) {
2275     warn "can only reload from main coroutine\n";
2276     return;
2277     }
2278    
2279 root 1.103 warn "reloading...";
2280    
2281 root 1.159 warn "freezing server";
2282 root 1.133 my $guard = freeze_mainloop;
2283 root 1.106 cf::emergency_save;
2284    
2285 root 1.159 warn "sync database to disk";
2286     cf::db_sync;
2287     IO::AIO::flush;
2288    
2289 root 1.103 eval {
2290 root 1.106 # if anything goes wrong in here, we should simply crash as we already saved
2291 root 1.65
2292 root 1.159 warn "cancel all watchers";
2293 root 1.87 for (Event::all_watchers) {
2294     $_->cancel if $_->data & WF_AUTOCANCEL;
2295     }
2296 root 1.65
2297 root 1.159 warn "cancel all extension coros";
2298 root 1.103 $_->cancel for values %EXT_CORO;
2299     %EXT_CORO = ();
2300    
2301 root 1.159 warn "remove commands";
2302     %COMMAND = ();
2303    
2304     warn "remove ext commands";
2305     %EXTCMD = ();
2306    
2307     warn "unload/nuke all extensions";
2308     for my $pkg (@EXTS) {
2309 root 1.160 warn "... unloading $pkg";
2310 root 1.159
2311     if (my $cb = $pkg->can ("unload")) {
2312     eval {
2313     $cb->($pkg);
2314     1
2315     } or warn "$pkg unloaded, but with errors: $@";
2316     }
2317    
2318 root 1.160 warn "... nuking $pkg";
2319 root 1.159 Symbol::delete_package $pkg;
2320 root 1.65 }
2321    
2322 root 1.159 warn "unload all perl modules loaded from $LIBDIR";
2323 root 1.65 while (my ($k, $v) = each %INC) {
2324     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
2325    
2326 root 1.103 warn "removing <$k>";
2327 root 1.65 delete $INC{$k};
2328    
2329     $k =~ s/\.pm$//;
2330     $k =~ s/\//::/g;
2331    
2332     if (my $cb = $k->can ("unload_module")) {
2333     $cb->();
2334     }
2335    
2336     Symbol::delete_package $k;
2337     }
2338    
2339 root 1.159 warn "get rid of safe::, as good as possible";
2340 root 1.65 Symbol::delete_package "safe::$_"
2341 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
2342 root 1.65
2343 root 1.159 warn "unload cf.pm \"a bit\"";
2344 root 1.65 delete $INC{"cf.pm"};
2345    
2346     # don't, removes xs symbols, too,
2347     # and global variables created in xs
2348     #Symbol::delete_package __PACKAGE__;
2349    
2350 root 1.103 warn "reloading cf.pm";
2351 root 1.65 require cf;
2352 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
2353    
2354 root 1.159 warn "load config and database again";
2355 root 1.73 cf::cfg_load;
2356 root 1.65 cf::db_load;
2357    
2358 root 1.103 warn "load extensions";
2359 root 1.65 cf::load_extensions;
2360    
2361 root 1.159 warn "reattach attachments to objects/players";
2362 root 1.65 _global_reattach;
2363 root 1.159 warn "reattach attachments to maps";
2364 root 1.144 reattach $_ for values %MAP;
2365 root 1.65 };
2366    
2367 root 1.106 if ($@) {
2368     warn $@;
2369     warn "error while reloading, exiting.";
2370     exit 1;
2371     }
2372    
2373 root 1.159 warn "reloaded";
2374 root 1.65 };
2375    
2376 root 1.111 register_command "reload" => sub {
2377 root 1.65 my ($who, $arg) = @_;
2378    
2379     if ($who->flag (FLAG_WIZ)) {
2380 root 1.107 $who->message ("start of reload.");
2381 root 1.111 reload;
2382 root 1.107 $who->message ("end of reload.");
2383 root 1.65 }
2384     };
2385    
2386 root 1.27 unshift @INC, $LIBDIR;
2387 root 1.17
2388 root 1.35 $TICK_WATCHER = Event->timer (
2389 root 1.104 reentrant => 0,
2390     prio => 0,
2391     at => $NEXT_TICK || $TICK,
2392     data => WF_AUTOCANCEL,
2393     cb => sub {
2394 root 1.163 $NOW = Event::time;
2395    
2396 root 1.133 cf::server_tick; # one server iteration
2397     $RUNTIME += $TICK;
2398 root 1.35 $NEXT_TICK += $TICK;
2399    
2400 root 1.155 $WAIT_FOR_TICK->broadcast;
2401     $WAIT_FOR_TICK_ONE->send if $WAIT_FOR_TICK_ONE->awaited;
2402    
2403 root 1.78 # if we are delayed by four ticks or more, skip them all
2404 root 1.103 $NEXT_TICK = Event::time if Event::time >= $NEXT_TICK + $TICK * 4;
2405 root 1.35
2406     $TICK_WATCHER->at ($NEXT_TICK);
2407     $TICK_WATCHER->start;
2408     },
2409     );
2410    
2411 root 1.80 IO::AIO::max_poll_time $TICK * 0.2;
2412 root 1.77
2413 root 1.168 $AIO_POLL_WATCHER = Event->io (
2414 root 1.108 fd => IO::AIO::poll_fileno,
2415     poll => 'r',
2416     prio => 5,
2417     data => WF_AUTOCANCEL,
2418     cb => \&IO::AIO::poll_cb,
2419     );
2420    
2421 root 1.168 $WRITE_RUNTIME_WATCHER = Event->timer (
2422 root 1.108 data => WF_AUTOCANCEL,
2423     after => 0,
2424     interval => 10,
2425     cb => sub {
2426     (Coro::unblock_sub {
2427     write_runtime
2428     or warn "ERROR: unable to write runtime file: $!";
2429     })->();
2430     },
2431     );
2432 root 1.103
2433 root 1.125 END { cf::emergency_save }
2434    
2435 root 1.1 1
2436