ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.331
Committed: Mon Aug 13 13:10:01 2007 UTC (16 years, 9 months ago) by root
Branch: MAIN
Changes since 1.330: +2 -1 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.250 use Socket;
9 root 1.6 use Storable;
10 root 1.224 use Event;
11 root 1.23 use Opcode;
12     use Safe;
13     use Safe::Hole;
14 root 1.19
15 root 1.244 use Coro 3.61 ();
16 root 1.224 use Coro::State;
17 root 1.250 use Coro::Handle;
18 root 1.96 use Coro::Event;
19     use Coro::Timer;
20     use Coro::Signal;
21     use Coro::Semaphore;
22 root 1.105 use Coro::AIO;
23 root 1.237 use Coro::Storable;
24 root 1.96
25 root 1.288 use JSON::XS 1.4 ();
26 root 1.206 use BDB ();
27 root 1.154 use Data::Dumper;
28 root 1.108 use Digest::MD5;
29 root 1.105 use Fcntl;
30 root 1.227 use YAML::Syck ();
31 root 1.145 use IO::AIO 2.32 ();
32 root 1.32 use Time::HiRes;
33 root 1.208 use Compress::LZF;
34 root 1.302 use Digest::MD5 ();
35 root 1.208
36 root 1.227 # configure various modules to our taste
37     #
38 root 1.237 $Storable::canonical = 1; # reduce rsync transfers
39 root 1.224 Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator
40 root 1.208 Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later
41 root 1.227
42 root 1.224 $Event::Eval = 1; # no idea why this is required, but it is
43 root 1.1
44 root 1.72 # work around bug in YAML::Syck - bad news for perl6, will it be as broken wrt. unicode?
45     $YAML::Syck::ImplicitUnicode = 1;
46    
47 root 1.139 $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
48 root 1.1
49 root 1.227 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
50    
51 root 1.85 our %COMMAND = ();
52     our %COMMAND_TIME = ();
53 root 1.159
54     our @EXTS = (); # list of extension package names
55 root 1.85 our %EXTCMD = ();
56 root 1.287 our %EXTICMD = ();
57 root 1.159 our %EXT_CORO = (); # coroutines bound to extensions
58 root 1.161 our %EXT_MAP = (); # pluggable maps
59 root 1.85
60 root 1.223 our $RELOAD; # number of reloads so far
61 root 1.1 our @EVENT;
62 root 1.253
63     our $CONFDIR = confdir;
64     our $DATADIR = datadir;
65     our $LIBDIR = "$DATADIR/ext";
66     our $PODDIR = "$DATADIR/pod";
67     our $MAPDIR = "$DATADIR/" . mapdir;
68     our $LOCALDIR = localdir;
69     our $TMPDIR = "$LOCALDIR/" . tmpdir;
70     our $UNIQUEDIR = "$LOCALDIR/" . uniquedir;
71     our $PLAYERDIR = "$LOCALDIR/" . playerdir;
72     our $RANDOMDIR = "$LOCALDIR/random";
73     our $BDBDIR = "$LOCALDIR/db";
74 root 1.1
75 root 1.245 our $TICK = MAX_TIME * 1e-6; # this is a CONSTANT(!)
76 root 1.35 our $TICK_WATCHER;
77 root 1.168 our $AIO_POLL_WATCHER;
78 root 1.214 our $NEXT_RUNTIME_WRITE; # when should the runtime file be written
79 root 1.35 our $NEXT_TICK;
80 root 1.103 our $NOW;
81 root 1.205 our $USE_FSYNC = 1; # use fsync to write maps - default off
82 root 1.35
83 root 1.206 our $BDB_POLL_WATCHER;
84     our $DB_ENV;
85    
86 root 1.70 our %CFG;
87    
88 root 1.84 our $UPTIME; $UPTIME ||= time;
89 root 1.103 our $RUNTIME;
90    
91 root 1.143 our %PLAYER; # all users
92     our %MAP; # all maps
93 root 1.166 our $LINK_MAP; # the special {link} map, which is always available
94 root 1.103
95 root 1.166 # used to convert map paths into valid unix filenames by replacing / by ∕
96     our $PATH_SEP = "∕"; # U+2215, chosen purely for visual reasons
97    
98 root 1.265 our $LOAD; # a number between 0 (idle) and 1 (too many objects)
99     our $LOADAVG; # same thing, but with alpha-smoothing
100     our $tick_start; # for load detecting purposes
101    
102 root 1.103 binmode STDOUT;
103     binmode STDERR;
104    
105     # read virtual server time, if available
106 root 1.253 unless ($RUNTIME || !-e "$LOCALDIR/runtime") {
107     open my $fh, "<", "$LOCALDIR/runtime"
108 root 1.103 or die "unable to read runtime file: $!";
109     $RUNTIME = <$fh> + 0.;
110     }
111    
112 root 1.253 mkdir $_
113     for $LOCALDIR, $TMPDIR, $UNIQUEDIR, $PLAYERDIR, $RANDOMDIR, $BDBDIR;
114 root 1.103
115 root 1.131 our $EMERGENCY_POSITION;
116 root 1.110
117 root 1.199 sub cf::map::normalise;
118    
119 root 1.70 #############################################################################
120    
121     =head2 GLOBAL VARIABLES
122    
123     =over 4
124    
125 root 1.83 =item $cf::UPTIME
126    
127     The timestamp of the server start (so not actually an uptime).
128    
129 root 1.103 =item $cf::RUNTIME
130    
131     The time this server has run, starts at 0 and is increased by $cf::TICK on
132     every server tick.
133    
134 root 1.253 =item $cf::CONFDIR $cf::DATADIR $cf::LIBDIR $cf::PODDIR
135     $cf::MAPDIR $cf::LOCALDIR $cf::TMPDIR $cf::UNIQUEDIR
136     $cf::PLAYERDIR $cf::RANDOMDIR $cf::BDBDIR
137    
138     Various directories - "/etc", read-only install directory, perl-library
139     directory, pod-directory, read-only maps directory, "/var", "/var/tmp",
140     unique-items directory, player file directory, random maps directory and
141     database environment.
142 root 1.70
143 root 1.103 =item $cf::NOW
144    
145     The time of the last (current) server tick.
146    
147 root 1.70 =item $cf::TICK
148    
149     The interval between server ticks, in seconds.
150    
151 root 1.265 =item $cf::LOADAVG
152    
153     The current CPU load on the server (alpha-smoothed), as a value between 0
154     (none) and 1 (overloaded), indicating how much time is spent on processing
155     objects per tick. Healthy values are < 0.5.
156    
157     =item $cf::LOAD
158    
159     The raw value load value from the last tick.
160    
161 root 1.70 =item %cf::CFG
162    
163     Configuration for the server, loaded from C</etc/crossfire/config>, or
164     from wherever your confdir points to.
165    
166 root 1.239 =item cf::wait_for_tick, cf::wait_for_tick_begin
167 root 1.155
168 root 1.239 These are functions that inhibit the current coroutine one tick. cf::wait_for_tick_begin only
169     returns directly I<after> the tick processing (and consequently, can only wake one process
170     per tick), while cf::wait_for_tick wakes up all waiters after tick processing.
171 root 1.155
172 elmex 1.310 =item @cf::INVOKE_RESULTS
173    
174     This array contains the results of the last C<invoke ()> call. When
175     C<cf::override> is called C<@cf::INVOKE_RESULTS> is set to the parameters of
176     that call.
177    
178 root 1.70 =back
179    
180     =cut
181    
182 root 1.1 BEGIN {
183     *CORE::GLOBAL::warn = sub {
184     my $msg = join "", @_;
185 root 1.103
186 root 1.1 $msg .= "\n"
187     unless $msg =~ /\n$/;
188    
189 root 1.255 $msg =~ s/([\x00-\x08\x0b-\x1f])/sprintf "\\x%02x", ord $1/ge;
190 root 1.250
191     utf8::encode $msg;
192 root 1.146 LOG llevError, $msg;
193 root 1.1 };
194     }
195    
196 root 1.93 @safe::cf::global::ISA = @cf::global::ISA = 'cf::attachable';
197     @safe::cf::object::ISA = @cf::object::ISA = 'cf::attachable';
198     @safe::cf::player::ISA = @cf::player::ISA = 'cf::attachable';
199     @safe::cf::client::ISA = @cf::client::ISA = 'cf::attachable';
200     @safe::cf::map::ISA = @cf::map::ISA = 'cf::attachable';
201 root 1.273 @safe::cf::arch::ISA = @cf::arch::ISA = 'cf::object';
202     @safe::cf::object::player::ISA = @cf::object::player::ISA = 'cf::object'; # not really true (yet)
203 root 1.25
204 root 1.41 # we bless all objects into (empty) derived classes to force a method lookup
205 root 1.25 # within the Safe compartment.
206 root 1.86 for my $pkg (qw(
207 root 1.100 cf::global cf::attachable
208 root 1.86 cf::object cf::object::player
209 root 1.89 cf::client cf::player
210 root 1.86 cf::arch cf::living
211     cf::map cf::party cf::region
212     )) {
213 root 1.25 no strict 'refs';
214 root 1.41 @{"safe::$pkg\::wrap::ISA"} = @{"$pkg\::wrap::ISA"} = $pkg;
215 root 1.25 }
216 root 1.1
217 root 1.18 $Event::DIED = sub {
218     warn "error in event callback: @_";
219     };
220    
221 root 1.281 #############################################################################
222    
223 root 1.70 =head2 UTILITY FUNCTIONS
224    
225     =over 4
226    
227 root 1.154 =item dumpval $ref
228    
229 root 1.70 =cut
230 root 1.44
231 root 1.154 sub dumpval {
232     eval {
233     local $SIG{__DIE__};
234     my $d;
235     if (1) {
236     $d = new Data::Dumper([$_[0]], ["*var"]);
237     $d->Terse(1);
238     $d->Indent(2);
239     $d->Quotekeys(0);
240     $d->Useqq(1);
241     #$d->Bless(...);
242     $d->Seen($_[1]) if @_ > 1;
243     $d = $d->Dump();
244     }
245     $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge;
246     $d
247     } || "[unable to dump $_[0]: '$@']";
248     }
249    
250 root 1.70 =item $ref = cf::from_json $json
251    
252     Converts a JSON string into the corresponding perl data structure.
253    
254     =item $json = cf::to_json $ref
255    
256     Converts a perl data structure into its JSON representation.
257    
258 root 1.287 =cut
259    
260 root 1.290 our $json_coder = JSON::XS->new->utf8->max_size (1e6); # accept ~1mb max
261 root 1.287
262     sub to_json ($) { $json_coder->encode ($_[0]) }
263     sub from_json ($) { $json_coder->decode ($_[0]) }
264    
265 root 1.120 =item cf::lock_wait $string
266    
267     Wait until the given lock is available. See cf::lock_acquire.
268    
269     =item my $lock = cf::lock_acquire $string
270    
271     Wait until the given lock is available and then acquires it and returns
272 root 1.135 a Coro::guard object. If the guard object gets destroyed (goes out of scope,
273 root 1.120 for example when the coroutine gets canceled), the lock is automatically
274     returned.
275    
276 root 1.133 Lock names should begin with a unique identifier (for example, cf::map::find
277     uses map_find and cf::map::load uses map_load).
278 root 1.120
279 root 1.253 =item $locked = cf::lock_active $string
280    
281     Return true if the lock is currently active, i.e. somebody has locked it.
282    
283 root 1.120 =cut
284    
285     our %LOCK;
286    
287     sub lock_wait($) {
288     my ($key) = @_;
289    
290     # wait for lock, if any
291     while ($LOCK{$key}) {
292     push @{ $LOCK{$key} }, $Coro::current;
293     Coro::schedule;
294     }
295     }
296    
297     sub lock_acquire($) {
298     my ($key) = @_;
299    
300     # wait, to be sure we are not locked
301     lock_wait $key;
302    
303     $LOCK{$key} = [];
304    
305 root 1.135 Coro::guard {
306 root 1.120 # wake up all waiters, to be on the safe side
307     $_->ready for @{ delete $LOCK{$key} };
308     }
309     }
310    
311 root 1.253 sub lock_active($) {
312     my ($key) = @_;
313    
314     ! ! $LOCK{$key}
315     }
316    
317 root 1.133 sub freeze_mainloop {
318     return unless $TICK_WATCHER->is_active;
319    
320 root 1.168 my $guard = Coro::guard {
321     $TICK_WATCHER->start;
322     };
323 root 1.133 $TICK_WATCHER->stop;
324     $guard
325     }
326    
327 root 1.315 =item cf::get_slot $time[, $priority[, $name]]
328 root 1.314
329     Allocate $time seconds of blocking CPU time at priority C<$priority>:
330     This call blocks and returns only when you have at least C<$time> seconds
331     of cpu time till the next tick. The slot is only valid till the next cede.
332    
333 root 1.315 The optional C<$name> can be used to identify the job to run. It might be
334     used for statistical purposes and should identify the same time-class.
335    
336 root 1.314 Useful for short background jobs.
337    
338     =cut
339    
340     our @SLOT_QUEUE;
341     our $SLOT_QUEUE;
342    
343     $SLOT_QUEUE->cancel if $SLOT_QUEUE;
344     $SLOT_QUEUE = Coro::async {
345     my $signal = new Coro::Signal;
346    
347     while () {
348     next_job:
349     my $avail = cf::till_tick;
350     if ($avail > 0.01) {
351     for (0 .. $#SLOT_QUEUE) {
352     if ($SLOT_QUEUE[$_][0] < $avail) {
353     my $job = splice @SLOT_QUEUE, $_, 1, ();
354     $job->[2]->send;
355     Coro::cede;
356     goto next_job;
357     }
358     }
359     }
360    
361     if (@SLOT_QUEUE) {
362     # we do not use wait_For_tick() as it returns immediately when tick is inactive
363     push @cf::WAIT_FOR_TICK, $signal;
364     $signal->wait;
365     } else {
366     Coro::schedule;
367     }
368     }
369     };
370    
371 root 1.315 sub get_slot($;$$) {
372     my ($time, $pri, $name) = @_;
373    
374     $time = $TICK * .6 if $time > $TICK * .6;
375     my $sig = new Coro::Signal;
376 root 1.314
377 root 1.315 push @SLOT_QUEUE, [$time, $pri, $sig, $name];
378 root 1.314 @SLOT_QUEUE = sort { $b->[1] <=> $a->[1] } @SLOT_QUEUE;
379     $SLOT_QUEUE->ready;
380     $sig->wait;
381     }
382    
383 root 1.140 =item cf::async { BLOCK }
384    
385     Currently the same as Coro::async_pool, meaning you cannot use
386     C<on_destroy>, C<join> or other gimmicks on these coroutines. The only
387     thing you are allowed to do is call C<prio> on it.
388    
389     =cut
390    
391     BEGIN { *async = \&Coro::async_pool }
392    
393 root 1.106 =item cf::sync_job { BLOCK }
394    
395 root 1.281 The design of Crossfire TRT requires that the main coroutine ($Coro::main)
396     is always able to handle events or runnable, as Crossfire TRT is only
397     partly reentrant. Thus "blocking" it by e.g. waiting for I/O is not
398     acceptable.
399 root 1.106
400     If it must be done, put the blocking parts into C<sync_job>. This will run
401     the given BLOCK in another coroutine while waiting for the result. The
402     server will be frozen during this time, so the block should either finish
403     fast or be very important.
404    
405     =cut
406    
407 root 1.105 sub sync_job(&) {
408     my ($job) = @_;
409    
410     if ($Coro::current == $Coro::main) {
411 root 1.265 my $time = Event::time;
412    
413 root 1.112 # this is the main coro, too bad, we have to block
414     # till the operation succeeds, freezing the server :/
415    
416 root 1.110 # TODO: use suspend/resume instead
417 root 1.112 # (but this is cancel-safe)
418 root 1.133 my $freeze_guard = freeze_mainloop;
419 root 1.112
420     my $busy = 1;
421     my @res;
422    
423 root 1.140 (async {
424 root 1.112 @res = eval { $job->() };
425     warn $@ if $@;
426     undef $busy;
427     })->prio (Coro::PRIO_MAX);
428    
429 root 1.105 while ($busy) {
430 root 1.141 Coro::cede or Event::one_event;
431 root 1.105 }
432 root 1.112
433 root 1.265 $time = Event::time - $time;
434    
435 root 1.266 LOG llevError | logBacktrace, Carp::longmess "long sync job"
436     if $time > $TICK * 0.5 && $TICK_WATCHER->is_active;
437 root 1.265
438     $tick_start += $time; # do not account sync jobs to server load
439    
440 root 1.112 wantarray ? @res : $res[0]
441 root 1.105 } else {
442 root 1.112 # we are in another coroutine, how wonderful, everything just works
443    
444     $job->()
445 root 1.105 }
446     }
447    
448 root 1.140 =item $coro = cf::async_ext { BLOCK }
449 root 1.103
450 root 1.159 Like async, but this coro is automatically being canceled when the
451 root 1.140 extension calling this is being unloaded.
452 root 1.103
453     =cut
454    
455 root 1.140 sub async_ext(&) {
456 root 1.103 my $cb = shift;
457    
458 root 1.140 my $coro = &Coro::async ($cb);
459 root 1.103
460     $coro->on_destroy (sub {
461     delete $EXT_CORO{$coro+0};
462     });
463     $EXT_CORO{$coro+0} = $coro;
464    
465     $coro
466     }
467    
468 root 1.281 =item fork_call { }, $args
469    
470     Executes the given code block with the given arguments in a seperate
471     process, returning the results. Everything must be serialisable with
472     Coro::Storable. May, of course, block. Note that the executed sub may
473     never block itself or use any form of Event handling.
474    
475     =cut
476    
477 root 1.299 sub _store_scalar {
478     open my $fh, ">", \my $buf
479     or die "fork_call: cannot open fh-to-buf in child : $!";
480     Storable::store_fd $_[0], $fh;
481     close $fh;
482    
483     $buf
484     }
485    
486 root 1.281 sub fork_call(&@) {
487     my ($cb, @args) = @_;
488    
489     # socketpair my $fh1, my $fh2, Socket::AF_UNIX, Socket::SOCK_STREAM, Socket::PF_UNSPEC
490     # or die "socketpair: $!";
491     pipe my $fh1, my $fh2
492     or die "pipe: $!";
493    
494     if (my $pid = fork) {
495     close $fh2;
496    
497     my $res = (Coro::Handle::unblock $fh1)->readline (undef);
498 root 1.298 warn "pst<$res>" unless $res =~ /^pst/;
499 root 1.281 $res = Coro::Storable::thaw $res;
500    
501     waitpid $pid, 0; # should not block anymore, we expect the child to simply behave
502    
503 root 1.298 Carp::confess $$res unless "ARRAY" eq ref $res;
504 root 1.281
505     return wantarray ? @$res : $res->[-1];
506     } else {
507     reset_signals;
508     local $SIG{__WARN__};
509     local $SIG{__DIE__};
510 root 1.299 # just in case, this hack effectively disables event
511     # in the child. cleaner and slower would be canceling all watchers,
512     # but this works for the time being.
513 root 1.298 local $Coro::idle;
514     $Coro::current->prio (Coro::PRIO_MAX);
515 root 1.299
516 root 1.281 eval {
517     close $fh1;
518    
519     my @res = eval { $cb->(@args) };
520 root 1.298
521 root 1.299 syswrite $fh2, _store_scalar $@ ? \"$@" : \@res;
522 root 1.298 close $fh2;
523 root 1.281 };
524    
525     warn $@ if $@;
526     _exit 0;
527     }
528     }
529    
530     =item $value = cf::db_get $family => $key
531    
532     Returns a single value from the environment database.
533    
534     =item cf::db_put $family => $key => $value
535    
536     Stores the given C<$value> in the family. It can currently store binary
537     data only (use Compress::LZF::sfreeze_cr/sthaw to convert to/from binary).
538    
539     =cut
540    
541     our $DB;
542    
543     sub db_init {
544     unless ($DB) {
545     $DB = BDB::db_create $DB_ENV;
546    
547     cf::sync_job {
548     eval {
549     $DB->set_flags (BDB::CHKSUM);
550    
551     BDB::db_open $DB, undef, "db", undef, BDB::BTREE,
552     BDB::CREATE | BDB::AUTO_COMMIT, 0666;
553     cf::cleanup "db_open(db): $!" if $!;
554     };
555     cf::cleanup "db_open(db): $@" if $@;
556     };
557     }
558     }
559    
560     sub db_get($$) {
561     my $key = "$_[0]/$_[1]";
562    
563     cf::sync_job {
564     BDB::db_get $DB, undef, $key, my $data;
565    
566     $! ? ()
567     : $data
568     }
569     }
570    
571     sub db_put($$$) {
572     BDB::dbreq_pri 4;
573     BDB::db_put $DB, undef, "$_[0]/$_[1]", $_[2], 0, sub { };
574     }
575    
576     =item cf::cache $id => [$paths...], $processversion => $process
577    
578     Generic caching function that returns the value of the resource $id,
579     caching and regenerating as required.
580    
581     This function can block.
582    
583     =cut
584    
585     sub cache {
586     my ($id, $src, $processversion, $process) = @_;
587    
588     my $meta =
589     join "\x00",
590     $processversion,
591     map {
592     aio_stat $_
593     and Carp::croak "$_: $!";
594 root 1.264
595 root 1.281 ($_, (stat _)[7,9])
596     } @$src;
597 root 1.264
598 root 1.281 my $dbmeta = db_get cache => "$id/meta";
599     if ($dbmeta ne $meta) {
600     # changed, we may need to process
601 root 1.264
602 root 1.281 my @data;
603     my $md5;
604 root 1.219
605 root 1.281 for (0 .. $#$src) {
606     0 <= aio_load $src->[$_], $data[$_]
607     or Carp::croak "$src->[$_]: $!";
608     }
609 root 1.108
610 root 1.281 # if processing is expensive, check
611     # checksum first
612     if (1) {
613     $md5 =
614     join "\x00",
615     $processversion,
616     map {
617     Coro::cede;
618     ($src->[$_], Digest::MD5::md5_hex $data[$_])
619     } 0.. $#$src;
620    
621 root 1.186
622 root 1.281 my $dbmd5 = db_get cache => "$id/md5";
623     if ($dbmd5 eq $md5) {
624     db_put cache => "$id/meta", $meta;
625 root 1.108
626 root 1.281 return db_get cache => "$id/data";
627     }
628     }
629 root 1.108
630 root 1.281 my $t1 = Time::HiRes::time;
631     my $data = $process->(\@data);
632     my $t2 = Time::HiRes::time;
633 root 1.264
634 root 1.281 warn "cache: '$id' processed in ", $t2 - $t1, "s\n";
635 root 1.108
636 root 1.281 db_put cache => "$id/data", $data;
637     db_put cache => "$id/md5" , $md5;
638     db_put cache => "$id/meta", $meta;
639 root 1.108
640 root 1.281 return $data;
641     }
642 root 1.263
643 root 1.281 db_get cache => "$id/data"
644 root 1.108 }
645    
646 root 1.230 =item cf::datalog type => key => value, ...
647    
648     Log a datalog packet of the given type with the given key-value pairs.
649    
650     =cut
651    
652     sub datalog($@) {
653     my ($type, %kv) = @_;
654     warn "DATALOG ", JSON::XS->new->ascii->encode ({ %kv, type => $type });
655     }
656    
657 root 1.70 =back
658    
659 root 1.71 =cut
660    
661 root 1.44 #############################################################################
662 root 1.39
663 root 1.93 =head2 ATTACHABLE OBJECTS
664    
665 root 1.94 Many objects in crossfire are so-called attachable objects. That means you can
666     attach callbacks/event handlers (a collection of which is called an "attachment")
667     to it. All such attachable objects support the following methods.
668    
669     In the following description, CLASS can be any of C<global>, C<object>
670     C<player>, C<client> or C<map> (i.e. the attachable objects in
671 root 1.281 Crossfire TRT).
672 root 1.55
673     =over 4
674    
675 root 1.94 =item $attachable->attach ($attachment, key => $value...)
676    
677     =item $attachable->detach ($attachment)
678    
679     Attach/detach a pre-registered attachment to a specific object and give it
680     the specified key/value pairs as arguments.
681    
682     Example, attach a minesweeper attachment to the given object, making it a
683     10x10 minesweeper game:
684 root 1.46
685 root 1.94 $obj->attach (minesweeper => width => 10, height => 10);
686 root 1.53
687 root 1.93 =item $bool = $attachable->attached ($name)
688 root 1.46
689 root 1.93 Checks wether the named attachment is currently attached to the object.
690 root 1.46
691 root 1.94 =item cf::CLASS->attach ...
692 root 1.46
693 root 1.94 =item cf::CLASS->detach ...
694 root 1.92
695 root 1.94 Define an anonymous attachment and attach it to all objects of the given
696     CLASS. See the next function for an explanation of its arguments.
697 root 1.92
698 root 1.93 You can attach to global events by using the C<cf::global> class.
699 root 1.92
700 root 1.94 Example, log all player logins:
701    
702     cf::player->attach (
703     on_login => sub {
704     my ($pl) = @_;
705     ...
706     },
707     );
708    
709     Example, attach to the jeweler skill:
710    
711     cf::object->attach (
712     type => cf::SKILL,
713     subtype => cf::SK_JEWELER,
714     on_use_skill => sub {
715     my ($sk, $ob, $part, $dir, $msg) = @_;
716     ...
717     },
718     );
719    
720     =item cf::CLASS::attachment $name, ...
721    
722     Register an attachment by C<$name> through which attachable objects of the
723     given CLASS can refer to this attachment.
724    
725     Some classes such as crossfire maps and objects can specify attachments
726     that are attached at load/instantiate time, thus the need for a name.
727    
728     These calls expect any number of the following handler/hook descriptions:
729 root 1.46
730     =over 4
731    
732     =item prio => $number
733    
734     Set the priority for all following handlers/hooks (unless overwritten
735     by another C<prio> setting). Lower priority handlers get executed
736     earlier. The default priority is C<0>, and many built-in handlers are
737     registered at priority C<-1000>, so lower priorities should not be used
738     unless you know what you are doing.
739    
740 root 1.93 =item type => $type
741    
742     (Only for C<< cf::object->attach >> calls), limits the attachment to the
743     given type of objects only (the additional parameter C<subtype> can be
744     used to further limit to the given subtype).
745    
746 root 1.46 =item on_I<event> => \&cb
747    
748     Call the given code reference whenever the named event happens (event is
749     something like C<instantiate>, C<apply>, C<use_skill> and so on, and which
750     handlers are recognised generally depends on the type of object these
751     handlers attach to).
752    
753     See F<include/eventinc.h> for the full list of events supported, and their
754     class.
755    
756     =item package => package::
757    
758     Look for sub functions of the name C<< on_I<event> >> in the given
759     package and register them. Only handlers for eevents supported by the
760     object/class are recognised.
761    
762     =back
763    
764 root 1.94 Example, define an attachment called "sockpuppet" that calls the given
765     event handler when a monster attacks:
766    
767     cf::object::attachment sockpuppet =>
768     on_skill_attack => sub {
769     my ($self, $victim) = @_;
770     ...
771     }
772     }
773    
774 root 1.96 =item $attachable->valid
775    
776     Just because you have a perl object does not mean that the corresponding
777     C-level object still exists. If you try to access an object that has no
778     valid C counterpart anymore you get an exception at runtime. This method
779     can be used to test for existence of the C object part without causing an
780     exception.
781    
782 root 1.39 =cut
783    
784 root 1.40 # the following variables are defined in .xs and must not be re-created
785 root 1.100 our @CB_GLOBAL = (); # registry for all global events
786     our @CB_ATTACHABLE = (); # registry for all attachables
787     our @CB_OBJECT = (); # all objects (should not be used except in emergency)
788     our @CB_PLAYER = ();
789     our @CB_CLIENT = ();
790     our @CB_TYPE = (); # registry for type (cf-object class) based events
791     our @CB_MAP = ();
792 root 1.39
793 root 1.45 my %attachment;
794    
795 root 1.170 sub cf::attachable::thawer_merge {
796     # simply override everything except _meta
797     local $_[0]{_meta};
798     %{$_[0]} = %{$_[1]};
799     }
800    
801 root 1.93 sub _attach_cb($$$$) {
802     my ($registry, $event, $prio, $cb) = @_;
803 root 1.39
804     use sort 'stable';
805    
806     $cb = [$prio, $cb];
807    
808     @{$registry->[$event]} = sort
809     { $a->[0] cmp $b->[0] }
810     @{$registry->[$event] || []}, $cb;
811     }
812    
813 root 1.100 # hack
814     my %attachable_klass = map +($_ => 1), KLASS_OBJECT, KLASS_CLIENT, KLASS_PLAYER, KLASS_MAP;
815    
816 root 1.39 # attach handles attaching event callbacks
817     # the only thing the caller has to do is pass the correct
818     # registry (== where the callback attaches to).
819 root 1.93 sub _attach {
820 root 1.45 my ($registry, $klass, @arg) = @_;
821 root 1.39
822 root 1.93 my $object_type;
823 root 1.39 my $prio = 0;
824     my %cb_id = map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == $klass, 0 .. $#EVENT;
825    
826 root 1.100 #TODO: get rid of this hack
827     if ($attachable_klass{$klass}) {
828     %cb_id = (%cb_id, map +("on_" . lc $EVENT[$_][0], $_) , grep $EVENT[$_][1] == KLASS_ATTACHABLE, 0 .. $#EVENT);
829     }
830    
831 root 1.45 while (@arg) {
832     my $type = shift @arg;
833 root 1.39
834     if ($type eq "prio") {
835 root 1.45 $prio = shift @arg;
836 root 1.39
837 root 1.93 } elsif ($type eq "type") {
838     $object_type = shift @arg;
839     $registry = $CB_TYPE[$object_type] ||= [];
840    
841     } elsif ($type eq "subtype") {
842     defined $object_type or Carp::croak "subtype specified without type";
843     my $object_subtype = shift @arg;
844 root 1.267 $registry = $CB_TYPE[$object_type + $object_subtype * NUM_TYPES] ||= [];
845 root 1.93
846 root 1.39 } elsif ($type eq "package") {
847 root 1.45 my $pkg = shift @arg;
848 root 1.39
849     while (my ($name, $id) = each %cb_id) {
850     if (my $cb = $pkg->can ($name)) {
851 root 1.93 _attach_cb $registry, $id, $prio, $cb;
852 root 1.39 }
853     }
854    
855     } elsif (exists $cb_id{$type}) {
856 root 1.93 _attach_cb $registry, $cb_id{$type}, $prio, shift @arg;
857 root 1.39
858     } elsif (ref $type) {
859     warn "attaching objects not supported, ignoring.\n";
860    
861     } else {
862 root 1.45 shift @arg;
863 root 1.39 warn "attach argument '$type' not supported, ignoring.\n";
864     }
865     }
866     }
867    
868 root 1.93 sub _object_attach {
869 root 1.48 my ($obj, $name, %arg) = @_;
870 root 1.46
871 root 1.55 return if exists $obj->{_attachment}{$name};
872    
873 root 1.46 if (my $attach = $attachment{$name}) {
874     my $registry = $obj->registry;
875    
876 root 1.47 for (@$attach) {
877     my ($klass, @attach) = @$_;
878 root 1.93 _attach $registry, $klass, @attach;
879 root 1.47 }
880 root 1.46
881 root 1.48 $obj->{$name} = \%arg;
882 root 1.46 } else {
883 root 1.280 warn "object uses attachment '$name' which is not available, postponing.\n";
884 root 1.46 }
885    
886 root 1.50 $obj->{_attachment}{$name} = undef;
887 root 1.46 }
888    
889 root 1.93 sub cf::attachable::attach {
890     if (ref $_[0]) {
891     _object_attach @_;
892     } else {
893     _attach shift->_attach_registry, @_;
894     }
895 root 1.267 _recalc_want;
896 root 1.55 };
897 root 1.46
898 root 1.54 # all those should be optimised
899 root 1.93 sub cf::attachable::detach {
900 root 1.54 my ($obj, $name) = @_;
901 root 1.46
902 root 1.93 if (ref $obj) {
903     delete $obj->{_attachment}{$name};
904     reattach ($obj);
905     } else {
906     Carp::croak "cannot, currently, detach class attachments";
907     }
908 root 1.267 _recalc_want;
909 root 1.55 };
910    
911 root 1.93 sub cf::attachable::attached {
912 root 1.55 my ($obj, $name) = @_;
913    
914     exists $obj->{_attachment}{$name}
915 root 1.39 }
916    
917 root 1.100 for my $klass (qw(ATTACHABLE GLOBAL OBJECT PLAYER CLIENT MAP)) {
918 root 1.93 eval "#line " . __LINE__ . " 'cf.pm'
919     sub cf::\L$klass\E::_attach_registry {
920     (\\\@CB_$klass, KLASS_$klass)
921     }
922 root 1.45
923 root 1.93 sub cf::\L$klass\E::attachment {
924     my \$name = shift;
925 root 1.39
926 root 1.93 \$attachment{\$name} = [[KLASS_$klass, \@_]];
927     }
928     ";
929     die if $@;
930 root 1.52 }
931    
932 root 1.39 our $override;
933 elmex 1.310 our @INVOKE_RESULTS = (); # referenced from .xs code. TODO: play tricks with reify and mortals?
934 root 1.39
935 root 1.45 sub override {
936     $override = 1;
937 elmex 1.310 @INVOKE_RESULTS = (@_);
938 root 1.39 }
939    
940 root 1.45 sub do_invoke {
941 root 1.39 my $event = shift;
942 root 1.40 my $callbacks = shift;
943 root 1.39
944 elmex 1.310 @INVOKE_RESULTS = ();
945 root 1.45
946 root 1.39 local $override;
947    
948 root 1.40 for (@$callbacks) {
949 root 1.39 eval { &{$_->[1]} };
950    
951     if ($@) {
952     warn "$@";
953 root 1.64 warn "... while processing $EVENT[$event][0](@_) event, skipping processing altogether.\n";
954 root 1.39 override;
955     }
956    
957     return 1 if $override;
958     }
959    
960     0
961     }
962    
963 root 1.96 =item $bool = cf::global::invoke (EVENT_CLASS_XXX, ...)
964 root 1.55
965 root 1.96 =item $bool = $attachable->invoke (EVENT_CLASS_XXX, ...)
966 root 1.55
967 root 1.96 Generate an object-specific event with the given arguments.
968 root 1.55
969 root 1.96 This API is preliminary (most likely, the EVENT_CLASS_xxx prefix will be
970 root 1.55 removed in future versions), and there is no public API to access override
971 elmex 1.310 results (if you must, access C<@cf::INVOKE_RESULTS> directly).
972 root 1.55
973     =back
974    
975 root 1.71 =cut
976    
977 root 1.70 #############################################################################
978 root 1.45 # object support
979 root 1.312 #
980    
981     sub _can_merge {
982     my ($ob1, $ob2) = @_;
983    
984     local $Storable::canonical = 1;
985     my $fob1 = Storable::freeze $ob1;
986     my $fob2 = Storable::freeze $ob2;
987    
988     $fob1 eq $fob2
989     }
990 root 1.45
991 root 1.102 sub reattach {
992     # basically do the same as instantiate, without calling instantiate
993     my ($obj) = @_;
994    
995 root 1.169 bless $obj, ref $obj; # re-bless in case extensions have been reloaded
996    
997 root 1.102 my $registry = $obj->registry;
998    
999     @$registry = ();
1000    
1001     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
1002    
1003     for my $name (keys %{ $obj->{_attachment} || {} }) {
1004     if (my $attach = $attachment{$name}) {
1005     for (@$attach) {
1006     my ($klass, @attach) = @$_;
1007     _attach $registry, $klass, @attach;
1008     }
1009     } else {
1010     warn "object uses attachment '$name' that is not available, postponing.\n";
1011     }
1012     }
1013     }
1014    
1015 root 1.100 cf::attachable->attach (
1016     prio => -1000000,
1017     on_instantiate => sub {
1018     my ($obj, $data) = @_;
1019 root 1.45
1020 root 1.100 $data = from_json $data;
1021 root 1.45
1022 root 1.100 for (@$data) {
1023     my ($name, $args) = @$_;
1024 root 1.49
1025 root 1.100 $obj->attach ($name, %{$args || {} });
1026     }
1027     },
1028 root 1.102 on_reattach => \&reattach,
1029 root 1.100 on_clone => sub {
1030     my ($src, $dst) = @_;
1031    
1032     @{$dst->registry} = @{$src->registry};
1033    
1034     %$dst = %$src;
1035    
1036     %{$dst->{_attachment}} = %{$src->{_attachment}}
1037     if exists $src->{_attachment};
1038     },
1039     );
1040 root 1.45
1041 root 1.46 sub object_freezer_save {
1042 root 1.59 my ($filename, $rdata, $objs) = @_;
1043 root 1.46
1044 root 1.105 sync_job {
1045     if (length $$rdata) {
1046     warn sprintf "saving %s (%d,%d)\n",
1047     $filename, length $$rdata, scalar @$objs;
1048 root 1.60
1049 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
1050 root 1.60 chmod SAVE_MODE, $fh;
1051 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
1052 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
1053 root 1.60 close $fh;
1054 root 1.105
1055     if (@$objs) {
1056     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
1057     chmod SAVE_MODE, $fh;
1058     my $data = Storable::nfreeze { version => 1, objs => $objs };
1059     aio_write $fh, 0, (length $data), $data, 0;
1060 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
1061 root 1.105 close $fh;
1062     aio_rename "$filename.pst~", "$filename.pst";
1063     }
1064     } else {
1065     aio_unlink "$filename.pst";
1066     }
1067    
1068     aio_rename "$filename~", $filename;
1069 root 1.60 } else {
1070 root 1.105 warn "FATAL: $filename~: $!\n";
1071 root 1.60 }
1072 root 1.59 } else {
1073 root 1.105 aio_unlink $filename;
1074     aio_unlink "$filename.pst";
1075 root 1.59 }
1076 root 1.45 }
1077     }
1078    
1079 root 1.80 sub object_freezer_as_string {
1080     my ($rdata, $objs) = @_;
1081    
1082     use Data::Dumper;
1083    
1084 root 1.81 $$rdata . Dumper $objs
1085 root 1.80 }
1086    
1087 root 1.46 sub object_thawer_load {
1088     my ($filename) = @_;
1089    
1090 root 1.105 my ($data, $av);
1091 root 1.61
1092 root 1.105 (aio_load $filename, $data) >= 0
1093     or return;
1094 root 1.61
1095 root 1.105 unless (aio_stat "$filename.pst") {
1096     (aio_load "$filename.pst", $av) >= 0
1097     or return;
1098 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
1099 root 1.61 }
1100 root 1.45
1101 root 1.118 warn sprintf "loading %s (%d)\n",
1102 root 1.274 $filename, length $data, scalar @{$av || []};
1103 root 1.105 return ($data, $av);
1104 root 1.45 }
1105    
1106 root 1.281 =head2 COMMAND CALLBACKS
1107    
1108     =over 4
1109    
1110     =cut
1111    
1112 root 1.45 #############################################################################
1113 root 1.85 # command handling &c
1114 root 1.39
1115 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
1116 root 1.1
1117 root 1.85 Register a callback for execution when the client sends the user command
1118     $name.
1119 root 1.5
1120 root 1.85 =cut
1121 root 1.5
1122 root 1.85 sub register_command {
1123     my ($name, $cb) = @_;
1124 root 1.5
1125 root 1.85 my $caller = caller;
1126     #warn "registering command '$name/$time' to '$caller'";
1127 root 1.1
1128 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
1129 root 1.1 }
1130    
1131 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
1132 root 1.1
1133 root 1.287 Register a callback for execution when the client sends an (synchronous)
1134     extcmd packet. Ext commands will be processed in the order they are
1135     received by the server, like other user commands. The first argument is
1136     the logged-in player. Ext commands can only be processed after a player
1137     has logged in successfully.
1138    
1139     If the callback returns something, it is sent back as if reply was being
1140     called.
1141    
1142     =item cf::register_exticmd $name => \&callback($ns,$packet);
1143    
1144     Register a callback for execution when the client sends an (asynchronous)
1145     exticmd packet. Exti commands are processed by the server as soon as they
1146     are received, i.e. out of order w.r.t. other commands. The first argument
1147     is a client socket. Exti commands can be received anytime, even before
1148     log-in.
1149 root 1.1
1150 root 1.85 If the callback returns something, it is sent back as if reply was being
1151     called.
1152 root 1.1
1153 root 1.85 =cut
1154 root 1.1
1155 root 1.16 sub register_extcmd {
1156     my ($name, $cb) = @_;
1157    
1158 root 1.159 $EXTCMD{$name} = $cb;
1159 root 1.16 }
1160    
1161 root 1.287 sub register_exticmd {
1162     my ($name, $cb) = @_;
1163    
1164     $EXTICMD{$name} = $cb;
1165     }
1166    
1167 root 1.93 cf::player->attach (
1168 root 1.85 on_command => sub {
1169     my ($pl, $name, $params) = @_;
1170    
1171     my $cb = $COMMAND{$name}
1172     or return;
1173    
1174     for my $cmd (@$cb) {
1175     $cmd->[1]->($pl->ob, $params);
1176     }
1177    
1178     cf::override;
1179     },
1180     on_extcmd => sub {
1181     my ($pl, $buf) = @_;
1182    
1183 root 1.290 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) };
1184 root 1.85
1185     if (ref $msg) {
1186 root 1.316 my ($type, $reply, @payload) =
1187     "ARRAY" eq ref $msg
1188     ? @$msg
1189     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
1190    
1191     if (my $cb = $EXTCMD{$type}) {
1192     my @reply = $cb->($pl, @payload);
1193    
1194     $pl->ext_reply ($reply, @reply)
1195     if $reply;
1196 root 1.85 }
1197     } else {
1198     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1199     }
1200    
1201     cf::override;
1202     },
1203 root 1.93 );
1204 root 1.85
1205 root 1.278 sub load_extensions {
1206     cf::sync_job {
1207     my %todo;
1208    
1209     for my $path (<$LIBDIR/*.ext>) {
1210     next unless -r $path;
1211    
1212     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
1213     my $base = $1;
1214     my $pkg = $1;
1215     $pkg =~ s/[^[:word:]]/_/g;
1216     $pkg = "ext::$pkg";
1217    
1218     open my $fh, "<:utf8", $path
1219     or die "$path: $!";
1220    
1221     my $source = do { local $/; <$fh> };
1222 root 1.1
1223 root 1.278 my %ext = (
1224     path => $path,
1225     base => $base,
1226     pkg => $pkg,
1227     );
1228 root 1.1
1229 root 1.279 $ext{meta} = { map { (split /=/, $_, 2)[0, 1] } split /\s+/, $1 }
1230     if $source =~ /\A#!.*?perl.*?#\s*(.*)$/m;
1231 root 1.1
1232 root 1.278 $ext{source} =
1233     "package $pkg; use strict; use utf8;\n"
1234     . "#line 1 \"$path\"\n{\n"
1235     . $source
1236     . "\n};\n1";
1237 root 1.1
1238 root 1.278 $todo{$base} = \%ext;
1239 root 1.166 }
1240 root 1.1
1241 root 1.278 my %done;
1242     while (%todo) {
1243     my $progress;
1244    
1245     while (my ($k, $v) = each %todo) {
1246 root 1.279 for (split /,\s*/, $v->{meta}{depends}) {
1247 root 1.278 goto skip
1248     unless exists $done{$_};
1249     }
1250    
1251     warn "... loading '$k' into '$v->{pkg}'\n";
1252    
1253     unless (eval $v->{source}) {
1254     my $msg = $@ ? "$v->{path}: $@\n"
1255 root 1.279 : "$v->{base}: extension inactive.\n";
1256 root 1.278
1257     if (exists $v->{meta}{mandatory}) {
1258     warn $msg;
1259     warn "mandatory extension failed to load, exiting.\n";
1260     exit 1;
1261     }
1262    
1263 root 1.279 warn $msg;
1264 root 1.278 }
1265    
1266     $done{$k} = delete $todo{$k};
1267     push @EXTS, $v->{pkg};
1268 root 1.279 $progress = 1;
1269 root 1.278 }
1270 root 1.1
1271 root 1.278 skip:
1272     die "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n"
1273     unless $progress;
1274     }
1275     };
1276 root 1.1 }
1277    
1278 root 1.8 #############################################################################
1279 root 1.70
1280 root 1.281 =back
1281    
1282 root 1.70 =head2 CORE EXTENSIONS
1283    
1284     Functions and methods that extend core crossfire objects.
1285    
1286 root 1.143 =cut
1287    
1288     package cf::player;
1289    
1290 root 1.154 use Coro::AIO;
1291    
1292 root 1.95 =head3 cf::player
1293    
1294 root 1.70 =over 4
1295 root 1.22
1296 root 1.143 =item cf::player::find $login
1297 root 1.23
1298 root 1.143 Returns the given player object, loading it if necessary (might block).
1299 root 1.23
1300     =cut
1301    
1302 root 1.145 sub playerdir($) {
1303 root 1.253 "$PLAYERDIR/"
1304 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1305     }
1306    
1307 root 1.143 sub path($) {
1308 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1309    
1310 root 1.234 (playerdir $login) . "/playerdata"
1311 root 1.143 }
1312    
1313     sub find_active($) {
1314     $cf::PLAYER{$_[0]}
1315     and $cf::PLAYER{$_[0]}->active
1316     and $cf::PLAYER{$_[0]}
1317     }
1318    
1319     sub exists($) {
1320     my ($login) = @_;
1321    
1322     $cf::PLAYER{$login}
1323 root 1.180 or cf::sync_job { !aio_stat path $login }
1324 root 1.143 }
1325    
1326     sub find($) {
1327     return $cf::PLAYER{$_[0]} || do {
1328     my $login = $_[0];
1329    
1330     my $guard = cf::lock_acquire "user_find:$login";
1331    
1332 root 1.151 $cf::PLAYER{$_[0]} || do {
1333 root 1.234 # rename old playerfiles to new ones
1334     #TODO: remove when no longer required
1335     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1336     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1337     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1338     aio_unlink +(playerdir $login) . "/$login.pl";
1339    
1340 root 1.151 my $pl = load_pl path $login
1341     or return;
1342     $cf::PLAYER{$login} = $pl
1343     }
1344     }
1345 root 1.143 }
1346    
1347     sub save($) {
1348     my ($pl) = @_;
1349    
1350     return if $pl->{deny_save};
1351    
1352     my $path = path $pl;
1353     my $guard = cf::lock_acquire "user_save:$path";
1354    
1355     return if $pl->{deny_save};
1356 root 1.146
1357 root 1.154 aio_mkdir playerdir $pl, 0770;
1358 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1359    
1360     $pl->save_pl ($path);
1361     Coro::cede;
1362     }
1363    
1364     sub new($) {
1365     my ($login) = @_;
1366    
1367     my $self = create;
1368    
1369     $self->ob->name ($login);
1370     $self->{deny_save} = 1;
1371    
1372     $cf::PLAYER{$login} = $self;
1373    
1374     $self
1375 root 1.23 }
1376    
1377 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1378    
1379     =cut
1380    
1381     sub send_msg {
1382     my $ns = shift->ns
1383     or return;
1384     $ns->send_msg (@_);
1385     }
1386    
1387 root 1.154 =item $pl->quit_character
1388    
1389     Nukes the player without looking back. If logged in, the connection will
1390     be destroyed. May block for a long time.
1391    
1392     =cut
1393    
1394 root 1.145 sub quit_character {
1395     my ($pl) = @_;
1396    
1397 root 1.220 my $name = $pl->ob->name;
1398    
1399 root 1.145 $pl->{deny_save} = 1;
1400     $pl->password ("*"); # this should lock out the player until we nuked the dir
1401    
1402     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1403     $pl->deactivate;
1404     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1405     $pl->ns->destroy if $pl->ns;
1406    
1407     my $path = playerdir $pl;
1408     my $temp = "$path~$cf::RUNTIME~deleting~";
1409 root 1.154 aio_rename $path, $temp;
1410 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1411     $pl->destroy;
1412 root 1.220
1413     my $prefix = qr<^~\Q$name\E/>;
1414    
1415     # nuke player maps
1416     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1417    
1418 root 1.150 IO::AIO::aio_rmtree $temp;
1419 root 1.145 }
1420    
1421 pippijn 1.221 =item $pl->kick
1422    
1423     Kicks a player out of the game. This destroys the connection.
1424    
1425     =cut
1426    
1427     sub kick {
1428     my ($pl, $kicker) = @_;
1429    
1430     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1431     $pl->killer ("kicked");
1432     $pl->ns->destroy;
1433     }
1434    
1435 root 1.154 =item cf::player::list_logins
1436    
1437     Returns am arrayref of all valid playernames in the system, can take a
1438     while and may block, so not sync_job-capable, ever.
1439    
1440     =cut
1441    
1442     sub list_logins {
1443 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1444 root 1.154 or return [];
1445    
1446     my @logins;
1447    
1448     for my $login (@$dirs) {
1449     my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next;
1450     aio_read $fh, 0, 512, my $buf, 0 or next;
1451 root 1.155 $buf !~ /^password -------------$/m or next; # official not-valid tag
1452 root 1.154
1453     utf8::decode $login;
1454     push @logins, $login;
1455     }
1456    
1457     \@logins
1458     }
1459    
1460     =item $player->maps
1461    
1462 root 1.166 Returns an arrayref of map paths that are private for this
1463 root 1.154 player. May block.
1464    
1465     =cut
1466    
1467     sub maps($) {
1468     my ($pl) = @_;
1469    
1470 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1471    
1472 root 1.154 my $files = aio_readdir playerdir $pl
1473     or return;
1474    
1475     my @paths;
1476    
1477     for (@$files) {
1478     utf8::decode $_;
1479     next if /\.(?:pl|pst)$/;
1480 root 1.158 next unless /^$PATH_SEP/o;
1481 root 1.154
1482 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1483 root 1.154 }
1484    
1485     \@paths
1486     }
1487    
1488 root 1.283 =item $protocol_xml = $player->expand_cfpod ($crossfire_pod)
1489    
1490     Expand crossfire pod fragments into protocol xml.
1491    
1492     =cut
1493    
1494     sub expand_cfpod {
1495     ((my $self), (local $_)) = @_;
1496    
1497     # escape & and <
1498     s/&/&amp;/g;
1499     s/(?<![BIUGH])</&lt;/g;
1500    
1501     # this is buggy, it needs to properly take care of nested <'s
1502    
1503     1 while
1504     # replace B<>, I<>, U<> etc.
1505     s/B<([^\>]*)>/<b>$1<\/b>/
1506     || s/I<([^\>]*)>/<i>$1<\/i>/
1507     || s/U<([^\>]*)>/<u>$1<\/u>/
1508     # replace G<male|female> tags
1509     || s{G<([^>|]*)\|([^>]*)>}{
1510     $self->gender ? $2 : $1
1511     }ge
1512     # replace H<hint text>
1513 root 1.291 || s{H<([^\>]*)>}
1514     {
1515     ("<fg name=\"lightblue\">[$1 (Use hintmode to suppress hints)]</fg>",
1516     "<fg name=\"lightblue\">[Hint suppressed, see hintmode]</fg>",
1517     "")
1518     [$self->{hintmode}]
1519     }ge;
1520 root 1.283
1521     # create single paragraphs (very hackish)
1522     s/(?<=\S)\n(?=\w)/ /g;
1523    
1524 root 1.291 # compress some whitespace
1525 root 1.295 s/\s+\n/\n/g; # ws line-ends
1526     s/\n\n+/\n/g; # double lines
1527     s/^\n+//; # beginning lines
1528     s/\n+$//; # ending lines
1529 root 1.293
1530 root 1.283 $_
1531     }
1532    
1533 root 1.291 sub hintmode {
1534     $_[0]{hintmode} = $_[1] if @_ > 1;
1535     $_[0]{hintmode}
1536     }
1537    
1538 root 1.316 =item $player->ext_reply ($msgid, @msg)
1539 root 1.95
1540     Sends an ext reply to the player.
1541    
1542     =cut
1543    
1544 root 1.316 sub ext_reply($$@) {
1545     my ($self, $id, @msg) = @_;
1546 root 1.95
1547 root 1.316 if ($self->ns->extcmd == 2) {
1548     $self->send ("ext " . $self->ns->{json_coder}->encode (["reply-$id", @msg]));
1549     } elsif ($self->ns->extcmd == 1) {
1550     #TODO: version 1, remove
1551     unshift @msg, msgtype => "reply", msgid => $id;
1552     $self->send ("ext " . $self->ns->{json_coder}->encode ({@msg}));
1553     }
1554 root 1.95 }
1555    
1556 root 1.316 =item $player->ext_msg ($type, @msg)
1557 root 1.231
1558     Sends an ext event to the client.
1559    
1560     =cut
1561    
1562 root 1.316 sub ext_msg($$@) {
1563     my ($self, $type, @msg) = @_;
1564 root 1.231
1565 root 1.316 $self->ns->ext_msg ($type, @msg);
1566 root 1.231 }
1567    
1568 root 1.238 =head3 cf::region
1569    
1570     =over 4
1571    
1572     =cut
1573    
1574     package cf::region;
1575    
1576     =item cf::region::find_by_path $path
1577    
1578 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1579 root 1.238
1580     =cut
1581    
1582     sub find_by_path($) {
1583     my ($path) = @_;
1584    
1585     my ($match, $specificity);
1586    
1587     for my $region (list) {
1588 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1589 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1590     if $region->specificity > $specificity;
1591     }
1592     }
1593    
1594     $match
1595     }
1596 root 1.143
1597 root 1.95 =back
1598    
1599 root 1.110 =head3 cf::map
1600    
1601     =over 4
1602    
1603     =cut
1604    
1605     package cf::map;
1606    
1607     use Fcntl;
1608     use Coro::AIO;
1609    
1610 root 1.166 use overload
1611 root 1.173 '""' => \&as_string,
1612     fallback => 1;
1613 root 1.166
1614 root 1.133 our $MAX_RESET = 3600;
1615     our $DEFAULT_RESET = 3000;
1616 root 1.110
1617     sub generate_random_map {
1618 root 1.166 my ($self, $rmp) = @_;
1619 root 1.110 # mit "rum" bekleckern, nicht
1620 root 1.166 $self->_create_random_map (
1621 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1622     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1623     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1624     $rmp->{exit_on_final_map},
1625     $rmp->{xsize}, $rmp->{ysize},
1626     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1627     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1628     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1629     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1630     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1631 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1632     )
1633 root 1.110 }
1634    
1635 root 1.187 =item cf::map->register ($regex, $prio)
1636    
1637     Register a handler for the map path matching the given regex at the
1638     givne priority (higher is better, built-in handlers have priority 0, the
1639     default).
1640    
1641     =cut
1642    
1643 root 1.166 sub register {
1644 root 1.187 my (undef, $regex, $prio) = @_;
1645 root 1.166 my $pkg = caller;
1646    
1647     no strict;
1648     push @{"$pkg\::ISA"}, __PACKAGE__;
1649    
1650 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1651 root 1.166 }
1652    
1653     # also paths starting with '/'
1654 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1655 root 1.166
1656 root 1.170 sub thawer_merge {
1657 root 1.172 my ($self, $merge) = @_;
1658    
1659 root 1.170 # we have to keep some variables in memory intact
1660 root 1.172 local $self->{path};
1661     local $self->{load_path};
1662 root 1.170
1663 root 1.172 $self->SUPER::thawer_merge ($merge);
1664 root 1.170 }
1665    
1666 root 1.166 sub normalise {
1667     my ($path, $base) = @_;
1668    
1669 root 1.192 $path = "$path"; # make sure its a string
1670    
1671 root 1.199 $path =~ s/\.map$//;
1672    
1673 root 1.166 # map plan:
1674     #
1675     # /! non-realised random map exit (special hack!)
1676     # {... are special paths that are not being touched
1677     # ?xxx/... are special absolute paths
1678     # ?random/... random maps
1679     # /... normal maps
1680     # ~user/... per-player map of a specific user
1681    
1682     $path =~ s/$PATH_SEP/\//go;
1683    
1684     # treat it as relative path if it starts with
1685     # something that looks reasonable
1686     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1687     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1688    
1689     $base =~ s{[^/]+/?$}{};
1690     $path = "$base/$path";
1691     }
1692    
1693     for ($path) {
1694     redo if s{//}{/};
1695     redo if s{/\.?/}{/};
1696     redo if s{/[^/]+/\.\./}{/};
1697     }
1698    
1699     $path
1700     }
1701    
1702     sub new_from_path {
1703     my (undef, $path, $base) = @_;
1704    
1705     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1706    
1707     $path = normalise $path, $base;
1708    
1709 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1710     if ($path =~ $EXT_MAP{$pkg}[1]) {
1711 root 1.166 my $self = bless cf::map::new, $pkg;
1712     $self->{path} = $path; $self->path ($path);
1713     $self->init; # pass $1 etc.
1714     return $self;
1715     }
1716     }
1717    
1718 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1719 root 1.166 ()
1720     }
1721    
1722     sub init {
1723     my ($self) = @_;
1724    
1725     $self
1726     }
1727    
1728     sub as_string {
1729     my ($self) = @_;
1730    
1731     "$self->{path}"
1732     }
1733    
1734     # the displayed name, this is a one way mapping
1735     sub visible_name {
1736     &as_string
1737     }
1738    
1739     # the original (read-only) location
1740     sub load_path {
1741     my ($self) = @_;
1742    
1743 root 1.254 "$MAPDIR/$self->{path}.map"
1744 root 1.166 }
1745    
1746     # the temporary/swap location
1747     sub save_path {
1748     my ($self) = @_;
1749    
1750 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1751 root 1.254 "$TMPDIR/$path.map"
1752 root 1.166 }
1753    
1754     # the unique path, undef == no special unique path
1755     sub uniq_path {
1756     my ($self) = @_;
1757    
1758 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1759 root 1.253 "$UNIQUEDIR/$path"
1760 root 1.166 }
1761    
1762 root 1.110 # and all this just because we cannot iterate over
1763     # all maps in C++...
1764     sub change_all_map_light {
1765     my ($change) = @_;
1766    
1767 root 1.122 $_->change_map_light ($change)
1768     for grep $_->outdoor, values %cf::MAP;
1769 root 1.110 }
1770    
1771 root 1.275 sub decay_objects {
1772     my ($self) = @_;
1773    
1774     return if $self->{deny_reset};
1775    
1776     $self->do_decay_objects;
1777     }
1778    
1779 root 1.166 sub unlink_save {
1780     my ($self) = @_;
1781    
1782     utf8::encode (my $save = $self->save_path);
1783 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1784     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1785 root 1.166 }
1786    
1787     sub load_header_from($) {
1788     my ($self, $path) = @_;
1789 root 1.110
1790     utf8::encode $path;
1791 root 1.200 #aio_open $path, O_RDONLY, 0
1792     # or return;
1793 root 1.110
1794 root 1.166 $self->_load_header ($path)
1795 root 1.110 or return;
1796    
1797 root 1.166 $self->{load_path} = $path;
1798 root 1.135
1799 root 1.166 1
1800     }
1801 root 1.110
1802 root 1.188 sub load_header_orig {
1803 root 1.166 my ($self) = @_;
1804 root 1.110
1805 root 1.166 $self->load_header_from ($self->load_path)
1806 root 1.110 }
1807    
1808 root 1.188 sub load_header_temp {
1809 root 1.166 my ($self) = @_;
1810 root 1.110
1811 root 1.166 $self->load_header_from ($self->save_path)
1812     }
1813 root 1.110
1814 root 1.188 sub prepare_temp {
1815     my ($self) = @_;
1816    
1817     $self->last_access ((delete $self->{last_access})
1818     || $cf::RUNTIME); #d#
1819     # safety
1820     $self->{instantiate_time} = $cf::RUNTIME
1821     if $self->{instantiate_time} > $cf::RUNTIME;
1822     }
1823    
1824     sub prepare_orig {
1825     my ($self) = @_;
1826    
1827     $self->{load_original} = 1;
1828     $self->{instantiate_time} = $cf::RUNTIME;
1829     $self->last_access ($cf::RUNTIME);
1830     $self->instantiate;
1831     }
1832    
1833 root 1.166 sub load_header {
1834     my ($self) = @_;
1835 root 1.110
1836 root 1.188 if ($self->load_header_temp) {
1837     $self->prepare_temp;
1838 root 1.166 } else {
1839 root 1.188 $self->load_header_orig
1840 root 1.166 or return;
1841 root 1.188 $self->prepare_orig;
1842 root 1.166 }
1843 root 1.120
1844 root 1.275 $self->{deny_reset} = 1
1845     if $self->no_reset;
1846    
1847 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1848     unless $self->default_region;
1849    
1850 root 1.166 1
1851     }
1852 root 1.110
1853 root 1.166 sub find;
1854     sub find {
1855     my ($path, $origin) = @_;
1856 root 1.134
1857 root 1.166 $path = normalise $path, $origin && $origin->path;
1858 root 1.110
1859 root 1.166 cf::lock_wait "map_find:$path";
1860 root 1.110
1861 root 1.166 $cf::MAP{$path} || do {
1862     my $guard = cf::lock_acquire "map_find:$path";
1863     my $map = new_from_path cf::map $path
1864     or return;
1865 root 1.110
1866 root 1.116 $map->{last_save} = $cf::RUNTIME;
1867 root 1.110
1868 root 1.166 $map->load_header
1869     or return;
1870 root 1.134
1871 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
1872 root 1.185 # doing this can freeze the server in a sync job, obviously
1873     #$cf::WAIT_FOR_TICK->wait;
1874 root 1.112 $map->reset;
1875 root 1.123 undef $guard;
1876 root 1.192 return find $path;
1877 root 1.112 }
1878 root 1.110
1879 root 1.166 $cf::MAP{$path} = $map
1880 root 1.110 }
1881     }
1882    
1883 root 1.188 sub pre_load { }
1884     sub post_load { }
1885    
1886 root 1.110 sub load {
1887     my ($self) = @_;
1888    
1889 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
1890    
1891 root 1.120 my $path = $self->{path};
1892    
1893 root 1.256 {
1894     my $guard = cf::lock_acquire "map_load:$path";
1895    
1896     return if $self->in_memory != cf::MAP_SWAPPED;
1897 root 1.110
1898 root 1.256 $self->in_memory (cf::MAP_LOADING);
1899 root 1.110
1900 root 1.256 $self->alloc;
1901 root 1.188
1902 root 1.256 $self->pre_load;
1903     Coro::cede;
1904 root 1.188
1905 root 1.256 $self->_load_objects ($self->{load_path}, 1)
1906     or return;
1907 root 1.110
1908 root 1.256 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1909     if delete $self->{load_original};
1910 root 1.111
1911 root 1.256 if (my $uniq = $self->uniq_path) {
1912     utf8::encode $uniq;
1913     if (aio_open $uniq, O_RDONLY, 0) {
1914     $self->clear_unique_items;
1915     $self->_load_objects ($uniq, 0);
1916     }
1917 root 1.110 }
1918    
1919 root 1.166 Coro::cede;
1920 root 1.256 # now do the right thing for maps
1921     $self->link_multipart_objects;
1922 root 1.110 $self->difficulty ($self->estimate_difficulty)
1923     unless $self->difficulty;
1924 root 1.166 Coro::cede;
1925 root 1.256
1926     unless ($self->{deny_activate}) {
1927     $self->decay_objects;
1928     $self->fix_auto_apply;
1929     $self->update_buttons;
1930     Coro::cede;
1931     $self->set_darkness_map;
1932     Coro::cede;
1933     $self->activate;
1934     }
1935    
1936 root 1.325 $self->{last_save} = $cf::RUNTIME;
1937     $self->last_access ($cf::RUNTIME);
1938 root 1.324
1939 root 1.256 $self->in_memory (cf::MAP_IN_MEMORY);
1940 root 1.110 }
1941    
1942 root 1.188 $self->post_load;
1943 root 1.166 }
1944    
1945     sub customise_for {
1946     my ($self, $ob) = @_;
1947    
1948     return find "~" . $ob->name . "/" . $self->{path}
1949     if $self->per_player;
1950 root 1.134
1951 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
1952     # if $self->per_party;
1953    
1954 root 1.166 $self
1955 root 1.110 }
1956    
1957 root 1.157 # find and load all maps in the 3x3 area around a map
1958     sub load_diag {
1959     my ($map) = @_;
1960    
1961     my @diag; # diagonal neighbours
1962    
1963     for (0 .. 3) {
1964     my $neigh = $map->tile_path ($_)
1965     or next;
1966     $neigh = find $neigh, $map
1967     or next;
1968     $neigh->load;
1969    
1970     push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh],
1971     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1972     }
1973    
1974     for (@diag) {
1975     my $neigh = find @$_
1976     or next;
1977     $neigh->load;
1978     }
1979     }
1980    
1981 root 1.133 sub find_sync {
1982 root 1.110 my ($path, $origin) = @_;
1983    
1984 root 1.157 cf::sync_job { find $path, $origin }
1985 root 1.133 }
1986    
1987     sub do_load_sync {
1988     my ($map) = @_;
1989 root 1.110
1990 root 1.133 cf::sync_job { $map->load };
1991 root 1.110 }
1992    
1993 root 1.157 our %MAP_PREFETCH;
1994 root 1.183 our $MAP_PREFETCHER = undef;
1995 root 1.157
1996     sub find_async {
1997     my ($path, $origin) = @_;
1998    
1999 root 1.166 $path = normalise $path, $origin && $origin->{path};
2000 root 1.157
2001 root 1.166 if (my $map = $cf::MAP{$path}) {
2002 root 1.157 return $map if $map->in_memory == cf::MAP_IN_MEMORY;
2003     }
2004    
2005 root 1.183 undef $MAP_PREFETCH{$path};
2006     $MAP_PREFETCHER ||= cf::async {
2007     while (%MAP_PREFETCH) {
2008     for my $path (keys %MAP_PREFETCH) {
2009 root 1.308 if (my $map = find $path) {
2010     $map->load;
2011     }
2012 root 1.183
2013     delete $MAP_PREFETCH{$path};
2014     }
2015     }
2016     undef $MAP_PREFETCHER;
2017     };
2018 root 1.189 $MAP_PREFETCHER->prio (6);
2019 root 1.157
2020     ()
2021     }
2022    
2023 root 1.110 sub save {
2024     my ($self) = @_;
2025    
2026 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
2027    
2028 root 1.110 $self->{last_save} = $cf::RUNTIME;
2029    
2030     return unless $self->dirty;
2031    
2032 root 1.166 my $save = $self->save_path; utf8::encode $save;
2033     my $uniq = $self->uniq_path; utf8::encode $uniq;
2034 root 1.117
2035 root 1.110 $self->{load_path} = $save;
2036    
2037     return if $self->{deny_save};
2038    
2039 root 1.132 local $self->{last_access} = $self->last_access;#d#
2040    
2041 root 1.143 cf::async {
2042     $_->contr->save for $self->players;
2043     };
2044    
2045 root 1.110 if ($uniq) {
2046 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2047     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2048 root 1.110 } else {
2049 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2050 root 1.110 }
2051     }
2052    
2053     sub swap_out {
2054     my ($self) = @_;
2055    
2056 root 1.130 # save first because save cedes
2057     $self->save;
2058    
2059 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
2060    
2061 root 1.110 return if $self->players;
2062     return if $self->in_memory != cf::MAP_IN_MEMORY;
2063     return if $self->{deny_save};
2064    
2065     $self->clear;
2066     $self->in_memory (cf::MAP_SWAPPED);
2067     }
2068    
2069 root 1.112 sub reset_at {
2070     my ($self) = @_;
2071 root 1.110
2072     # TODO: safety, remove and allow resettable per-player maps
2073 root 1.114 return 1e99 if $self->{deny_reset};
2074 root 1.110
2075 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2076 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2077 root 1.110
2078 root 1.112 $time + $to
2079     }
2080    
2081     sub should_reset {
2082     my ($self) = @_;
2083    
2084     $self->reset_at <= $cf::RUNTIME
2085 root 1.111 }
2086    
2087 root 1.110 sub reset {
2088     my ($self) = @_;
2089    
2090 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2091 root 1.137
2092 root 1.110 return if $self->players;
2093    
2094 root 1.274 warn "resetting map ", $self->path;
2095 root 1.110
2096 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2097    
2098     # need to save uniques path
2099     unless ($self->{deny_save}) {
2100     my $uniq = $self->uniq_path; utf8::encode $uniq;
2101    
2102     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2103     if $uniq;
2104     }
2105    
2106 root 1.111 delete $cf::MAP{$self->path};
2107 root 1.110
2108 root 1.167 $self->clear;
2109    
2110 root 1.110 $_->clear_links_to ($self) for values %cf::MAP;
2111    
2112 root 1.166 $self->unlink_save;
2113 root 1.111 $self->destroy;
2114 root 1.110 }
2115    
2116 root 1.114 my $nuke_counter = "aaaa";
2117    
2118     sub nuke {
2119     my ($self) = @_;
2120    
2121 root 1.174 delete $cf::MAP{$self->path};
2122    
2123     $self->unlink_save;
2124    
2125     bless $self, "cf::map";
2126     delete $self->{deny_reset};
2127 root 1.114 $self->{deny_save} = 1;
2128     $self->reset_timeout (1);
2129 root 1.174 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2130    
2131     $cf::MAP{$self->path} = $self;
2132    
2133 root 1.114 $self->reset; # polite request, might not happen
2134     }
2135    
2136 root 1.276 =item $maps = cf::map::tmp_maps
2137    
2138     Returns an arrayref with all map paths of currently instantiated and saved
2139 root 1.277 maps. May block.
2140 root 1.276
2141     =cut
2142    
2143     sub tmp_maps() {
2144     [
2145     map {
2146     utf8::decode $_;
2147 root 1.277 /\.map$/
2148 root 1.276 ? normalise $_
2149     : ()
2150     } @{ aio_readdir $TMPDIR or [] }
2151     ]
2152     }
2153    
2154 root 1.277 =item $maps = cf::map::random_maps
2155    
2156     Returns an arrayref with all map paths of currently instantiated and saved
2157     random maps. May block.
2158    
2159     =cut
2160    
2161     sub random_maps() {
2162     [
2163     map {
2164     utf8::decode $_;
2165     /\.map$/
2166     ? normalise "?random/$_"
2167     : ()
2168     } @{ aio_readdir $RANDOMDIR or [] }
2169     ]
2170     }
2171    
2172 root 1.158 =item cf::map::unique_maps
2173    
2174 root 1.166 Returns an arrayref of paths of all shared maps that have
2175 root 1.158 instantiated unique items. May block.
2176    
2177     =cut
2178    
2179     sub unique_maps() {
2180 root 1.276 [
2181     map {
2182     utf8::decode $_;
2183 root 1.277 /\.map$/
2184 root 1.276 ? normalise $_
2185     : ()
2186     } @{ aio_readdir $UNIQUEDIR or [] }
2187     ]
2188 root 1.158 }
2189    
2190 root 1.155 package cf;
2191    
2192     =back
2193    
2194     =head3 cf::object
2195    
2196     =cut
2197    
2198     package cf::object;
2199    
2200     =over 4
2201    
2202     =item $ob->inv_recursive
2203 root 1.110
2204 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
2205 root 1.110
2206 root 1.155 =cut
2207 root 1.144
2208 root 1.155 sub inv_recursive_;
2209     sub inv_recursive_ {
2210     map { $_, inv_recursive_ $_->inv } @_
2211     }
2212 root 1.110
2213 root 1.155 sub inv_recursive {
2214     inv_recursive_ inv $_[0]
2215 root 1.110 }
2216    
2217     package cf;
2218    
2219     =back
2220    
2221 root 1.95 =head3 cf::object::player
2222    
2223     =over 4
2224    
2225 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2226 root 1.28
2227     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2228     can be C<undef>. Does the right thing when the player is currently in a
2229     dialogue with the given NPC character.
2230    
2231     =cut
2232    
2233 root 1.22 # rough implementation of a future "reply" method that works
2234     # with dialog boxes.
2235 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2236 root 1.23 sub cf::object::player::reply($$$;$) {
2237     my ($self, $npc, $msg, $flags) = @_;
2238    
2239     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2240 root 1.22
2241 root 1.24 if ($self->{record_replies}) {
2242     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2243 elmex 1.282
2244 root 1.24 } else {
2245 elmex 1.282 my $pl = $self->contr;
2246    
2247     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2248 root 1.316 my $dialog = $pl->{npc_dialog};
2249     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2250 elmex 1.282
2251     } else {
2252     $msg = $npc->name . " says: $msg" if $npc;
2253     $self->message ($msg, $flags);
2254     }
2255 root 1.24 }
2256 root 1.22 }
2257    
2258 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2259    
2260     =cut
2261    
2262     sub cf::object::send_msg {
2263     my $pl = shift->contr
2264     or return;
2265     $pl->send_msg (@_);
2266     }
2267    
2268 root 1.79 =item $player_object->may ("access")
2269    
2270     Returns wether the given player is authorized to access resource "access"
2271     (e.g. "command_wizcast").
2272    
2273     =cut
2274    
2275     sub cf::object::player::may {
2276     my ($self, $access) = @_;
2277    
2278     $self->flag (cf::FLAG_WIZ) ||
2279     (ref $cf::CFG{"may_$access"}
2280     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2281     : $cf::CFG{"may_$access"})
2282     }
2283 root 1.70
2284 root 1.115 =item $player_object->enter_link
2285    
2286     Freezes the player and moves him/her to a special map (C<{link}>).
2287    
2288 root 1.166 The player should be reasonably safe there for short amounts of time. You
2289 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
2290    
2291 root 1.166 Will never block.
2292    
2293 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2294    
2295 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2296     map. If the map is not valid (or omitted), the player will be moved back
2297     to the location he/she was before the call to C<enter_link>, or, if that
2298     fails, to the emergency map position.
2299 root 1.115
2300     Might block.
2301    
2302     =cut
2303    
2304 root 1.166 sub link_map {
2305     unless ($LINK_MAP) {
2306     $LINK_MAP = cf::map::find "{link}"
2307 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2308 root 1.166 $LINK_MAP->load;
2309     }
2310    
2311     $LINK_MAP
2312     }
2313    
2314 root 1.110 sub cf::object::player::enter_link {
2315     my ($self) = @_;
2316    
2317 root 1.259 $self->deactivate_recursive;
2318 root 1.258
2319 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2320 root 1.110
2321 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2322 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2323 root 1.110
2324 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
2325 root 1.110 }
2326    
2327     sub cf::object::player::leave_link {
2328     my ($self, $map, $x, $y) = @_;
2329    
2330 root 1.270 return unless $self->contr->active;
2331    
2332 root 1.110 my $link_pos = delete $self->{_link_pos};
2333    
2334     unless ($map) {
2335     # restore original map position
2336     ($map, $x, $y) = @{ $link_pos || [] };
2337 root 1.133 $map = cf::map::find $map;
2338 root 1.110
2339     unless ($map) {
2340     ($map, $x, $y) = @$EMERGENCY_POSITION;
2341 root 1.133 $map = cf::map::find $map
2342 root 1.110 or die "FATAL: cannot load emergency map\n";
2343     }
2344     }
2345    
2346     ($x, $y) = (-1, -1)
2347     unless (defined $x) && (defined $y);
2348    
2349     # use -1 or undef as default coordinates, not 0, 0
2350     ($x, $y) = ($map->enter_x, $map->enter_y)
2351     if $x <=0 && $y <= 0;
2352    
2353     $map->load;
2354 root 1.157 $map->load_diag;
2355 root 1.110
2356 root 1.143 return unless $self->contr->active;
2357 root 1.110 $self->activate_recursive;
2358 root 1.215
2359     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2360 root 1.110 $self->enter_map ($map, $x, $y);
2361     }
2362    
2363 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2364 root 1.268
2365     Moves the player to the given map-path and coordinates by first freezing
2366     her, loading and preparing them map, calling the provided $check callback
2367     that has to return the map if sucecssful, and then unfreezes the player on
2368 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2369     be called at the end of this process.
2370 root 1.110
2371     =cut
2372    
2373 root 1.270 our $GOTOGEN;
2374    
2375 root 1.136 sub cf::object::player::goto {
2376 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2377 root 1.268
2378 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2379     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2380    
2381 root 1.110 $self->enter_link;
2382    
2383 root 1.140 (async {
2384 root 1.197 my $map = eval {
2385     my $map = cf::map::find $path;
2386 root 1.268
2387     if ($map) {
2388     $map = $map->customise_for ($self);
2389     $map = $check->($map) if $check && $map;
2390     } else {
2391     $self->message ("The exit to '$path' is closed", cf::NDI_UNIQUE | cf::NDI_RED);
2392     }
2393    
2394 root 1.197 $map
2395 root 1.268 };
2396    
2397     if ($@) {
2398     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2399     LOG llevError | logBacktrace, Carp::longmess $@;
2400     }
2401 root 1.115
2402 root 1.270 if ($gen == $self->{_goto_generation}) {
2403     delete $self->{_goto_generation};
2404     $self->leave_link ($map, $x, $y);
2405     }
2406 root 1.306
2407     $done->() if $done;
2408 root 1.110 })->prio (1);
2409     }
2410    
2411     =item $player_object->enter_exit ($exit_object)
2412    
2413     =cut
2414    
2415     sub parse_random_map_params {
2416     my ($spec) = @_;
2417    
2418     my $rmp = { # defaults
2419 root 1.181 xsize => (cf::rndm 15, 40),
2420     ysize => (cf::rndm 15, 40),
2421     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2422 root 1.182 #layout => string,
2423 root 1.110 };
2424    
2425     for (split /\n/, $spec) {
2426     my ($k, $v) = split /\s+/, $_, 2;
2427    
2428     $rmp->{lc $k} = $v if (length $k) && (length $v);
2429     }
2430    
2431     $rmp
2432     }
2433    
2434     sub prepare_random_map {
2435     my ($exit) = @_;
2436    
2437 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
2438    
2439 root 1.110 # all this does is basically replace the /! path by
2440     # a new random map path (?random/...) with a seed
2441     # that depends on the exit object
2442    
2443     my $rmp = parse_random_map_params $exit->msg;
2444    
2445     if ($exit->map) {
2446 root 1.198 $rmp->{region} = $exit->region->name;
2447 root 1.110 $rmp->{origin_map} = $exit->map->path;
2448     $rmp->{origin_x} = $exit->x;
2449     $rmp->{origin_y} = $exit->y;
2450     }
2451    
2452     $rmp->{random_seed} ||= $exit->random_seed;
2453    
2454     my $data = cf::to_json $rmp;
2455     my $md5 = Digest::MD5::md5_hex $data;
2456 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2457 root 1.110
2458 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2459 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2460 root 1.177 undef $fh;
2461     aio_rename "$meta~", $meta;
2462 root 1.110
2463     $exit->slaying ("?random/$md5");
2464     $exit->msg (undef);
2465     }
2466     }
2467    
2468     sub cf::object::player::enter_exit {
2469     my ($self, $exit) = @_;
2470    
2471     return unless $self->type == cf::PLAYER;
2472    
2473 root 1.195 if ($exit->slaying eq "/!") {
2474     #TODO: this should de-fi-ni-te-ly not be a sync-job
2475 root 1.233 # the problem is that $exit might not survive long enough
2476     # so it needs to be done right now, right here
2477 root 1.195 cf::sync_job { prepare_random_map $exit };
2478     }
2479    
2480     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2481     my $hp = $exit->stats->hp;
2482     my $sp = $exit->stats->sp;
2483    
2484 root 1.110 $self->enter_link;
2485    
2486 root 1.296 # if exit is damned, update players death & WoR home-position
2487     $self->contr->savebed ($slaying, $hp, $sp)
2488     if $exit->flag (FLAG_DAMNED);
2489    
2490 root 1.140 (async {
2491 root 1.133 $self->deactivate_recursive; # just to be sure
2492 root 1.110 unless (eval {
2493 root 1.195 $self->goto ($slaying, $hp, $sp);
2494 root 1.110
2495     1;
2496     }) {
2497     $self->message ("Something went wrong deep within the crossfire server. "
2498 root 1.233 . "I'll try to bring you back to the map you were before. "
2499     . "Please report this to the dungeon master!",
2500     cf::NDI_UNIQUE | cf::NDI_RED);
2501 root 1.110
2502     warn "ERROR in enter_exit: $@";
2503     $self->leave_link;
2504     }
2505     })->prio (1);
2506     }
2507    
2508 root 1.95 =head3 cf::client
2509    
2510     =over 4
2511    
2512     =item $client->send_drawinfo ($text, $flags)
2513    
2514     Sends a drawinfo packet to the client. Circumvents output buffering so
2515     should not be used under normal circumstances.
2516    
2517 root 1.70 =cut
2518    
2519 root 1.95 sub cf::client::send_drawinfo {
2520     my ($self, $text, $flags) = @_;
2521    
2522     utf8::encode $text;
2523 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2524 root 1.95 }
2525    
2526 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2527 root 1.283
2528     Send a drawinfo or msg packet to the client, formatting the msg for the
2529     client if neccessary. C<$type> should be a string identifying the type of
2530     the message, with C<log> being the default. If C<$color> is negative, suppress
2531     the message unless the client supports the msg packet.
2532    
2533     =cut
2534    
2535     sub cf::client::send_msg {
2536 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
2537 root 1.283
2538     $msg = $self->pl->expand_cfpod ($msg);
2539    
2540 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
2541 root 1.311
2542     if (ref $channel) {
2543     # send meta info to client, if not yet sent
2544     unless (exists $self->{channel}{$channel->{id}}) {
2545     $self->{channel}{$channel->{id}} = $channel;
2546 root 1.319 $self->ext_msg (channel_info => $channel);
2547 root 1.311 }
2548    
2549     $channel = $channel->{id};
2550     }
2551    
2552 root 1.313 return unless @extra || length $msg;
2553    
2554 root 1.283 if ($self->can_msg) {
2555 root 1.323 # default colour, mask it out
2556     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2557     if $color & cf::NDI_DEF;
2558    
2559     $self->send_packet ("msg " . $self->{json_coder}->encode (
2560     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]));
2561 root 1.283 } else {
2562 root 1.323 if ($color >= 0) {
2563     # replace some tags by gcfclient-compatible ones
2564     for ($msg) {
2565     1 while
2566     s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2567     || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2568     || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2569     || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2570     || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2571     }
2572    
2573     $color &= cf::NDI_COLOR_MASK;
2574 root 1.283
2575 root 1.327 utf8::encode $msg;
2576    
2577 root 1.284 if (0 && $msg =~ /\[/) {
2578 root 1.331 # COMMAND/INFO
2579     $self->send_packet ("drawextinfo $color 10 8 $msg")
2580 root 1.283 } else {
2581 root 1.286 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2582 root 1.283 $self->send_packet ("drawinfo $color $msg")
2583     }
2584     }
2585     }
2586     }
2587    
2588 root 1.316 =item $client->ext_msg ($type, @msg)
2589 root 1.232
2590 root 1.287 Sends an ext event to the client.
2591 root 1.232
2592     =cut
2593    
2594 root 1.316 sub cf::client::ext_msg($$@) {
2595     my ($self, $type, @msg) = @_;
2596 root 1.232
2597 root 1.316 my $extcmd = $self->extcmd;
2598 root 1.305
2599 root 1.316 if ($extcmd == 2) {
2600     $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2601     } elsif ($extcmd == 1) { # TODO: remove
2602     push @msg, msgtype => "event_$type";
2603     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2604     }
2605 root 1.232 }
2606 root 1.95
2607     =item $success = $client->query ($flags, "text", \&cb)
2608    
2609     Queues a query to the client, calling the given callback with
2610     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2611     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2612    
2613 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
2614     become reliable at some point in the future.
2615 root 1.95
2616     =cut
2617    
2618     sub cf::client::query {
2619     my ($self, $flags, $text, $cb) = @_;
2620    
2621     return unless $self->state == ST_PLAYING
2622     || $self->state == ST_SETUP
2623     || $self->state == ST_CUSTOM;
2624    
2625     $self->state (ST_CUSTOM);
2626    
2627     utf8::encode $text;
2628     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2629    
2630     $self->send_packet ($self->{query_queue}[0][0])
2631     if @{ $self->{query_queue} } == 1;
2632 root 1.287
2633     1
2634 root 1.95 }
2635    
2636     cf::client->attach (
2637 root 1.290 on_connect => sub {
2638     my ($ns) = @_;
2639    
2640     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2641     },
2642 root 1.95 on_reply => sub {
2643     my ($ns, $msg) = @_;
2644    
2645     # this weird shuffling is so that direct followup queries
2646     # get handled first
2647 root 1.128 my $queue = delete $ns->{query_queue}
2648 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2649 root 1.95
2650     (shift @$queue)->[1]->($msg);
2651 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2652 root 1.95
2653     push @{ $ns->{query_queue} }, @$queue;
2654    
2655     if (@{ $ns->{query_queue} } == @$queue) {
2656     if (@$queue) {
2657     $ns->send_packet ($ns->{query_queue}[0][0]);
2658     } else {
2659 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2660 root 1.95 }
2661     }
2662     },
2663 root 1.287 on_exticmd => sub {
2664     my ($ns, $buf) = @_;
2665    
2666 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2667 root 1.287
2668     if (ref $msg) {
2669 root 1.316 my ($type, $reply, @payload) =
2670     "ARRAY" eq ref $msg
2671     ? @$msg
2672     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2673    
2674     if (my $cb = $EXTICMD{$type}) {
2675     my @reply = $cb->($ns, @payload);
2676    
2677     $ns->ext_reply ($reply, @reply)
2678     if $reply;
2679 root 1.287 }
2680     } else {
2681     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2682     }
2683    
2684     cf::override;
2685     },
2686 root 1.95 );
2687    
2688 root 1.140 =item $client->async (\&cb)
2689 root 1.96
2690     Create a new coroutine, running the specified callback. The coroutine will
2691     be automatically cancelled when the client gets destroyed (e.g. on logout,
2692     or loss of connection).
2693    
2694     =cut
2695    
2696 root 1.140 sub cf::client::async {
2697 root 1.96 my ($self, $cb) = @_;
2698    
2699 root 1.140 my $coro = &Coro::async ($cb);
2700 root 1.103
2701     $coro->on_destroy (sub {
2702 root 1.96 delete $self->{_coro}{$coro+0};
2703 root 1.103 });
2704 root 1.96
2705     $self->{_coro}{$coro+0} = $coro;
2706 root 1.103
2707     $coro
2708 root 1.96 }
2709    
2710     cf::client->attach (
2711     on_destroy => sub {
2712     my ($ns) = @_;
2713    
2714 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2715 root 1.96 },
2716     );
2717    
2718 root 1.95 =back
2719    
2720 root 1.70
2721     =head2 SAFE SCRIPTING
2722    
2723     Functions that provide a safe environment to compile and execute
2724     snippets of perl code without them endangering the safety of the server
2725     itself. Looping constructs, I/O operators and other built-in functionality
2726     is not available in the safe scripting environment, and the number of
2727 root 1.79 functions and methods that can be called is greatly reduced.
2728 root 1.70
2729     =cut
2730 root 1.23
2731 root 1.42 our $safe = new Safe "safe";
2732 root 1.23 our $safe_hole = new Safe::Hole;
2733    
2734     $SIG{FPE} = 'IGNORE';
2735    
2736 root 1.328 $safe->permit_only (Opcode::opset qw(
2737     :base_core :base_mem :base_orig :base_math
2738     grepstart grepwhile mapstart mapwhile
2739     sort time
2740     ));
2741 root 1.23
2742 root 1.25 # here we export the classes and methods available to script code
2743    
2744 root 1.70 =pod
2745    
2746 root 1.228 The following functions and methods are available within a safe environment:
2747 root 1.70
2748 root 1.297 cf::object
2749     contr pay_amount pay_player map x y force_find force_add
2750 elmex 1.330 insert remove name archname title slaying race
2751 root 1.297
2752     cf::object::player
2753     player
2754    
2755     cf::player
2756     peaceful
2757    
2758     cf::map
2759     trigger
2760 root 1.70
2761     =cut
2762    
2763 root 1.25 for (
2764 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2765 elmex 1.330 insert remove inv name archname title slaying race)],
2766 root 1.25 ["cf::object::player" => qw(player)],
2767     ["cf::player" => qw(peaceful)],
2768 elmex 1.91 ["cf::map" => qw(trigger)],
2769 root 1.25 ) {
2770     no strict 'refs';
2771     my ($pkg, @funs) = @$_;
2772 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2773 root 1.25 for @funs;
2774     }
2775 root 1.23
2776 root 1.70 =over 4
2777    
2778     =item @retval = safe_eval $code, [var => value, ...]
2779    
2780     Compiled and executes the given perl code snippet. additional var/value
2781     pairs result in temporary local (my) scalar variables of the given name
2782     that are available in the code snippet. Example:
2783    
2784     my $five = safe_eval '$first + $second', first => 1, second => 4;
2785    
2786     =cut
2787    
2788 root 1.23 sub safe_eval($;@) {
2789     my ($code, %vars) = @_;
2790    
2791     my $qcode = $code;
2792     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2793     $qcode =~ s/\n/\\n/g;
2794    
2795     local $_;
2796 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2797 root 1.23
2798 root 1.42 my $eval =
2799 root 1.23 "do {\n"
2800     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2801     . "#line 0 \"{$qcode}\"\n"
2802     . $code
2803     . "\n}"
2804 root 1.25 ;
2805    
2806     sub_generation_inc;
2807 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2808 root 1.25 sub_generation_inc;
2809    
2810 root 1.42 if ($@) {
2811     warn "$@";
2812     warn "while executing safe code '$code'\n";
2813     warn "with arguments " . (join " ", %vars) . "\n";
2814     }
2815    
2816 root 1.25 wantarray ? @res : $res[0]
2817 root 1.23 }
2818    
2819 root 1.69 =item cf::register_script_function $function => $cb
2820    
2821     Register a function that can be called from within map/npc scripts. The
2822     function should be reasonably secure and should be put into a package name
2823     like the extension.
2824    
2825     Example: register a function that gets called whenever a map script calls
2826     C<rent::overview>, as used by the C<rent> extension.
2827    
2828     cf::register_script_function "rent::overview" => sub {
2829     ...
2830     };
2831    
2832     =cut
2833    
2834 root 1.23 sub register_script_function {
2835     my ($fun, $cb) = @_;
2836    
2837     no strict 'refs';
2838 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2839 root 1.23 }
2840    
2841 root 1.70 =back
2842    
2843 root 1.71 =cut
2844    
2845 root 1.23 #############################################################################
2846 root 1.203 # the server's init and main functions
2847    
2848 root 1.246 sub load_facedata($) {
2849     my ($path) = @_;
2850 root 1.223
2851 root 1.229 warn "loading facedata from $path\n";
2852 root 1.223
2853 root 1.236 my $facedata;
2854     0 < aio_load $path, $facedata
2855 root 1.223 or die "$path: $!";
2856    
2857 root 1.237 $facedata = Coro::Storable::thaw $facedata;
2858 root 1.223
2859 root 1.236 $facedata->{version} == 2
2860 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
2861    
2862 root 1.236 {
2863     my $faces = $facedata->{faceinfo};
2864    
2865     while (my ($face, $info) = each %$faces) {
2866     my $idx = (cf::face::find $face) || cf::face::alloc $face;
2867 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
2868     cf::face::set_magicmap $idx, $info->{magicmap};
2869 root 1.236 cf::face::set_data $idx, 0, $info->{data32}, $info->{chksum32};
2870     cf::face::set_data $idx, 1, $info->{data64}, $info->{chksum64};
2871 root 1.302
2872     cf::cede_to_tick;
2873 root 1.236 }
2874    
2875     while (my ($face, $info) = each %$faces) {
2876     next unless $info->{smooth};
2877     my $idx = cf::face::find $face
2878     or next;
2879     if (my $smooth = cf::face::find $info->{smooth}) {
2880 root 1.302 cf::face::set_smooth $idx, $smooth;
2881     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
2882 root 1.236 } else {
2883     warn "smooth face '$info->{smooth}' not found for face '$face'";
2884     }
2885 root 1.302
2886     cf::cede_to_tick;
2887 root 1.236 }
2888 root 1.223 }
2889    
2890 root 1.236 {
2891     my $anims = $facedata->{animinfo};
2892    
2893     while (my ($anim, $info) = each %$anims) {
2894     cf::anim::set $anim, $info->{frames}, $info->{facings};
2895 root 1.302 cf::cede_to_tick;
2896 root 1.225 }
2897 root 1.236
2898     cf::anim::invalidate_all; # d'oh
2899 root 1.225 }
2900    
2901 root 1.302 {
2902     # TODO: for gcfclient pleasure, we should give resources
2903     # that gcfclient doesn't grok a >10000 face index.
2904     my $res = $facedata->{resource};
2905     my $enc = JSON::XS->new->utf8->canonical;
2906    
2907 root 1.321 my $soundconf = delete $res->{"res/sound.conf"};
2908 root 1.320
2909 root 1.302 while (my ($name, $info) = each %$res) {
2910     my $meta = $enc->encode ({
2911 root 1.320 name => $name,
2912     %{ $info->{meta} || {} },
2913 root 1.302 });
2914    
2915     my $idx = (cf::face::find $name) || cf::face::alloc $name;
2916 root 1.307
2917 root 1.318 if ($info->{type} & 1) {
2918     # prepend meta info
2919    
2920 root 1.307 my $data = pack "(w/a*)*", $meta, $info->{data};
2921     my $chk = Digest::MD5::md5 "$info->{chksum},$meta"; # mangle data checksum and metadata
2922    
2923     cf::face::set_data $idx, 0, $data, $chk;
2924 root 1.318 } else {
2925     cf::face::set_data $idx, 0, $info->{data}, $info->{chksum};
2926 root 1.307 }
2927 root 1.302
2928 root 1.318 cf::face::set_type $idx, $info->{type};
2929    
2930 root 1.302 cf::cede_to_tick;
2931     }
2932 root 1.321
2933     if ($soundconf) {
2934     $soundconf = $enc->decode (delete $soundconf->{data});
2935    
2936     for (0 .. SOUND_CAST_SPELL_0 - 1) {
2937     my $sound = $soundconf->{compat}[$_]
2938     or next;
2939    
2940     my $face = cf::face::find "sound/$sound->[1]";
2941     cf::sound::set $sound->[0] => $face;
2942     cf::sound::old_sound_index $_, $face; # gcfclient-compat
2943     }
2944    
2945 root 1.326 while (my ($k, $v) = each %{$soundconf->{event}}) {
2946     my $face = cf::face::find "sound/$v";
2947     cf::sound::set $k => $face;
2948     }
2949 root 1.321 }
2950 root 1.302 }
2951    
2952 root 1.223 1
2953     }
2954    
2955 root 1.318 register_exticmd fx_want => sub {
2956     my ($ns, $want) = @_;
2957    
2958     while (my ($k, $v) = each %$want) {
2959     $ns->fx_want ($k, $v);
2960     }
2961     };
2962    
2963 root 1.253 sub reload_regions {
2964     load_resource_file "$MAPDIR/regions"
2965     or die "unable to load regions file\n";
2966 root 1.304
2967     for (cf::region::list) {
2968     $_->{match} = qr/$_->{match}/
2969     if exists $_->{match};
2970     }
2971 root 1.253 }
2972    
2973 root 1.246 sub reload_facedata {
2974 root 1.253 load_facedata "$DATADIR/facedata"
2975 root 1.246 or die "unable to load facedata\n";
2976     }
2977    
2978     sub reload_archetypes {
2979 root 1.253 load_resource_file "$DATADIR/archetypes"
2980 root 1.246 or die "unable to load archetypes\n";
2981 root 1.289 #d# NEED to laod twice to resolve forward references
2982     # this really needs to be done in an extra post-pass
2983     # (which needs to be synchronous, so solve it differently)
2984     load_resource_file "$DATADIR/archetypes"
2985     or die "unable to load archetypes\n";
2986 root 1.241 }
2987    
2988 root 1.246 sub reload_treasures {
2989 root 1.253 load_resource_file "$DATADIR/treasures"
2990 root 1.246 or die "unable to load treasurelists\n";
2991 root 1.241 }
2992    
2993 root 1.223 sub reload_resources {
2994 root 1.245 warn "reloading resource files...\n";
2995    
2996 root 1.246 reload_regions;
2997     reload_facedata;
2998 root 1.274 #reload_archetypes;#d#
2999 root 1.246 reload_archetypes;
3000     reload_treasures;
3001 root 1.245
3002     warn "finished reloading resource files\n";
3003 root 1.223 }
3004    
3005     sub init {
3006     reload_resources;
3007 root 1.203 }
3008 root 1.34
3009 root 1.73 sub cfg_load {
3010 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3011 root 1.72 or return;
3012    
3013     local $/;
3014     *CFG = YAML::Syck::Load <$fh>;
3015 root 1.131
3016     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
3017    
3018 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3019     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3020    
3021 root 1.131 if (exists $CFG{mlockall}) {
3022     eval {
3023 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3024 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3025     };
3026     warn $@ if $@;
3027     }
3028 root 1.72 }
3029    
3030 root 1.39 sub main {
3031 root 1.108 # we must not ever block the main coroutine
3032     local $Coro::idle = sub {
3033 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3034 root 1.175 (async {
3035     Event::one_event;
3036     })->prio (Coro::PRIO_MAX);
3037 root 1.108 };
3038    
3039 root 1.73 cfg_load;
3040 root 1.210 db_init;
3041 root 1.61 load_extensions;
3042 root 1.183
3043     $TICK_WATCHER->start;
3044 root 1.34 Event::loop;
3045     }
3046    
3047     #############################################################################
3048 root 1.155 # initialisation and cleanup
3049    
3050     # install some emergency cleanup handlers
3051     BEGIN {
3052     for my $signal (qw(INT HUP TERM)) {
3053     Event->signal (
3054 root 1.189 reentrant => 0,
3055     data => WF_AUTOCANCEL,
3056     signal => $signal,
3057 root 1.191 prio => 0,
3058 root 1.189 cb => sub {
3059 root 1.155 cf::cleanup "SIG$signal";
3060     },
3061     );
3062     }
3063     }
3064    
3065 root 1.281 sub write_runtime {
3066     my $runtime = "$LOCALDIR/runtime";
3067    
3068     # first touch the runtime file to show we are still running:
3069     # the fsync below can take a very very long time.
3070    
3071     IO::AIO::aio_utime $runtime, undef, undef;
3072    
3073     my $guard = cf::lock_acquire "write_runtime";
3074    
3075     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
3076     or return;
3077    
3078     my $value = $cf::RUNTIME + 90 + 10;
3079     # 10 is the runtime save interval, for a monotonic clock
3080     # 60 allows for the watchdog to kill the server.
3081    
3082     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3083     and return;
3084    
3085     # always fsync - this file is important
3086     aio_fsync $fh
3087     and return;
3088    
3089     # touch it again to show we are up-to-date
3090     aio_utime $fh, undef, undef;
3091    
3092     close $fh
3093     or return;
3094    
3095     aio_rename "$runtime~", $runtime
3096     and return;
3097    
3098     warn "runtime file written.\n";
3099    
3100     1
3101     }
3102    
3103 root 1.156 sub emergency_save() {
3104 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3105    
3106     warn "enter emergency perl save\n";
3107    
3108     cf::sync_job {
3109     # use a peculiar iteration method to avoid tripping on perl
3110     # refcount bugs in for. also avoids problems with players
3111 root 1.167 # and maps saved/destroyed asynchronously.
3112 root 1.155 warn "begin emergency player save\n";
3113     for my $login (keys %cf::PLAYER) {
3114     my $pl = $cf::PLAYER{$login} or next;
3115     $pl->valid or next;
3116     $pl->save;
3117     }
3118     warn "end emergency player save\n";
3119    
3120     warn "begin emergency map save\n";
3121     for my $path (keys %cf::MAP) {
3122     my $map = $cf::MAP{$path} or next;
3123     $map->valid or next;
3124     $map->save;
3125     }
3126     warn "end emergency map save\n";
3127 root 1.208
3128     warn "begin emergency database checkpoint\n";
3129     BDB::db_env_txn_checkpoint $DB_ENV;
3130     warn "end emergency database checkpoint\n";
3131 root 1.155 };
3132    
3133     warn "leave emergency perl save\n";
3134     }
3135 root 1.22
3136 root 1.211 sub post_cleanup {
3137     my ($make_core) = @_;
3138    
3139     warn Carp::longmess "post_cleanup backtrace"
3140     if $make_core;
3141     }
3142    
3143 root 1.246 sub do_reload_perl() {
3144 root 1.106 # can/must only be called in main
3145     if ($Coro::current != $Coro::main) {
3146 root 1.183 warn "can only reload from main coroutine";
3147 root 1.106 return;
3148     }
3149    
3150 root 1.103 warn "reloading...";
3151    
3152 root 1.212 warn "entering sync_job";
3153    
3154 root 1.213 cf::sync_job {
3155 root 1.214 cf::write_runtime; # external watchdog should not bark
3156 root 1.212 cf::emergency_save;
3157 root 1.214 cf::write_runtime; # external watchdog should not bark
3158 root 1.183
3159 root 1.212 warn "syncing database to disk";
3160     BDB::db_env_txn_checkpoint $DB_ENV;
3161 root 1.106
3162     # if anything goes wrong in here, we should simply crash as we already saved
3163 root 1.65
3164 root 1.183 warn "cancelling all WF_AUTOCANCEL watchers";
3165 root 1.87 for (Event::all_watchers) {
3166     $_->cancel if $_->data & WF_AUTOCANCEL;
3167     }
3168 root 1.65
3169 root 1.183 warn "flushing outstanding aio requests";
3170     for (;;) {
3171 root 1.208 BDB::flush;
3172 root 1.183 IO::AIO::flush;
3173     Coro::cede;
3174 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
3175 root 1.183 warn "iterate...";
3176     }
3177    
3178 root 1.223 ++$RELOAD;
3179    
3180 root 1.183 warn "cancelling all extension coros";
3181 root 1.103 $_->cancel for values %EXT_CORO;
3182     %EXT_CORO = ();
3183    
3184 root 1.183 warn "removing commands";
3185 root 1.159 %COMMAND = ();
3186    
3187 root 1.287 warn "removing ext/exti commands";
3188     %EXTCMD = ();
3189     %EXTICMD = ();
3190 root 1.159
3191 root 1.183 warn "unloading/nuking all extensions";
3192 root 1.159 for my $pkg (@EXTS) {
3193 root 1.160 warn "... unloading $pkg";
3194 root 1.159
3195     if (my $cb = $pkg->can ("unload")) {
3196     eval {
3197     $cb->($pkg);
3198     1
3199     } or warn "$pkg unloaded, but with errors: $@";
3200     }
3201    
3202 root 1.160 warn "... nuking $pkg";
3203 root 1.159 Symbol::delete_package $pkg;
3204 root 1.65 }
3205    
3206 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
3207 root 1.65 while (my ($k, $v) = each %INC) {
3208     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3209    
3210 root 1.183 warn "... unloading $k";
3211 root 1.65 delete $INC{$k};
3212    
3213     $k =~ s/\.pm$//;
3214     $k =~ s/\//::/g;
3215    
3216     if (my $cb = $k->can ("unload_module")) {
3217     $cb->();
3218     }
3219    
3220     Symbol::delete_package $k;
3221     }
3222    
3223 root 1.183 warn "getting rid of safe::, as good as possible";
3224 root 1.65 Symbol::delete_package "safe::$_"
3225 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3226 root 1.65
3227 root 1.183 warn "unloading cf.pm \"a bit\"";
3228 root 1.65 delete $INC{"cf.pm"};
3229 root 1.252 delete $INC{"cf/pod.pm"};
3230 root 1.65
3231     # don't, removes xs symbols, too,
3232     # and global variables created in xs
3233     #Symbol::delete_package __PACKAGE__;
3234    
3235 root 1.183 warn "unload completed, starting to reload now";
3236    
3237 root 1.103 warn "reloading cf.pm";
3238 root 1.65 require cf;
3239 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3240    
3241 root 1.183 warn "loading config and database again";
3242 root 1.73 cf::cfg_load;
3243 root 1.65
3244 root 1.183 warn "loading extensions";
3245 root 1.65 cf::load_extensions;
3246    
3247 root 1.183 warn "reattaching attachments to objects/players";
3248 root 1.222 _global_reattach; # objects, sockets
3249 root 1.183 warn "reattaching attachments to maps";
3250 root 1.144 reattach $_ for values %MAP;
3251 root 1.222 warn "reattaching attachments to players";
3252     reattach $_ for values %PLAYER;
3253 root 1.183
3254 root 1.212 warn "leaving sync_job";
3255 root 1.183
3256 root 1.212 1
3257     } or do {
3258 root 1.106 warn $@;
3259     warn "error while reloading, exiting.";
3260     exit 1;
3261 root 1.212 };
3262 root 1.106
3263 root 1.159 warn "reloaded";
3264 root 1.65 };
3265    
3266 root 1.175 our $RELOAD_WATCHER; # used only during reload
3267    
3268 root 1.246 sub reload_perl() {
3269     # doing reload synchronously and two reloads happen back-to-back,
3270     # coro crashes during coro_state_free->destroy here.
3271    
3272     $RELOAD_WATCHER ||= Event->timer (
3273     reentrant => 0,
3274     after => 0,
3275     data => WF_AUTOCANCEL,
3276     cb => sub {
3277     do_reload_perl;
3278     undef $RELOAD_WATCHER;
3279     },
3280     );
3281     }
3282    
3283 root 1.111 register_command "reload" => sub {
3284 root 1.65 my ($who, $arg) = @_;
3285    
3286     if ($who->flag (FLAG_WIZ)) {
3287 root 1.175 $who->message ("reloading server.");
3288 root 1.246 async { reload_perl };
3289 root 1.65 }
3290     };
3291    
3292 root 1.27 unshift @INC, $LIBDIR;
3293 root 1.17
3294 root 1.183 my $bug_warning = 0;
3295    
3296 root 1.239 our @WAIT_FOR_TICK;
3297     our @WAIT_FOR_TICK_BEGIN;
3298    
3299     sub wait_for_tick {
3300 root 1.240 return unless $TICK_WATCHER->is_active;
3301 root 1.241 return if $Coro::current == $Coro::main;
3302    
3303 root 1.239 my $signal = new Coro::Signal;
3304     push @WAIT_FOR_TICK, $signal;
3305     $signal->wait;
3306     }
3307    
3308     sub wait_for_tick_begin {
3309 root 1.240 return unless $TICK_WATCHER->is_active;
3310 root 1.241 return if $Coro::current == $Coro::main;
3311    
3312 root 1.239 my $signal = new Coro::Signal;
3313     push @WAIT_FOR_TICK_BEGIN, $signal;
3314     $signal->wait;
3315     }
3316    
3317 root 1.268 my $min = 1e6;#d#
3318     my $avg = 10;
3319 root 1.35 $TICK_WATCHER = Event->timer (
3320 root 1.104 reentrant => 0,
3321 root 1.183 parked => 1,
3322 root 1.191 prio => 0,
3323 root 1.104 at => $NEXT_TICK || $TICK,
3324     data => WF_AUTOCANCEL,
3325     cb => sub {
3326 root 1.183 if ($Coro::current != $Coro::main) {
3327     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3328     unless ++$bug_warning > 10;
3329     return;
3330     }
3331    
3332 root 1.265 $NOW = $tick_start = Event::time;
3333 root 1.163
3334 root 1.133 cf::server_tick; # one server iteration
3335 root 1.245
3336 root 1.268 0 && sync_job {#d#
3337     for(1..10) {
3338     my $t = Event::time;
3339     my $map = my $map = new_from_path cf::map "/tmp/x.map"
3340     or die;
3341    
3342     $map->width (50);
3343     $map->height (50);
3344     $map->alloc;
3345     $map->_load_objects ("/tmp/x.map", 1);
3346     my $t = Event::time - $t;
3347    
3348     #next unless $t < 0.0013;#d#
3349     if ($t < $min) {
3350     $min = $t;
3351     }
3352     $avg = $avg * 0.99 + $t * 0.01;
3353     }
3354     warn "XXXXXXXXXXXXXXXXXX min $min avg $avg\n";#d#
3355     exit 0;
3356     # 2007-05-22 02:33:04.569 min 0.00112509727478027 avg 0.0012259249572477
3357     };
3358    
3359 root 1.133 $RUNTIME += $TICK;
3360 root 1.35 $NEXT_TICK += $TICK;
3361    
3362 root 1.214 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3363     $NEXT_RUNTIME_WRITE = $NOW + 10;
3364     Coro::async_pool {
3365     write_runtime
3366     or warn "ERROR: unable to write runtime file: $!";
3367     };
3368     }
3369    
3370 root 1.191 # my $AFTER = Event::time;
3371     # warn $AFTER - $NOW;#d#
3372 root 1.190
3373 root 1.245 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3374     $sig->send;
3375     }
3376     while (my $sig = shift @WAIT_FOR_TICK) {
3377     $sig->send;
3378     }
3379    
3380 root 1.265 $NOW = Event::time;
3381    
3382     # if we are delayed by four ticks or more, skip them all
3383     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
3384    
3385     $TICK_WATCHER->at ($NEXT_TICK);
3386     $TICK_WATCHER->start;
3387    
3388     $LOAD = ($NOW - $tick_start) / $TICK;
3389     $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3390    
3391 root 1.245 _post_tick;
3392 root 1.265
3393    
3394 root 1.35 },
3395     );
3396    
3397 root 1.206 {
3398     BDB::max_poll_time $TICK * 0.1;
3399     $BDB_POLL_WATCHER = Event->io (
3400     reentrant => 0,
3401     fd => BDB::poll_fileno,
3402     poll => 'r',
3403     prio => 0,
3404     data => WF_AUTOCANCEL,
3405     cb => \&BDB::poll_cb,
3406     );
3407     BDB::min_parallel 8;
3408    
3409     BDB::set_sync_prepare {
3410     my $status;
3411     my $current = $Coro::current;
3412     (
3413     sub {
3414     $status = $!;
3415     $current->ready; undef $current;
3416     },
3417     sub {
3418     Coro::schedule while defined $current;
3419     $! = $status;
3420     },
3421     )
3422     };
3423 root 1.77
3424 root 1.206 unless ($DB_ENV) {
3425     $DB_ENV = BDB::db_env_create;
3426    
3427     cf::sync_job {
3428 root 1.208 eval {
3429     BDB::db_env_open
3430     $DB_ENV,
3431 root 1.253 $BDBDIR,
3432 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3433     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3434     0666;
3435    
3436 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3437 root 1.208
3438     $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC, 1);
3439     $DB_ENV->set_lk_detect;
3440     };
3441    
3442     cf::cleanup "db_env_open(db): $@" if $@;
3443 root 1.206 };
3444     }
3445     }
3446    
3447     {
3448     IO::AIO::min_parallel 8;
3449    
3450     undef $Coro::AIO::WATCHER;
3451     IO::AIO::max_poll_time $TICK * 0.1;
3452     $AIO_POLL_WATCHER = Event->io (
3453     reentrant => 0,
3454 root 1.214 data => WF_AUTOCANCEL,
3455 root 1.206 fd => IO::AIO::poll_fileno,
3456     poll => 'r',
3457     prio => 6,
3458     cb => \&IO::AIO::poll_cb,
3459     );
3460     }
3461 root 1.108
3462 root 1.262 my $_log_backtrace;
3463    
3464 root 1.260 sub _log_backtrace {
3465     my ($msg, @addr) = @_;
3466    
3467 root 1.262 $msg =~ s/\n//;
3468 root 1.260
3469 root 1.262 # limit the # of concurrent backtraces
3470     if ($_log_backtrace < 2) {
3471     ++$_log_backtrace;
3472     async {
3473     my @bt = fork_call {
3474     @addr = map { sprintf "%x", $_ } @addr;
3475     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3476     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3477     or die "addr2line: $!";
3478    
3479     my @funcs;
3480     my @res = <$fh>;
3481     chomp for @res;
3482     while (@res) {
3483     my ($func, $line) = splice @res, 0, 2, ();
3484     push @funcs, "[$func] $line";
3485     }
3486 root 1.260
3487 root 1.262 @funcs
3488     };
3489 root 1.260
3490 root 1.262 LOG llevInfo, "[ABT] $msg\n";
3491     LOG llevInfo, "[ABT] $_\n" for @bt;
3492     --$_log_backtrace;
3493     };
3494     } else {
3495 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3496 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3497     }
3498 root 1.260 }
3499    
3500 root 1.249 # load additional modules
3501     use cf::pod;
3502    
3503 root 1.125 END { cf::emergency_save }
3504    
3505 root 1.1 1
3506