ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.222
Committed: Tue Mar 6 03:06:00 2007 UTC (17 years, 2 months ago) by root
Branch: MAIN
CVS Tags: rel-2_0
Changes since 1.221: +3 -1 lines
Log Message:
- automake insists on naming all libdirs .../cfserver now. i have to concur :/
- correctly reattach to players on reload, this likely fixes the reload crash bug.
- init env vars very early, so perl gets to see them.

File Contents

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