ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.211
Committed: Tue Feb 13 16:23:32 2007 UTC (17 years, 3 months ago) by root
Branch: MAIN
Changes since 1.210: +9 -0 lines
Log Message:
- make server more fragile w.r.t. watchdog on freeze
- try to make a perl backtrace on crash, too.

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