ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.215
Committed: Thu Feb 15 03:19:02 2007 UTC (17 years, 3 months ago) by root
Branch: MAIN
Changes since 1.214: +4 -2 lines
Log Message:
- implement worldmap underlay and regionset and fill in C++,
  resulting in a *major* speedup in worldmap loading.
- use first arch (e.g. deep_sea) and first region (e.g. panthalassia)
  to fill maps outside the world: less hardcoded content data.
- fix ext/rent.ext to actually check again for entrance.
- temporarily remembver previous map in $ob->{_prev_pos}
  (HACK to make ext/rent.ext work).

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