ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.303
Committed: Wed Jul 11 15:57:31 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.302: +1 -4 lines
Log Message:
rudimentary resource support, create new region_change event and new ext/player-env.ext

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