ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.301
Committed: Tue Jul 10 07:01:51 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.300: +0 -1 lines
Log Message:
doh

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.283 $_
1441     }
1442    
1443 root 1.291 sub hintmode {
1444     $_[0]{hintmode} = $_[1] if @_ > 1;
1445     $_[0]{hintmode}
1446     }
1447    
1448 root 1.231 =item $player->ext_reply ($msgid, %msg)
1449 root 1.95
1450     Sends an ext reply to the player.
1451    
1452     =cut
1453    
1454 root 1.231 sub ext_reply($$%) {
1455 root 1.95 my ($self, $id, %msg) = @_;
1456    
1457     $msg{msgid} = $id;
1458 root 1.290 $self->send ("ext " . $self->ns->{json_coder}->encode (\%msg));
1459 root 1.95 }
1460    
1461 root 1.231 =item $player->ext_event ($type, %msg)
1462    
1463     Sends an ext event to the client.
1464    
1465     =cut
1466    
1467     sub ext_event($$%) {
1468     my ($self, $type, %msg) = @_;
1469    
1470 root 1.232 $self->ns->ext_event ($type, %msg);
1471 root 1.231 }
1472    
1473 root 1.238 =head3 cf::region
1474    
1475     =over 4
1476    
1477     =cut
1478    
1479     package cf::region;
1480    
1481     =item cf::region::find_by_path $path
1482    
1483 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1484 root 1.238
1485     =cut
1486    
1487     sub find_by_path($) {
1488     my ($path) = @_;
1489    
1490     my ($match, $specificity);
1491    
1492     for my $region (list) {
1493     if ($region->match && $path =~ $region->match) {
1494     ($match, $specificity) = ($region, $region->specificity)
1495     if $region->specificity > $specificity;
1496     }
1497     }
1498    
1499     $match
1500     }
1501 root 1.143
1502 root 1.95 =back
1503    
1504 root 1.110 =head3 cf::map
1505    
1506     =over 4
1507    
1508     =cut
1509    
1510     package cf::map;
1511    
1512     use Fcntl;
1513     use Coro::AIO;
1514    
1515 root 1.166 use overload
1516 root 1.173 '""' => \&as_string,
1517     fallback => 1;
1518 root 1.166
1519 root 1.133 our $MAX_RESET = 3600;
1520     our $DEFAULT_RESET = 3000;
1521 root 1.110
1522     sub generate_random_map {
1523 root 1.166 my ($self, $rmp) = @_;
1524 root 1.110 # mit "rum" bekleckern, nicht
1525 root 1.166 $self->_create_random_map (
1526 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1527     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1528     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1529     $rmp->{exit_on_final_map},
1530     $rmp->{xsize}, $rmp->{ysize},
1531     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1532     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1533     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1534     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1535     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1536 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1537     )
1538 root 1.110 }
1539    
1540 root 1.187 =item cf::map->register ($regex, $prio)
1541    
1542     Register a handler for the map path matching the given regex at the
1543     givne priority (higher is better, built-in handlers have priority 0, the
1544     default).
1545    
1546     =cut
1547    
1548 root 1.166 sub register {
1549 root 1.187 my (undef, $regex, $prio) = @_;
1550 root 1.166 my $pkg = caller;
1551    
1552     no strict;
1553     push @{"$pkg\::ISA"}, __PACKAGE__;
1554    
1555 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1556 root 1.166 }
1557    
1558     # also paths starting with '/'
1559 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1560 root 1.166
1561 root 1.170 sub thawer_merge {
1562 root 1.172 my ($self, $merge) = @_;
1563    
1564 root 1.170 # we have to keep some variables in memory intact
1565 root 1.172 local $self->{path};
1566     local $self->{load_path};
1567 root 1.170
1568 root 1.172 $self->SUPER::thawer_merge ($merge);
1569 root 1.170 }
1570    
1571 root 1.166 sub normalise {
1572     my ($path, $base) = @_;
1573    
1574 root 1.192 $path = "$path"; # make sure its a string
1575    
1576 root 1.199 $path =~ s/\.map$//;
1577    
1578 root 1.166 # map plan:
1579     #
1580     # /! non-realised random map exit (special hack!)
1581     # {... are special paths that are not being touched
1582     # ?xxx/... are special absolute paths
1583     # ?random/... random maps
1584     # /... normal maps
1585     # ~user/... per-player map of a specific user
1586    
1587     $path =~ s/$PATH_SEP/\//go;
1588    
1589     # treat it as relative path if it starts with
1590     # something that looks reasonable
1591     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1592     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1593    
1594     $base =~ s{[^/]+/?$}{};
1595     $path = "$base/$path";
1596     }
1597    
1598     for ($path) {
1599     redo if s{//}{/};
1600     redo if s{/\.?/}{/};
1601     redo if s{/[^/]+/\.\./}{/};
1602     }
1603    
1604     $path
1605     }
1606    
1607     sub new_from_path {
1608     my (undef, $path, $base) = @_;
1609    
1610     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1611    
1612     $path = normalise $path, $base;
1613    
1614 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1615     if ($path =~ $EXT_MAP{$pkg}[1]) {
1616 root 1.166 my $self = bless cf::map::new, $pkg;
1617     $self->{path} = $path; $self->path ($path);
1618     $self->init; # pass $1 etc.
1619     return $self;
1620     }
1621     }
1622    
1623 root 1.192 Carp::carp "unable to resolve path '$path' (base '$base').";
1624 root 1.166 ()
1625     }
1626    
1627     sub init {
1628     my ($self) = @_;
1629    
1630     $self
1631     }
1632    
1633     sub as_string {
1634     my ($self) = @_;
1635    
1636     "$self->{path}"
1637     }
1638    
1639     # the displayed name, this is a one way mapping
1640     sub visible_name {
1641     &as_string
1642     }
1643    
1644     # the original (read-only) location
1645     sub load_path {
1646     my ($self) = @_;
1647    
1648 root 1.254 "$MAPDIR/$self->{path}.map"
1649 root 1.166 }
1650    
1651     # the temporary/swap location
1652     sub save_path {
1653     my ($self) = @_;
1654    
1655 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1656 root 1.254 "$TMPDIR/$path.map"
1657 root 1.166 }
1658    
1659     # the unique path, undef == no special unique path
1660     sub uniq_path {
1661     my ($self) = @_;
1662    
1663 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1664 root 1.253 "$UNIQUEDIR/$path"
1665 root 1.166 }
1666    
1667 root 1.110 # and all this just because we cannot iterate over
1668     # all maps in C++...
1669     sub change_all_map_light {
1670     my ($change) = @_;
1671    
1672 root 1.122 $_->change_map_light ($change)
1673     for grep $_->outdoor, values %cf::MAP;
1674 root 1.110 }
1675    
1676 root 1.275 sub decay_objects {
1677     my ($self) = @_;
1678    
1679     return if $self->{deny_reset};
1680    
1681     $self->do_decay_objects;
1682     }
1683    
1684 root 1.166 sub unlink_save {
1685     my ($self) = @_;
1686    
1687     utf8::encode (my $save = $self->save_path);
1688 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1689     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1690 root 1.166 }
1691    
1692     sub load_header_from($) {
1693     my ($self, $path) = @_;
1694 root 1.110
1695     utf8::encode $path;
1696 root 1.200 #aio_open $path, O_RDONLY, 0
1697     # or return;
1698 root 1.110
1699 root 1.166 $self->_load_header ($path)
1700 root 1.110 or return;
1701    
1702 root 1.166 $self->{load_path} = $path;
1703 root 1.135
1704 root 1.166 1
1705     }
1706 root 1.110
1707 root 1.188 sub load_header_orig {
1708 root 1.166 my ($self) = @_;
1709 root 1.110
1710 root 1.166 $self->load_header_from ($self->load_path)
1711 root 1.110 }
1712    
1713 root 1.188 sub load_header_temp {
1714 root 1.166 my ($self) = @_;
1715 root 1.110
1716 root 1.166 $self->load_header_from ($self->save_path)
1717     }
1718 root 1.110
1719 root 1.188 sub prepare_temp {
1720     my ($self) = @_;
1721    
1722     $self->last_access ((delete $self->{last_access})
1723     || $cf::RUNTIME); #d#
1724     # safety
1725     $self->{instantiate_time} = $cf::RUNTIME
1726     if $self->{instantiate_time} > $cf::RUNTIME;
1727     }
1728    
1729     sub prepare_orig {
1730     my ($self) = @_;
1731    
1732     $self->{load_original} = 1;
1733     $self->{instantiate_time} = $cf::RUNTIME;
1734     $self->last_access ($cf::RUNTIME);
1735     $self->instantiate;
1736     }
1737    
1738 root 1.166 sub load_header {
1739     my ($self) = @_;
1740 root 1.110
1741 root 1.188 if ($self->load_header_temp) {
1742     $self->prepare_temp;
1743 root 1.166 } else {
1744 root 1.188 $self->load_header_orig
1745 root 1.166 or return;
1746 root 1.188 $self->prepare_orig;
1747 root 1.166 }
1748 root 1.120
1749 root 1.275 $self->{deny_reset} = 1
1750     if $self->no_reset;
1751    
1752 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1753     unless $self->default_region;
1754    
1755 root 1.166 1
1756     }
1757 root 1.110
1758 root 1.166 sub find;
1759     sub find {
1760     my ($path, $origin) = @_;
1761 root 1.134
1762 root 1.166 $path = normalise $path, $origin && $origin->path;
1763 root 1.110
1764 root 1.166 cf::lock_wait "map_find:$path";
1765 root 1.110
1766 root 1.166 $cf::MAP{$path} || do {
1767     my $guard = cf::lock_acquire "map_find:$path";
1768     my $map = new_from_path cf::map $path
1769     or return;
1770 root 1.110
1771 root 1.116 $map->{last_save} = $cf::RUNTIME;
1772 root 1.110
1773 root 1.166 $map->load_header
1774     or return;
1775 root 1.134
1776 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
1777 root 1.185 # doing this can freeze the server in a sync job, obviously
1778     #$cf::WAIT_FOR_TICK->wait;
1779 root 1.112 $map->reset;
1780 root 1.123 undef $guard;
1781 root 1.192 return find $path;
1782 root 1.112 }
1783 root 1.110
1784 root 1.166 $cf::MAP{$path} = $map
1785 root 1.110 }
1786     }
1787    
1788 root 1.188 sub pre_load { }
1789     sub post_load { }
1790    
1791 root 1.110 sub load {
1792     my ($self) = @_;
1793    
1794 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
1795    
1796 root 1.120 my $path = $self->{path};
1797    
1798 root 1.256 {
1799     my $guard = cf::lock_acquire "map_load:$path";
1800    
1801     return if $self->in_memory != cf::MAP_SWAPPED;
1802 root 1.110
1803 root 1.256 $self->in_memory (cf::MAP_LOADING);
1804 root 1.110
1805 root 1.256 $self->alloc;
1806 root 1.188
1807 root 1.256 $self->pre_load;
1808     Coro::cede;
1809 root 1.188
1810 root 1.256 $self->_load_objects ($self->{load_path}, 1)
1811     or return;
1812 root 1.110
1813 root 1.256 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1814     if delete $self->{load_original};
1815 root 1.111
1816 root 1.256 if (my $uniq = $self->uniq_path) {
1817     utf8::encode $uniq;
1818     if (aio_open $uniq, O_RDONLY, 0) {
1819     $self->clear_unique_items;
1820     $self->_load_objects ($uniq, 0);
1821     }
1822 root 1.110 }
1823    
1824 root 1.166 Coro::cede;
1825 root 1.256 # now do the right thing for maps
1826     $self->link_multipart_objects;
1827 root 1.110 $self->difficulty ($self->estimate_difficulty)
1828     unless $self->difficulty;
1829 root 1.166 Coro::cede;
1830 root 1.256
1831     unless ($self->{deny_activate}) {
1832     $self->decay_objects;
1833     $self->fix_auto_apply;
1834     $self->update_buttons;
1835     Coro::cede;
1836     $self->set_darkness_map;
1837     Coro::cede;
1838     $self->activate;
1839     }
1840    
1841     $self->in_memory (cf::MAP_IN_MEMORY);
1842 root 1.110 }
1843    
1844 root 1.188 $self->post_load;
1845 root 1.166 }
1846    
1847     sub customise_for {
1848     my ($self, $ob) = @_;
1849    
1850     return find "~" . $ob->name . "/" . $self->{path}
1851     if $self->per_player;
1852 root 1.134
1853 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
1854     # if $self->per_party;
1855    
1856 root 1.166 $self
1857 root 1.110 }
1858    
1859 root 1.157 # find and load all maps in the 3x3 area around a map
1860     sub load_diag {
1861     my ($map) = @_;
1862    
1863     my @diag; # diagonal neighbours
1864    
1865     for (0 .. 3) {
1866     my $neigh = $map->tile_path ($_)
1867     or next;
1868     $neigh = find $neigh, $map
1869     or next;
1870     $neigh->load;
1871    
1872     push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh],
1873     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1874     }
1875    
1876     for (@diag) {
1877     my $neigh = find @$_
1878     or next;
1879     $neigh->load;
1880     }
1881     }
1882    
1883 root 1.133 sub find_sync {
1884 root 1.110 my ($path, $origin) = @_;
1885    
1886 root 1.157 cf::sync_job { find $path, $origin }
1887 root 1.133 }
1888    
1889     sub do_load_sync {
1890     my ($map) = @_;
1891 root 1.110
1892 root 1.133 cf::sync_job { $map->load };
1893 root 1.110 }
1894    
1895 root 1.157 our %MAP_PREFETCH;
1896 root 1.183 our $MAP_PREFETCHER = undef;
1897 root 1.157
1898     sub find_async {
1899     my ($path, $origin) = @_;
1900    
1901 root 1.166 $path = normalise $path, $origin && $origin->{path};
1902 root 1.157
1903 root 1.166 if (my $map = $cf::MAP{$path}) {
1904 root 1.157 return $map if $map->in_memory == cf::MAP_IN_MEMORY;
1905     }
1906    
1907 root 1.183 undef $MAP_PREFETCH{$path};
1908     $MAP_PREFETCHER ||= cf::async {
1909     while (%MAP_PREFETCH) {
1910     for my $path (keys %MAP_PREFETCH) {
1911     my $map = find $path
1912     or next;
1913     $map->load;
1914    
1915     delete $MAP_PREFETCH{$path};
1916     }
1917     }
1918     undef $MAP_PREFETCHER;
1919     };
1920 root 1.189 $MAP_PREFETCHER->prio (6);
1921 root 1.157
1922     ()
1923     }
1924    
1925 root 1.110 sub save {
1926     my ($self) = @_;
1927    
1928 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1929    
1930 root 1.110 $self->{last_save} = $cf::RUNTIME;
1931    
1932     return unless $self->dirty;
1933    
1934 root 1.166 my $save = $self->save_path; utf8::encode $save;
1935     my $uniq = $self->uniq_path; utf8::encode $uniq;
1936 root 1.117
1937 root 1.110 $self->{load_path} = $save;
1938    
1939     return if $self->{deny_save};
1940    
1941 root 1.132 local $self->{last_access} = $self->last_access;#d#
1942    
1943 root 1.143 cf::async {
1944     $_->contr->save for $self->players;
1945     };
1946    
1947 root 1.110 if ($uniq) {
1948 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1949     $self->_save_objects ($uniq, cf::IO_UNIQUES);
1950 root 1.110 } else {
1951 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1952 root 1.110 }
1953     }
1954    
1955     sub swap_out {
1956     my ($self) = @_;
1957    
1958 root 1.130 # save first because save cedes
1959     $self->save;
1960    
1961 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1962    
1963 root 1.110 return if $self->players;
1964     return if $self->in_memory != cf::MAP_IN_MEMORY;
1965     return if $self->{deny_save};
1966    
1967     $self->clear;
1968     $self->in_memory (cf::MAP_SWAPPED);
1969     }
1970    
1971 root 1.112 sub reset_at {
1972     my ($self) = @_;
1973 root 1.110
1974     # TODO: safety, remove and allow resettable per-player maps
1975 root 1.114 return 1e99 if $self->{deny_reset};
1976 root 1.110
1977 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1978 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1979 root 1.110
1980 root 1.112 $time + $to
1981     }
1982    
1983     sub should_reset {
1984     my ($self) = @_;
1985    
1986     $self->reset_at <= $cf::RUNTIME
1987 root 1.111 }
1988    
1989 root 1.110 sub reset {
1990     my ($self) = @_;
1991    
1992 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
1993 root 1.137
1994 root 1.110 return if $self->players;
1995    
1996 root 1.274 warn "resetting map ", $self->path;
1997 root 1.110
1998 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
1999    
2000     # need to save uniques path
2001     unless ($self->{deny_save}) {
2002     my $uniq = $self->uniq_path; utf8::encode $uniq;
2003    
2004     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2005     if $uniq;
2006     }
2007    
2008 root 1.111 delete $cf::MAP{$self->path};
2009 root 1.110
2010 root 1.167 $self->clear;
2011    
2012 root 1.110 $_->clear_links_to ($self) for values %cf::MAP;
2013    
2014 root 1.166 $self->unlink_save;
2015 root 1.111 $self->destroy;
2016 root 1.110 }
2017    
2018 root 1.114 my $nuke_counter = "aaaa";
2019    
2020     sub nuke {
2021     my ($self) = @_;
2022    
2023 root 1.174 delete $cf::MAP{$self->path};
2024    
2025     $self->unlink_save;
2026    
2027     bless $self, "cf::map";
2028     delete $self->{deny_reset};
2029 root 1.114 $self->{deny_save} = 1;
2030     $self->reset_timeout (1);
2031 root 1.174 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2032    
2033     $cf::MAP{$self->path} = $self;
2034    
2035 root 1.114 $self->reset; # polite request, might not happen
2036     }
2037    
2038 root 1.276 =item $maps = cf::map::tmp_maps
2039    
2040     Returns an arrayref with all map paths of currently instantiated and saved
2041 root 1.277 maps. May block.
2042 root 1.276
2043     =cut
2044    
2045     sub tmp_maps() {
2046     [
2047     map {
2048     utf8::decode $_;
2049 root 1.277 /\.map$/
2050 root 1.276 ? normalise $_
2051     : ()
2052     } @{ aio_readdir $TMPDIR or [] }
2053     ]
2054     }
2055    
2056 root 1.277 =item $maps = cf::map::random_maps
2057    
2058     Returns an arrayref with all map paths of currently instantiated and saved
2059     random maps. May block.
2060    
2061     =cut
2062    
2063     sub random_maps() {
2064     [
2065     map {
2066     utf8::decode $_;
2067     /\.map$/
2068     ? normalise "?random/$_"
2069     : ()
2070     } @{ aio_readdir $RANDOMDIR or [] }
2071     ]
2072     }
2073    
2074 root 1.158 =item cf::map::unique_maps
2075    
2076 root 1.166 Returns an arrayref of paths of all shared maps that have
2077 root 1.158 instantiated unique items. May block.
2078    
2079     =cut
2080    
2081     sub unique_maps() {
2082 root 1.276 [
2083     map {
2084     utf8::decode $_;
2085 root 1.277 /\.map$/
2086 root 1.276 ? normalise $_
2087     : ()
2088     } @{ aio_readdir $UNIQUEDIR or [] }
2089     ]
2090 root 1.158 }
2091    
2092 root 1.155 package cf;
2093    
2094     =back
2095    
2096     =head3 cf::object
2097    
2098     =cut
2099    
2100     package cf::object;
2101    
2102     =over 4
2103    
2104     =item $ob->inv_recursive
2105 root 1.110
2106 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
2107 root 1.110
2108 root 1.155 =cut
2109 root 1.144
2110 root 1.155 sub inv_recursive_;
2111     sub inv_recursive_ {
2112     map { $_, inv_recursive_ $_->inv } @_
2113     }
2114 root 1.110
2115 root 1.155 sub inv_recursive {
2116     inv_recursive_ inv $_[0]
2117 root 1.110 }
2118    
2119     package cf;
2120    
2121     =back
2122    
2123 root 1.95 =head3 cf::object::player
2124    
2125     =over 4
2126    
2127 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2128 root 1.28
2129     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2130     can be C<undef>. Does the right thing when the player is currently in a
2131     dialogue with the given NPC character.
2132    
2133     =cut
2134    
2135 root 1.22 # rough implementation of a future "reply" method that works
2136     # with dialog boxes.
2137 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2138 root 1.23 sub cf::object::player::reply($$$;$) {
2139     my ($self, $npc, $msg, $flags) = @_;
2140    
2141     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2142 root 1.22
2143 root 1.24 if ($self->{record_replies}) {
2144     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2145 elmex 1.282
2146 root 1.24 } else {
2147 elmex 1.282 my $pl = $self->contr;
2148    
2149     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2150     my $diag = $pl->{npc_dialog};
2151     $diag->{pl}->ext_reply (
2152 root 1.283 $diag->{id},
2153     msgtype => "reply",
2154     msg => $diag->{pl}->expand_cfpod ($msg),
2155     add_topics => []
2156 elmex 1.282 );
2157    
2158     } else {
2159     $msg = $npc->name . " says: $msg" if $npc;
2160     $self->message ($msg, $flags);
2161     }
2162 root 1.24 }
2163 root 1.22 }
2164    
2165 root 1.79 =item $player_object->may ("access")
2166    
2167     Returns wether the given player is authorized to access resource "access"
2168     (e.g. "command_wizcast").
2169    
2170     =cut
2171    
2172     sub cf::object::player::may {
2173     my ($self, $access) = @_;
2174    
2175     $self->flag (cf::FLAG_WIZ) ||
2176     (ref $cf::CFG{"may_$access"}
2177     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2178     : $cf::CFG{"may_$access"})
2179     }
2180 root 1.70
2181 root 1.115 =item $player_object->enter_link
2182    
2183     Freezes the player and moves him/her to a special map (C<{link}>).
2184    
2185 root 1.166 The player should be reasonably safe there for short amounts of time. You
2186 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
2187    
2188 root 1.166 Will never block.
2189    
2190 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2191    
2192 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2193     map. If the map is not valid (or omitted), the player will be moved back
2194     to the location he/she was before the call to C<enter_link>, or, if that
2195     fails, to the emergency map position.
2196 root 1.115
2197     Might block.
2198    
2199     =cut
2200    
2201 root 1.166 sub link_map {
2202     unless ($LINK_MAP) {
2203     $LINK_MAP = cf::map::find "{link}"
2204 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2205 root 1.166 $LINK_MAP->load;
2206     }
2207    
2208     $LINK_MAP
2209     }
2210    
2211 root 1.110 sub cf::object::player::enter_link {
2212     my ($self) = @_;
2213    
2214 root 1.259 $self->deactivate_recursive;
2215 root 1.258
2216 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2217 root 1.110
2218 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2219 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2220 root 1.110
2221 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
2222 root 1.110 }
2223    
2224     sub cf::object::player::leave_link {
2225     my ($self, $map, $x, $y) = @_;
2226    
2227 root 1.270 return unless $self->contr->active;
2228    
2229 root 1.110 my $link_pos = delete $self->{_link_pos};
2230    
2231     unless ($map) {
2232     # restore original map position
2233     ($map, $x, $y) = @{ $link_pos || [] };
2234 root 1.133 $map = cf::map::find $map;
2235 root 1.110
2236     unless ($map) {
2237     ($map, $x, $y) = @$EMERGENCY_POSITION;
2238 root 1.133 $map = cf::map::find $map
2239 root 1.110 or die "FATAL: cannot load emergency map\n";
2240     }
2241     }
2242    
2243     ($x, $y) = (-1, -1)
2244     unless (defined $x) && (defined $y);
2245    
2246     # use -1 or undef as default coordinates, not 0, 0
2247     ($x, $y) = ($map->enter_x, $map->enter_y)
2248     if $x <=0 && $y <= 0;
2249    
2250     $map->load;
2251 root 1.157 $map->load_diag;
2252 root 1.110
2253 root 1.143 return unless $self->contr->active;
2254 root 1.110 $self->activate_recursive;
2255 root 1.215
2256     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2257 root 1.110 $self->enter_map ($map, $x, $y);
2258     }
2259    
2260 root 1.268 =item $player_object->goto ($path, $x, $y[, $check->($map)])
2261    
2262     Moves the player to the given map-path and coordinates by first freezing
2263     her, loading and preparing them map, calling the provided $check callback
2264     that has to return the map if sucecssful, and then unfreezes the player on
2265     the new (success) or old (failed) map position.
2266 root 1.110
2267     =cut
2268    
2269 root 1.270 our $GOTOGEN;
2270    
2271 root 1.136 sub cf::object::player::goto {
2272 root 1.268 my ($self, $path, $x, $y, $check) = @_;
2273    
2274 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2275     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2276    
2277 root 1.110 $self->enter_link;
2278    
2279 root 1.140 (async {
2280 root 1.197 my $map = eval {
2281     my $map = cf::map::find $path;
2282 root 1.268
2283     if ($map) {
2284     $map = $map->customise_for ($self);
2285     $map = $check->($map) if $check && $map;
2286     } else {
2287     $self->message ("The exit to '$path' is closed", cf::NDI_UNIQUE | cf::NDI_RED);
2288     }
2289    
2290 root 1.197 $map
2291 root 1.268 };
2292    
2293     if ($@) {
2294     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2295     LOG llevError | logBacktrace, Carp::longmess $@;
2296     }
2297 root 1.115
2298 root 1.270 if ($gen == $self->{_goto_generation}) {
2299     delete $self->{_goto_generation};
2300     $self->leave_link ($map, $x, $y);
2301     }
2302 root 1.110 })->prio (1);
2303     }
2304    
2305     =item $player_object->enter_exit ($exit_object)
2306    
2307     =cut
2308    
2309     sub parse_random_map_params {
2310     my ($spec) = @_;
2311    
2312     my $rmp = { # defaults
2313 root 1.181 xsize => (cf::rndm 15, 40),
2314     ysize => (cf::rndm 15, 40),
2315     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2316 root 1.182 #layout => string,
2317 root 1.110 };
2318    
2319     for (split /\n/, $spec) {
2320     my ($k, $v) = split /\s+/, $_, 2;
2321    
2322     $rmp->{lc $k} = $v if (length $k) && (length $v);
2323     }
2324    
2325     $rmp
2326     }
2327    
2328     sub prepare_random_map {
2329     my ($exit) = @_;
2330    
2331 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
2332    
2333 root 1.110 # all this does is basically replace the /! path by
2334     # a new random map path (?random/...) with a seed
2335     # that depends on the exit object
2336    
2337     my $rmp = parse_random_map_params $exit->msg;
2338    
2339     if ($exit->map) {
2340 root 1.198 $rmp->{region} = $exit->region->name;
2341 root 1.110 $rmp->{origin_map} = $exit->map->path;
2342     $rmp->{origin_x} = $exit->x;
2343     $rmp->{origin_y} = $exit->y;
2344     }
2345    
2346     $rmp->{random_seed} ||= $exit->random_seed;
2347    
2348     my $data = cf::to_json $rmp;
2349     my $md5 = Digest::MD5::md5_hex $data;
2350 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2351 root 1.110
2352 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2353 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2354 root 1.177 undef $fh;
2355     aio_rename "$meta~", $meta;
2356 root 1.110
2357     $exit->slaying ("?random/$md5");
2358     $exit->msg (undef);
2359     }
2360     }
2361    
2362     sub cf::object::player::enter_exit {
2363     my ($self, $exit) = @_;
2364    
2365     return unless $self->type == cf::PLAYER;
2366    
2367 root 1.195 if ($exit->slaying eq "/!") {
2368     #TODO: this should de-fi-ni-te-ly not be a sync-job
2369 root 1.233 # the problem is that $exit might not survive long enough
2370     # so it needs to be done right now, right here
2371 root 1.195 cf::sync_job { prepare_random_map $exit };
2372     }
2373    
2374     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2375     my $hp = $exit->stats->hp;
2376     my $sp = $exit->stats->sp;
2377    
2378 root 1.110 $self->enter_link;
2379    
2380 root 1.296 # if exit is damned, update players death & WoR home-position
2381     $self->contr->savebed ($slaying, $hp, $sp)
2382     if $exit->flag (FLAG_DAMNED);
2383    
2384 root 1.140 (async {
2385 root 1.133 $self->deactivate_recursive; # just to be sure
2386 root 1.110 unless (eval {
2387 root 1.195 $self->goto ($slaying, $hp, $sp);
2388 root 1.110
2389     1;
2390     }) {
2391     $self->message ("Something went wrong deep within the crossfire server. "
2392 root 1.233 . "I'll try to bring you back to the map you were before. "
2393     . "Please report this to the dungeon master!",
2394     cf::NDI_UNIQUE | cf::NDI_RED);
2395 root 1.110
2396     warn "ERROR in enter_exit: $@";
2397     $self->leave_link;
2398     }
2399     })->prio (1);
2400     }
2401    
2402 root 1.95 =head3 cf::client
2403    
2404     =over 4
2405    
2406     =item $client->send_drawinfo ($text, $flags)
2407    
2408     Sends a drawinfo packet to the client. Circumvents output buffering so
2409     should not be used under normal circumstances.
2410    
2411 root 1.70 =cut
2412    
2413 root 1.95 sub cf::client::send_drawinfo {
2414     my ($self, $text, $flags) = @_;
2415    
2416     utf8::encode $text;
2417 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2418 root 1.95 }
2419    
2420 root 1.283 =item $client->send_msg ($color, $type, $msg, [extra...])
2421    
2422     Send a drawinfo or msg packet to the client, formatting the msg for the
2423     client if neccessary. C<$type> should be a string identifying the type of
2424     the message, with C<log> being the default. If C<$color> is negative, suppress
2425     the message unless the client supports the msg packet.
2426    
2427     =cut
2428    
2429     sub cf::client::send_msg {
2430     my ($self, $color, $type, $msg, @extra) = @_;
2431    
2432     $msg = $self->pl->expand_cfpod ($msg);
2433    
2434 root 1.294 return unless @extra || length $msg;
2435    
2436 root 1.283 if ($self->can_msg) {
2437 root 1.290 $self->send_packet ("msg " . $self->{json_coder}->encode ([$color, $type, $msg, @extra]));
2438 root 1.283 } else {
2439     # replace some tags by gcfclient-compatible ones
2440     for ($msg) {
2441     1 while
2442     s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2443     || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2444     || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2445     || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2446 root 1.285 || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2447 root 1.283 }
2448    
2449     if ($color >= 0) {
2450 root 1.284 if (0 && $msg =~ /\[/) {
2451 root 1.283 $self->send_packet ("drawextinfo $color 4 0 $msg")
2452     } else {
2453 root 1.286 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2454 root 1.283 $self->send_packet ("drawinfo $color $msg")
2455     }
2456     }
2457     }
2458     }
2459    
2460 root 1.232 =item $client->ext_event ($type, %msg)
2461    
2462 root 1.287 Sends an ext event to the client.
2463 root 1.232
2464     =cut
2465    
2466     sub cf::client::ext_event($$%) {
2467     my ($self, $type, %msg) = @_;
2468    
2469     $msg{msgtype} = "event_$type";
2470 root 1.290 $self->send_packet ("ext " . $self->{json_coder}->encode (\%msg));
2471 root 1.232 }
2472 root 1.95
2473     =item $success = $client->query ($flags, "text", \&cb)
2474    
2475     Queues a query to the client, calling the given callback with
2476     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2477     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2478    
2479 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
2480     become reliable at some point in the future.
2481 root 1.95
2482     =cut
2483    
2484     sub cf::client::query {
2485     my ($self, $flags, $text, $cb) = @_;
2486    
2487     return unless $self->state == ST_PLAYING
2488     || $self->state == ST_SETUP
2489     || $self->state == ST_CUSTOM;
2490    
2491     $self->state (ST_CUSTOM);
2492    
2493     utf8::encode $text;
2494     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2495    
2496     $self->send_packet ($self->{query_queue}[0][0])
2497     if @{ $self->{query_queue} } == 1;
2498 root 1.287
2499     1
2500 root 1.95 }
2501    
2502     cf::client->attach (
2503 root 1.290 on_connect => sub {
2504     my ($ns) = @_;
2505    
2506     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2507     },
2508 root 1.95 on_reply => sub {
2509     my ($ns, $msg) = @_;
2510    
2511     # this weird shuffling is so that direct followup queries
2512     # get handled first
2513 root 1.128 my $queue = delete $ns->{query_queue}
2514 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2515 root 1.95
2516     (shift @$queue)->[1]->($msg);
2517 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2518 root 1.95
2519     push @{ $ns->{query_queue} }, @$queue;
2520    
2521     if (@{ $ns->{query_queue} } == @$queue) {
2522     if (@$queue) {
2523     $ns->send_packet ($ns->{query_queue}[0][0]);
2524     } else {
2525 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2526 root 1.95 }
2527     }
2528     },
2529 root 1.287 on_exticmd => sub {
2530     my ($ns, $buf) = @_;
2531    
2532 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2533 root 1.287
2534     if (ref $msg) {
2535     if (my $cb = $EXTICMD{$msg->{msgtype}}) {
2536     if (my %reply = $cb->($ns, $msg)) {
2537     $reply{msgid} = $msg->{msgid};
2538 root 1.290 $ns->send ("ext " . $ns->{json_coder}->encode (\%reply));
2539 root 1.287 }
2540     }
2541     } else {
2542     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2543     }
2544    
2545     cf::override;
2546     },
2547 root 1.95 );
2548    
2549 root 1.140 =item $client->async (\&cb)
2550 root 1.96
2551     Create a new coroutine, running the specified callback. The coroutine will
2552     be automatically cancelled when the client gets destroyed (e.g. on logout,
2553     or loss of connection).
2554    
2555     =cut
2556    
2557 root 1.140 sub cf::client::async {
2558 root 1.96 my ($self, $cb) = @_;
2559    
2560 root 1.140 my $coro = &Coro::async ($cb);
2561 root 1.103
2562     $coro->on_destroy (sub {
2563 root 1.96 delete $self->{_coro}{$coro+0};
2564 root 1.103 });
2565 root 1.96
2566     $self->{_coro}{$coro+0} = $coro;
2567 root 1.103
2568     $coro
2569 root 1.96 }
2570    
2571     cf::client->attach (
2572     on_destroy => sub {
2573     my ($ns) = @_;
2574    
2575 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2576 root 1.96 },
2577     );
2578    
2579 root 1.95 =back
2580    
2581 root 1.70
2582     =head2 SAFE SCRIPTING
2583    
2584     Functions that provide a safe environment to compile and execute
2585     snippets of perl code without them endangering the safety of the server
2586     itself. Looping constructs, I/O operators and other built-in functionality
2587     is not available in the safe scripting environment, and the number of
2588 root 1.79 functions and methods that can be called is greatly reduced.
2589 root 1.70
2590     =cut
2591 root 1.23
2592 root 1.42 our $safe = new Safe "safe";
2593 root 1.23 our $safe_hole = new Safe::Hole;
2594    
2595     $SIG{FPE} = 'IGNORE';
2596    
2597     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
2598    
2599 root 1.25 # here we export the classes and methods available to script code
2600    
2601 root 1.70 =pod
2602    
2603 root 1.228 The following functions and methods are available within a safe environment:
2604 root 1.70
2605 root 1.297 cf::object
2606     contr pay_amount pay_player map x y force_find force_add
2607     insert remove
2608    
2609     cf::object::player
2610     player
2611    
2612     cf::player
2613     peaceful
2614    
2615     cf::map
2616     trigger
2617 root 1.70
2618     =cut
2619    
2620 root 1.25 for (
2621 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2622     insert remove)],
2623 root 1.25 ["cf::object::player" => qw(player)],
2624     ["cf::player" => qw(peaceful)],
2625 elmex 1.91 ["cf::map" => qw(trigger)],
2626 root 1.25 ) {
2627     no strict 'refs';
2628     my ($pkg, @funs) = @$_;
2629 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2630 root 1.25 for @funs;
2631     }
2632 root 1.23
2633 root 1.70 =over 4
2634    
2635     =item @retval = safe_eval $code, [var => value, ...]
2636    
2637     Compiled and executes the given perl code snippet. additional var/value
2638     pairs result in temporary local (my) scalar variables of the given name
2639     that are available in the code snippet. Example:
2640    
2641     my $five = safe_eval '$first + $second', first => 1, second => 4;
2642    
2643     =cut
2644    
2645 root 1.23 sub safe_eval($;@) {
2646     my ($code, %vars) = @_;
2647    
2648     my $qcode = $code;
2649     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2650     $qcode =~ s/\n/\\n/g;
2651    
2652     local $_;
2653 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2654 root 1.23
2655 root 1.42 my $eval =
2656 root 1.23 "do {\n"
2657     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2658     . "#line 0 \"{$qcode}\"\n"
2659     . $code
2660     . "\n}"
2661 root 1.25 ;
2662    
2663     sub_generation_inc;
2664 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2665 root 1.25 sub_generation_inc;
2666    
2667 root 1.42 if ($@) {
2668     warn "$@";
2669     warn "while executing safe code '$code'\n";
2670     warn "with arguments " . (join " ", %vars) . "\n";
2671     }
2672    
2673 root 1.25 wantarray ? @res : $res[0]
2674 root 1.23 }
2675    
2676 root 1.69 =item cf::register_script_function $function => $cb
2677    
2678     Register a function that can be called from within map/npc scripts. The
2679     function should be reasonably secure and should be put into a package name
2680     like the extension.
2681    
2682     Example: register a function that gets called whenever a map script calls
2683     C<rent::overview>, as used by the C<rent> extension.
2684    
2685     cf::register_script_function "rent::overview" => sub {
2686     ...
2687     };
2688    
2689     =cut
2690    
2691 root 1.23 sub register_script_function {
2692     my ($fun, $cb) = @_;
2693    
2694     no strict 'refs';
2695 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2696 root 1.23 }
2697    
2698 root 1.70 =back
2699    
2700 root 1.71 =cut
2701    
2702 root 1.23 #############################################################################
2703 root 1.203 # the server's init and main functions
2704    
2705 root 1.246 sub load_facedata($) {
2706     my ($path) = @_;
2707 root 1.223
2708 root 1.229 warn "loading facedata from $path\n";
2709 root 1.223
2710 root 1.236 my $facedata;
2711     0 < aio_load $path, $facedata
2712 root 1.223 or die "$path: $!";
2713    
2714 root 1.237 $facedata = Coro::Storable::thaw $facedata;
2715 root 1.223
2716 root 1.236 $facedata->{version} == 2
2717 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
2718    
2719 root 1.236 {
2720     my $faces = $facedata->{faceinfo};
2721    
2722     while (my ($face, $info) = each %$faces) {
2723     my $idx = (cf::face::find $face) || cf::face::alloc $face;
2724     cf::face::set $idx, $info->{visibility}, $info->{magicmap};
2725     cf::face::set_data $idx, 0, $info->{data32}, $info->{chksum32};
2726     cf::face::set_data $idx, 1, $info->{data64}, $info->{chksum64};
2727     Coro::cede;
2728     }
2729    
2730     while (my ($face, $info) = each %$faces) {
2731     next unless $info->{smooth};
2732     my $idx = cf::face::find $face
2733     or next;
2734     if (my $smooth = cf::face::find $info->{smooth}) {
2735     cf::face::set_smooth $idx, $smooth, $info->{smoothlevel};
2736     } else {
2737     warn "smooth face '$info->{smooth}' not found for face '$face'";
2738     }
2739     Coro::cede;
2740     }
2741 root 1.223 }
2742    
2743 root 1.236 {
2744     my $anims = $facedata->{animinfo};
2745    
2746     while (my ($anim, $info) = each %$anims) {
2747     cf::anim::set $anim, $info->{frames}, $info->{facings};
2748     Coro::cede;
2749 root 1.225 }
2750 root 1.236
2751     cf::anim::invalidate_all; # d'oh
2752 root 1.225 }
2753    
2754 root 1.223 1
2755     }
2756    
2757 root 1.253 sub reload_regions {
2758     load_resource_file "$MAPDIR/regions"
2759     or die "unable to load regions file\n";
2760     }
2761    
2762 root 1.246 sub reload_facedata {
2763 root 1.253 load_facedata "$DATADIR/facedata"
2764 root 1.246 or die "unable to load facedata\n";
2765     }
2766    
2767     sub reload_archetypes {
2768 root 1.253 load_resource_file "$DATADIR/archetypes"
2769 root 1.246 or die "unable to load archetypes\n";
2770 root 1.289 #d# NEED to laod twice to resolve forward references
2771     # this really needs to be done in an extra post-pass
2772     # (which needs to be synchronous, so solve it differently)
2773     load_resource_file "$DATADIR/archetypes"
2774     or die "unable to load archetypes\n";
2775 root 1.241 }
2776    
2777 root 1.246 sub reload_treasures {
2778 root 1.253 load_resource_file "$DATADIR/treasures"
2779 root 1.246 or die "unable to load treasurelists\n";
2780 root 1.241 }
2781    
2782 root 1.223 sub reload_resources {
2783 root 1.245 warn "reloading resource files...\n";
2784    
2785 root 1.246 reload_regions;
2786     reload_facedata;
2787 root 1.274 #reload_archetypes;#d#
2788 root 1.246 reload_archetypes;
2789     reload_treasures;
2790 root 1.245
2791     warn "finished reloading resource files\n";
2792 root 1.223 }
2793    
2794     sub init {
2795     reload_resources;
2796 root 1.203 }
2797 root 1.34
2798 root 1.73 sub cfg_load {
2799 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
2800 root 1.72 or return;
2801    
2802     local $/;
2803     *CFG = YAML::Syck::Load <$fh>;
2804 root 1.131
2805     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
2806    
2807 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
2808     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
2809    
2810 root 1.131 if (exists $CFG{mlockall}) {
2811     eval {
2812 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
2813 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
2814     };
2815     warn $@ if $@;
2816     }
2817 root 1.72 }
2818    
2819 root 1.39 sub main {
2820 root 1.108 # we must not ever block the main coroutine
2821     local $Coro::idle = sub {
2822 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
2823 root 1.175 (async {
2824     Event::one_event;
2825     })->prio (Coro::PRIO_MAX);
2826 root 1.108 };
2827    
2828 root 1.73 cfg_load;
2829 root 1.210 db_init;
2830 root 1.61 load_extensions;
2831 root 1.183
2832     $TICK_WATCHER->start;
2833 root 1.34 Event::loop;
2834     }
2835    
2836     #############################################################################
2837 root 1.155 # initialisation and cleanup
2838    
2839     # install some emergency cleanup handlers
2840     BEGIN {
2841     for my $signal (qw(INT HUP TERM)) {
2842     Event->signal (
2843 root 1.189 reentrant => 0,
2844     data => WF_AUTOCANCEL,
2845     signal => $signal,
2846 root 1.191 prio => 0,
2847 root 1.189 cb => sub {
2848 root 1.155 cf::cleanup "SIG$signal";
2849     },
2850     );
2851     }
2852     }
2853    
2854 root 1.281 sub write_runtime {
2855     my $runtime = "$LOCALDIR/runtime";
2856    
2857     # first touch the runtime file to show we are still running:
2858     # the fsync below can take a very very long time.
2859    
2860     IO::AIO::aio_utime $runtime, undef, undef;
2861    
2862     my $guard = cf::lock_acquire "write_runtime";
2863    
2864     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
2865     or return;
2866    
2867     my $value = $cf::RUNTIME + 90 + 10;
2868     # 10 is the runtime save interval, for a monotonic clock
2869     # 60 allows for the watchdog to kill the server.
2870    
2871     (aio_write $fh, 0, (length $value), $value, 0) <= 0
2872     and return;
2873    
2874     # always fsync - this file is important
2875     aio_fsync $fh
2876     and return;
2877    
2878     # touch it again to show we are up-to-date
2879     aio_utime $fh, undef, undef;
2880    
2881     close $fh
2882     or return;
2883    
2884     aio_rename "$runtime~", $runtime
2885     and return;
2886    
2887     warn "runtime file written.\n";
2888    
2889     1
2890     }
2891    
2892 root 1.156 sub emergency_save() {
2893 root 1.155 my $freeze_guard = cf::freeze_mainloop;
2894    
2895     warn "enter emergency perl save\n";
2896    
2897     cf::sync_job {
2898     # use a peculiar iteration method to avoid tripping on perl
2899     # refcount bugs in for. also avoids problems with players
2900 root 1.167 # and maps saved/destroyed asynchronously.
2901 root 1.155 warn "begin emergency player save\n";
2902     for my $login (keys %cf::PLAYER) {
2903     my $pl = $cf::PLAYER{$login} or next;
2904     $pl->valid or next;
2905     $pl->save;
2906     }
2907     warn "end emergency player save\n";
2908    
2909     warn "begin emergency map save\n";
2910     for my $path (keys %cf::MAP) {
2911     my $map = $cf::MAP{$path} or next;
2912     $map->valid or next;
2913     $map->save;
2914     }
2915     warn "end emergency map save\n";
2916 root 1.208
2917     warn "begin emergency database checkpoint\n";
2918     BDB::db_env_txn_checkpoint $DB_ENV;
2919     warn "end emergency database checkpoint\n";
2920 root 1.155 };
2921    
2922     warn "leave emergency perl save\n";
2923     }
2924 root 1.22
2925 root 1.211 sub post_cleanup {
2926     my ($make_core) = @_;
2927    
2928     warn Carp::longmess "post_cleanup backtrace"
2929     if $make_core;
2930     }
2931    
2932 root 1.246 sub do_reload_perl() {
2933 root 1.106 # can/must only be called in main
2934     if ($Coro::current != $Coro::main) {
2935 root 1.183 warn "can only reload from main coroutine";
2936 root 1.106 return;
2937     }
2938    
2939 root 1.103 warn "reloading...";
2940    
2941 root 1.212 warn "entering sync_job";
2942    
2943 root 1.213 cf::sync_job {
2944 root 1.214 cf::write_runtime; # external watchdog should not bark
2945 root 1.212 cf::emergency_save;
2946 root 1.214 cf::write_runtime; # external watchdog should not bark
2947 root 1.183
2948 root 1.212 warn "syncing database to disk";
2949     BDB::db_env_txn_checkpoint $DB_ENV;
2950 root 1.106
2951     # if anything goes wrong in here, we should simply crash as we already saved
2952 root 1.65
2953 root 1.183 warn "cancelling all WF_AUTOCANCEL watchers";
2954 root 1.87 for (Event::all_watchers) {
2955     $_->cancel if $_->data & WF_AUTOCANCEL;
2956     }
2957 root 1.65
2958 root 1.183 warn "flushing outstanding aio requests";
2959     for (;;) {
2960 root 1.208 BDB::flush;
2961 root 1.183 IO::AIO::flush;
2962     Coro::cede;
2963 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
2964 root 1.183 warn "iterate...";
2965     }
2966    
2967 root 1.223 ++$RELOAD;
2968    
2969 root 1.183 warn "cancelling all extension coros";
2970 root 1.103 $_->cancel for values %EXT_CORO;
2971     %EXT_CORO = ();
2972    
2973 root 1.183 warn "removing commands";
2974 root 1.159 %COMMAND = ();
2975    
2976 root 1.287 warn "removing ext/exti commands";
2977     %EXTCMD = ();
2978     %EXTICMD = ();
2979 root 1.159
2980 root 1.183 warn "unloading/nuking all extensions";
2981 root 1.159 for my $pkg (@EXTS) {
2982 root 1.160 warn "... unloading $pkg";
2983 root 1.159
2984     if (my $cb = $pkg->can ("unload")) {
2985     eval {
2986     $cb->($pkg);
2987     1
2988     } or warn "$pkg unloaded, but with errors: $@";
2989     }
2990    
2991 root 1.160 warn "... nuking $pkg";
2992 root 1.159 Symbol::delete_package $pkg;
2993 root 1.65 }
2994    
2995 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
2996 root 1.65 while (my ($k, $v) = each %INC) {
2997     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
2998    
2999 root 1.183 warn "... unloading $k";
3000 root 1.65 delete $INC{$k};
3001    
3002     $k =~ s/\.pm$//;
3003     $k =~ s/\//::/g;
3004    
3005     if (my $cb = $k->can ("unload_module")) {
3006     $cb->();
3007     }
3008    
3009     Symbol::delete_package $k;
3010     }
3011    
3012 root 1.183 warn "getting rid of safe::, as good as possible";
3013 root 1.65 Symbol::delete_package "safe::$_"
3014 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3015 root 1.65
3016 root 1.183 warn "unloading cf.pm \"a bit\"";
3017 root 1.65 delete $INC{"cf.pm"};
3018 root 1.252 delete $INC{"cf/pod.pm"};
3019 root 1.65
3020     # don't, removes xs symbols, too,
3021     # and global variables created in xs
3022     #Symbol::delete_package __PACKAGE__;
3023    
3024 root 1.183 warn "unload completed, starting to reload now";
3025    
3026 root 1.103 warn "reloading cf.pm";
3027 root 1.65 require cf;
3028 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3029    
3030 root 1.183 warn "loading config and database again";
3031 root 1.73 cf::cfg_load;
3032 root 1.65
3033 root 1.183 warn "loading extensions";
3034 root 1.65 cf::load_extensions;
3035    
3036 root 1.183 warn "reattaching attachments to objects/players";
3037 root 1.222 _global_reattach; # objects, sockets
3038 root 1.183 warn "reattaching attachments to maps";
3039 root 1.144 reattach $_ for values %MAP;
3040 root 1.222 warn "reattaching attachments to players";
3041     reattach $_ for values %PLAYER;
3042 root 1.183
3043 root 1.212 warn "leaving sync_job";
3044 root 1.183
3045 root 1.212 1
3046     } or do {
3047 root 1.106 warn $@;
3048     warn "error while reloading, exiting.";
3049     exit 1;
3050 root 1.212 };
3051 root 1.106
3052 root 1.159 warn "reloaded";
3053 root 1.65 };
3054    
3055 root 1.175 our $RELOAD_WATCHER; # used only during reload
3056    
3057 root 1.246 sub reload_perl() {
3058     # doing reload synchronously and two reloads happen back-to-back,
3059     # coro crashes during coro_state_free->destroy here.
3060    
3061     $RELOAD_WATCHER ||= Event->timer (
3062     reentrant => 0,
3063     after => 0,
3064     data => WF_AUTOCANCEL,
3065     cb => sub {
3066     do_reload_perl;
3067     undef $RELOAD_WATCHER;
3068     },
3069     );
3070     }
3071    
3072 root 1.111 register_command "reload" => sub {
3073 root 1.65 my ($who, $arg) = @_;
3074    
3075     if ($who->flag (FLAG_WIZ)) {
3076 root 1.175 $who->message ("reloading server.");
3077 root 1.246 async { reload_perl };
3078 root 1.65 }
3079     };
3080    
3081 root 1.27 unshift @INC, $LIBDIR;
3082 root 1.17
3083 root 1.183 my $bug_warning = 0;
3084    
3085 root 1.239 our @WAIT_FOR_TICK;
3086     our @WAIT_FOR_TICK_BEGIN;
3087    
3088     sub wait_for_tick {
3089 root 1.240 return unless $TICK_WATCHER->is_active;
3090 root 1.241 return if $Coro::current == $Coro::main;
3091    
3092 root 1.239 my $signal = new Coro::Signal;
3093     push @WAIT_FOR_TICK, $signal;
3094     $signal->wait;
3095     }
3096    
3097     sub wait_for_tick_begin {
3098 root 1.240 return unless $TICK_WATCHER->is_active;
3099 root 1.241 return if $Coro::current == $Coro::main;
3100    
3101 root 1.239 my $signal = new Coro::Signal;
3102     push @WAIT_FOR_TICK_BEGIN, $signal;
3103     $signal->wait;
3104     }
3105    
3106 root 1.268 my $min = 1e6;#d#
3107     my $avg = 10;
3108 root 1.35 $TICK_WATCHER = Event->timer (
3109 root 1.104 reentrant => 0,
3110 root 1.183 parked => 1,
3111 root 1.191 prio => 0,
3112 root 1.104 at => $NEXT_TICK || $TICK,
3113     data => WF_AUTOCANCEL,
3114     cb => sub {
3115 root 1.183 if ($Coro::current != $Coro::main) {
3116     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3117     unless ++$bug_warning > 10;
3118     return;
3119     }
3120    
3121 root 1.265 $NOW = $tick_start = Event::time;
3122 root 1.163
3123 root 1.133 cf::server_tick; # one server iteration
3124 root 1.245
3125 root 1.268 0 && sync_job {#d#
3126     for(1..10) {
3127     my $t = Event::time;
3128     my $map = my $map = new_from_path cf::map "/tmp/x.map"
3129     or die;
3130    
3131     $map->width (50);
3132     $map->height (50);
3133     $map->alloc;
3134     $map->_load_objects ("/tmp/x.map", 1);
3135     my $t = Event::time - $t;
3136    
3137     #next unless $t < 0.0013;#d#
3138     if ($t < $min) {
3139     $min = $t;
3140     }
3141     $avg = $avg * 0.99 + $t * 0.01;
3142     }
3143     warn "XXXXXXXXXXXXXXXXXX min $min avg $avg\n";#d#
3144     exit 0;
3145     # 2007-05-22 02:33:04.569 min 0.00112509727478027 avg 0.0012259249572477
3146     };
3147    
3148 root 1.133 $RUNTIME += $TICK;
3149 root 1.35 $NEXT_TICK += $TICK;
3150    
3151 root 1.214 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3152     $NEXT_RUNTIME_WRITE = $NOW + 10;
3153     Coro::async_pool {
3154     write_runtime
3155     or warn "ERROR: unable to write runtime file: $!";
3156     };
3157     }
3158    
3159 root 1.191 # my $AFTER = Event::time;
3160     # warn $AFTER - $NOW;#d#
3161 root 1.190
3162 root 1.245 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3163     $sig->send;
3164     }
3165     while (my $sig = shift @WAIT_FOR_TICK) {
3166     $sig->send;
3167     }
3168    
3169 root 1.265 $NOW = Event::time;
3170    
3171     # if we are delayed by four ticks or more, skip them all
3172     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
3173    
3174     $TICK_WATCHER->at ($NEXT_TICK);
3175     $TICK_WATCHER->start;
3176    
3177     $LOAD = ($NOW - $tick_start) / $TICK;
3178     $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3179    
3180 root 1.245 _post_tick;
3181 root 1.265
3182    
3183 root 1.35 },
3184     );
3185    
3186 root 1.206 {
3187     BDB::max_poll_time $TICK * 0.1;
3188     $BDB_POLL_WATCHER = Event->io (
3189     reentrant => 0,
3190     fd => BDB::poll_fileno,
3191     poll => 'r',
3192     prio => 0,
3193     data => WF_AUTOCANCEL,
3194     cb => \&BDB::poll_cb,
3195     );
3196     BDB::min_parallel 8;
3197    
3198     BDB::set_sync_prepare {
3199     my $status;
3200     my $current = $Coro::current;
3201     (
3202     sub {
3203     $status = $!;
3204     $current->ready; undef $current;
3205     },
3206     sub {
3207     Coro::schedule while defined $current;
3208     $! = $status;
3209     },
3210     )
3211     };
3212 root 1.77
3213 root 1.206 unless ($DB_ENV) {
3214     $DB_ENV = BDB::db_env_create;
3215    
3216     cf::sync_job {
3217 root 1.208 eval {
3218     BDB::db_env_open
3219     $DB_ENV,
3220 root 1.253 $BDBDIR,
3221 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3222     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3223     0666;
3224    
3225 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3226 root 1.208
3227     $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC, 1);
3228     $DB_ENV->set_lk_detect;
3229     };
3230    
3231     cf::cleanup "db_env_open(db): $@" if $@;
3232 root 1.206 };
3233     }
3234     }
3235    
3236     {
3237     IO::AIO::min_parallel 8;
3238    
3239     undef $Coro::AIO::WATCHER;
3240     IO::AIO::max_poll_time $TICK * 0.1;
3241     $AIO_POLL_WATCHER = Event->io (
3242     reentrant => 0,
3243 root 1.214 data => WF_AUTOCANCEL,
3244 root 1.206 fd => IO::AIO::poll_fileno,
3245     poll => 'r',
3246     prio => 6,
3247     cb => \&IO::AIO::poll_cb,
3248     );
3249     }
3250 root 1.108
3251 root 1.262 my $_log_backtrace;
3252    
3253 root 1.260 sub _log_backtrace {
3254     my ($msg, @addr) = @_;
3255    
3256 root 1.262 $msg =~ s/\n//;
3257 root 1.260
3258 root 1.262 # limit the # of concurrent backtraces
3259     if ($_log_backtrace < 2) {
3260     ++$_log_backtrace;
3261     async {
3262     my @bt = fork_call {
3263     @addr = map { sprintf "%x", $_ } @addr;
3264     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3265     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3266     or die "addr2line: $!";
3267    
3268     my @funcs;
3269     my @res = <$fh>;
3270     chomp for @res;
3271     while (@res) {
3272     my ($func, $line) = splice @res, 0, 2, ();
3273     push @funcs, "[$func] $line";
3274     }
3275 root 1.260
3276 root 1.262 @funcs
3277     };
3278 root 1.260
3279 root 1.262 LOG llevInfo, "[ABT] $msg\n";
3280     LOG llevInfo, "[ABT] $_\n" for @bt;
3281     --$_log_backtrace;
3282     };
3283     } else {
3284 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3285 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3286     }
3287 root 1.260 }
3288    
3289 root 1.249 # load additional modules
3290     use cf::pod;
3291    
3292 root 1.125 END { cf::emergency_save }
3293    
3294 root 1.1 1
3295