ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.220
Committed: Sat Feb 17 23:54:35 2007 UTC (17 years, 3 months ago) by root
Branch: MAIN
Changes since 1.219: +8 -0 lines
Log Message:
likely fix the freeze-on-quit bug, also nuke in-memory apartments on quit

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