ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.313
Committed: Sun Jul 22 14:17:58 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.312: +2 -2 lines
Log Message:
fix crash problem

File Contents

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