ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.224
Committed: Mon Mar 12 21:42:12 2007 UTC (17 years, 2 months ago) by root
Branch: MAIN
Changes since 1.223: +5 -3 lines
Log Message:
take advantage of Coro 3.52's ability to set the stacksize and work around the deep recursion required just to generate mazes

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