ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.233
Committed: Wed Apr 4 02:20:27 2007 UTC (17 years, 1 month ago) by root
Branch: MAIN
Changes since 1.232: +5 -3 lines
Log Message:
*** empty log message ***

File Contents

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