ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.305
Committed: Thu Jul 12 08:40:14 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.304: +2 -0 lines
Log Message:
first workable server-side music implementation

File Contents

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