ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.168
Committed: Sun Jan 14 00:37:14 2007 UTC (17 years, 4 months ago) by root
Branch: MAIN
Changes since 1.167: +9 -5 lines
Log Message:
stop runtime write watcher on freeze, too, to trip watchdog

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