ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.300
Committed: Tue Jul 10 06:44:29 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.299: +1 -0 lines
Log Message:
aggressively add hints to apply.C

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