ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.310
Committed: Mon Jul 16 15:43:49 2007 UTC (16 years, 10 months ago) by elmex
Branch: MAIN
Changes since 1.309: +10 -4 lines
Log Message:
renamed invoke_results

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    
924 root 1.102 sub reattach {
925     # basically do the same as instantiate, without calling instantiate
926     my ($obj) = @_;
927    
928 root 1.169 bless $obj, ref $obj; # re-bless in case extensions have been reloaded
929    
930 root 1.102 my $registry = $obj->registry;
931    
932     @$registry = ();
933    
934     delete $obj->{_attachment} unless scalar keys %{ $obj->{_attachment} || {} };
935    
936     for my $name (keys %{ $obj->{_attachment} || {} }) {
937     if (my $attach = $attachment{$name}) {
938     for (@$attach) {
939     my ($klass, @attach) = @$_;
940     _attach $registry, $klass, @attach;
941     }
942     } else {
943     warn "object uses attachment '$name' that is not available, postponing.\n";
944     }
945     }
946     }
947    
948 root 1.100 cf::attachable->attach (
949     prio => -1000000,
950     on_instantiate => sub {
951     my ($obj, $data) = @_;
952 root 1.45
953 root 1.100 $data = from_json $data;
954 root 1.45
955 root 1.100 for (@$data) {
956     my ($name, $args) = @$_;
957 root 1.49
958 root 1.100 $obj->attach ($name, %{$args || {} });
959     }
960     },
961 root 1.102 on_reattach => \&reattach,
962 root 1.100 on_clone => sub {
963     my ($src, $dst) = @_;
964    
965     @{$dst->registry} = @{$src->registry};
966    
967     %$dst = %$src;
968    
969     %{$dst->{_attachment}} = %{$src->{_attachment}}
970     if exists $src->{_attachment};
971     },
972     );
973 root 1.45
974 root 1.46 sub object_freezer_save {
975 root 1.59 my ($filename, $rdata, $objs) = @_;
976 root 1.46
977 root 1.105 sync_job {
978     if (length $$rdata) {
979     warn sprintf "saving %s (%d,%d)\n",
980     $filename, length $$rdata, scalar @$objs;
981 root 1.60
982 root 1.105 if (my $fh = aio_open "$filename~", O_WRONLY | O_CREAT, 0600) {
983 root 1.60 chmod SAVE_MODE, $fh;
984 root 1.105 aio_write $fh, 0, (length $$rdata), $$rdata, 0;
985 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
986 root 1.60 close $fh;
987 root 1.105
988     if (@$objs) {
989     if (my $fh = aio_open "$filename.pst~", O_WRONLY | O_CREAT, 0600) {
990     chmod SAVE_MODE, $fh;
991     my $data = Storable::nfreeze { version => 1, objs => $objs };
992     aio_write $fh, 0, (length $data), $data, 0;
993 root 1.204 aio_fsync $fh if $cf::USE_FSYNC;
994 root 1.105 close $fh;
995     aio_rename "$filename.pst~", "$filename.pst";
996     }
997     } else {
998     aio_unlink "$filename.pst";
999     }
1000    
1001     aio_rename "$filename~", $filename;
1002 root 1.60 } else {
1003 root 1.105 warn "FATAL: $filename~: $!\n";
1004 root 1.60 }
1005 root 1.59 } else {
1006 root 1.105 aio_unlink $filename;
1007     aio_unlink "$filename.pst";
1008 root 1.59 }
1009 root 1.45 }
1010     }
1011    
1012 root 1.80 sub object_freezer_as_string {
1013     my ($rdata, $objs) = @_;
1014    
1015     use Data::Dumper;
1016    
1017 root 1.81 $$rdata . Dumper $objs
1018 root 1.80 }
1019    
1020 root 1.46 sub object_thawer_load {
1021     my ($filename) = @_;
1022    
1023 root 1.105 my ($data, $av);
1024 root 1.61
1025 root 1.105 (aio_load $filename, $data) >= 0
1026     or return;
1027 root 1.61
1028 root 1.105 unless (aio_stat "$filename.pst") {
1029     (aio_load "$filename.pst", $av) >= 0
1030     or return;
1031 root 1.113 $av = eval { (Storable::thaw $av)->{objs} };
1032 root 1.61 }
1033 root 1.45
1034 root 1.118 warn sprintf "loading %s (%d)\n",
1035 root 1.274 $filename, length $data, scalar @{$av || []};
1036 root 1.105 return ($data, $av);
1037 root 1.45 }
1038    
1039 root 1.281 =head2 COMMAND CALLBACKS
1040    
1041     =over 4
1042    
1043     =cut
1044    
1045 root 1.45 #############################################################################
1046 root 1.85 # command handling &c
1047 root 1.39
1048 root 1.85 =item cf::register_command $name => \&callback($ob,$args);
1049 root 1.1
1050 root 1.85 Register a callback for execution when the client sends the user command
1051     $name.
1052 root 1.5
1053 root 1.85 =cut
1054 root 1.5
1055 root 1.85 sub register_command {
1056     my ($name, $cb) = @_;
1057 root 1.5
1058 root 1.85 my $caller = caller;
1059     #warn "registering command '$name/$time' to '$caller'";
1060 root 1.1
1061 root 1.85 push @{ $COMMAND{$name} }, [$caller, $cb];
1062 root 1.1 }
1063    
1064 root 1.85 =item cf::register_extcmd $name => \&callback($pl,$packet);
1065 root 1.1
1066 root 1.287 Register a callback for execution when the client sends an (synchronous)
1067     extcmd packet. Ext commands will be processed in the order they are
1068     received by the server, like other user commands. The first argument is
1069     the logged-in player. Ext commands can only be processed after a player
1070     has logged in successfully.
1071    
1072     If the callback returns something, it is sent back as if reply was being
1073     called.
1074    
1075     =item cf::register_exticmd $name => \&callback($ns,$packet);
1076    
1077     Register a callback for execution when the client sends an (asynchronous)
1078     exticmd packet. Exti commands are processed by the server as soon as they
1079     are received, i.e. out of order w.r.t. other commands. The first argument
1080     is a client socket. Exti commands can be received anytime, even before
1081     log-in.
1082 root 1.1
1083 root 1.85 If the callback returns something, it is sent back as if reply was being
1084     called.
1085 root 1.1
1086 root 1.85 =cut
1087 root 1.1
1088 root 1.16 sub register_extcmd {
1089     my ($name, $cb) = @_;
1090    
1091 root 1.159 $EXTCMD{$name} = $cb;
1092 root 1.16 }
1093    
1094 root 1.287 sub register_exticmd {
1095     my ($name, $cb) = @_;
1096    
1097     $EXTICMD{$name} = $cb;
1098     }
1099    
1100 root 1.93 cf::player->attach (
1101 root 1.85 on_command => sub {
1102     my ($pl, $name, $params) = @_;
1103    
1104     my $cb = $COMMAND{$name}
1105     or return;
1106    
1107     for my $cmd (@$cb) {
1108     $cmd->[1]->($pl->ob, $params);
1109     }
1110    
1111     cf::override;
1112     },
1113     on_extcmd => sub {
1114     my ($pl, $buf) = @_;
1115    
1116 root 1.290 my $msg = eval { $pl->ns->{json_coder}->decode ($buf) };
1117 root 1.85
1118     if (ref $msg) {
1119     if (my $cb = $EXTCMD{$msg->{msgtype}}) {
1120 root 1.159 if (my %reply = $cb->($pl, $msg)) {
1121 root 1.85 $pl->ext_reply ($msg->{msgid}, %reply);
1122     }
1123     }
1124     } else {
1125     warn "player " . ($pl->ob->name) . " sent unparseable ext message: <$buf>\n";
1126     }
1127    
1128     cf::override;
1129     },
1130 root 1.93 );
1131 root 1.85
1132 root 1.278 sub load_extensions {
1133     cf::sync_job {
1134     my %todo;
1135    
1136     for my $path (<$LIBDIR/*.ext>) {
1137     next unless -r $path;
1138    
1139     $path =~ /([^\/\\]+)\.ext$/ or die "$path";
1140     my $base = $1;
1141     my $pkg = $1;
1142     $pkg =~ s/[^[:word:]]/_/g;
1143     $pkg = "ext::$pkg";
1144    
1145     open my $fh, "<:utf8", $path
1146     or die "$path: $!";
1147    
1148     my $source = do { local $/; <$fh> };
1149 root 1.1
1150 root 1.278 my %ext = (
1151     path => $path,
1152     base => $base,
1153     pkg => $pkg,
1154     );
1155 root 1.1
1156 root 1.279 $ext{meta} = { map { (split /=/, $_, 2)[0, 1] } split /\s+/, $1 }
1157     if $source =~ /\A#!.*?perl.*?#\s*(.*)$/m;
1158 root 1.1
1159 root 1.278 $ext{source} =
1160     "package $pkg; use strict; use utf8;\n"
1161     . "#line 1 \"$path\"\n{\n"
1162     . $source
1163     . "\n};\n1";
1164 root 1.1
1165 root 1.278 $todo{$base} = \%ext;
1166 root 1.166 }
1167 root 1.1
1168 root 1.278 my %done;
1169     while (%todo) {
1170     my $progress;
1171    
1172     while (my ($k, $v) = each %todo) {
1173 root 1.279 for (split /,\s*/, $v->{meta}{depends}) {
1174 root 1.278 goto skip
1175     unless exists $done{$_};
1176     }
1177    
1178     warn "... loading '$k' into '$v->{pkg}'\n";
1179    
1180     unless (eval $v->{source}) {
1181     my $msg = $@ ? "$v->{path}: $@\n"
1182 root 1.279 : "$v->{base}: extension inactive.\n";
1183 root 1.278
1184     if (exists $v->{meta}{mandatory}) {
1185     warn $msg;
1186     warn "mandatory extension failed to load, exiting.\n";
1187     exit 1;
1188     }
1189    
1190 root 1.279 warn $msg;
1191 root 1.278 }
1192    
1193     $done{$k} = delete $todo{$k};
1194     push @EXTS, $v->{pkg};
1195 root 1.279 $progress = 1;
1196 root 1.278 }
1197 root 1.1
1198 root 1.278 skip:
1199     die "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n"
1200     unless $progress;
1201     }
1202     };
1203 root 1.1 }
1204    
1205 root 1.8 #############################################################################
1206 root 1.70
1207 root 1.281 =back
1208    
1209 root 1.70 =head2 CORE EXTENSIONS
1210    
1211     Functions and methods that extend core crossfire objects.
1212    
1213 root 1.143 =cut
1214    
1215     package cf::player;
1216    
1217 root 1.154 use Coro::AIO;
1218    
1219 root 1.95 =head3 cf::player
1220    
1221 root 1.70 =over 4
1222 root 1.22
1223 root 1.143 =item cf::player::find $login
1224 root 1.23
1225 root 1.143 Returns the given player object, loading it if necessary (might block).
1226 root 1.23
1227     =cut
1228    
1229 root 1.145 sub playerdir($) {
1230 root 1.253 "$PLAYERDIR/"
1231 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1232     }
1233    
1234 root 1.143 sub path($) {
1235 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1236    
1237 root 1.234 (playerdir $login) . "/playerdata"
1238 root 1.143 }
1239    
1240     sub find_active($) {
1241     $cf::PLAYER{$_[0]}
1242     and $cf::PLAYER{$_[0]}->active
1243     and $cf::PLAYER{$_[0]}
1244     }
1245    
1246     sub exists($) {
1247     my ($login) = @_;
1248    
1249     $cf::PLAYER{$login}
1250 root 1.180 or cf::sync_job { !aio_stat path $login }
1251 root 1.143 }
1252    
1253     sub find($) {
1254     return $cf::PLAYER{$_[0]} || do {
1255     my $login = $_[0];
1256    
1257     my $guard = cf::lock_acquire "user_find:$login";
1258    
1259 root 1.151 $cf::PLAYER{$_[0]} || do {
1260 root 1.234 # rename old playerfiles to new ones
1261     #TODO: remove when no longer required
1262     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1263     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1264     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1265     aio_unlink +(playerdir $login) . "/$login.pl";
1266    
1267 root 1.151 my $pl = load_pl path $login
1268     or return;
1269     $cf::PLAYER{$login} = $pl
1270     }
1271     }
1272 root 1.143 }
1273    
1274     sub save($) {
1275     my ($pl) = @_;
1276    
1277     return if $pl->{deny_save};
1278    
1279     my $path = path $pl;
1280     my $guard = cf::lock_acquire "user_save:$path";
1281    
1282     return if $pl->{deny_save};
1283 root 1.146
1284 root 1.154 aio_mkdir playerdir $pl, 0770;
1285 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1286    
1287     $pl->save_pl ($path);
1288     Coro::cede;
1289     }
1290    
1291     sub new($) {
1292     my ($login) = @_;
1293    
1294     my $self = create;
1295    
1296     $self->ob->name ($login);
1297     $self->{deny_save} = 1;
1298    
1299     $cf::PLAYER{$login} = $self;
1300    
1301     $self
1302 root 1.23 }
1303    
1304 root 1.154 =item $pl->quit_character
1305    
1306     Nukes the player without looking back. If logged in, the connection will
1307     be destroyed. May block for a long time.
1308    
1309     =cut
1310    
1311 root 1.145 sub quit_character {
1312     my ($pl) = @_;
1313    
1314 root 1.220 my $name = $pl->ob->name;
1315    
1316 root 1.145 $pl->{deny_save} = 1;
1317     $pl->password ("*"); # this should lock out the player until we nuked the dir
1318    
1319     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1320     $pl->deactivate;
1321     $pl->invoke (cf::EVENT_PLAYER_QUIT);
1322     $pl->ns->destroy if $pl->ns;
1323    
1324     my $path = playerdir $pl;
1325     my $temp = "$path~$cf::RUNTIME~deleting~";
1326 root 1.154 aio_rename $path, $temp;
1327 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1328     $pl->destroy;
1329 root 1.220
1330     my $prefix = qr<^~\Q$name\E/>;
1331    
1332     # nuke player maps
1333     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1334    
1335 root 1.150 IO::AIO::aio_rmtree $temp;
1336 root 1.145 }
1337    
1338 pippijn 1.221 =item $pl->kick
1339    
1340     Kicks a player out of the game. This destroys the connection.
1341    
1342     =cut
1343    
1344     sub kick {
1345     my ($pl, $kicker) = @_;
1346    
1347     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1348     $pl->killer ("kicked");
1349     $pl->ns->destroy;
1350     }
1351    
1352 root 1.154 =item cf::player::list_logins
1353    
1354     Returns am arrayref of all valid playernames in the system, can take a
1355     while and may block, so not sync_job-capable, ever.
1356    
1357     =cut
1358    
1359     sub list_logins {
1360 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1361 root 1.154 or return [];
1362    
1363     my @logins;
1364    
1365     for my $login (@$dirs) {
1366     my $fh = aio_open path $login, Fcntl::O_RDONLY, 0 or next;
1367     aio_read $fh, 0, 512, my $buf, 0 or next;
1368 root 1.155 $buf !~ /^password -------------$/m or next; # official not-valid tag
1369 root 1.154
1370     utf8::decode $login;
1371     push @logins, $login;
1372     }
1373    
1374     \@logins
1375     }
1376    
1377     =item $player->maps
1378    
1379 root 1.166 Returns an arrayref of map paths that are private for this
1380 root 1.154 player. May block.
1381    
1382     =cut
1383    
1384     sub maps($) {
1385     my ($pl) = @_;
1386    
1387 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1388    
1389 root 1.154 my $files = aio_readdir playerdir $pl
1390     or return;
1391    
1392     my @paths;
1393    
1394     for (@$files) {
1395     utf8::decode $_;
1396     next if /\.(?:pl|pst)$/;
1397 root 1.158 next unless /^$PATH_SEP/o;
1398 root 1.154
1399 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1400 root 1.154 }
1401    
1402     \@paths
1403     }
1404    
1405 root 1.283 =item $protocol_xml = $player->expand_cfpod ($crossfire_pod)
1406    
1407     Expand crossfire pod fragments into protocol xml.
1408    
1409     =cut
1410    
1411     sub expand_cfpod {
1412     ((my $self), (local $_)) = @_;
1413    
1414     # escape & and <
1415     s/&/&amp;/g;
1416     s/(?<![BIUGH])</&lt;/g;
1417    
1418     # this is buggy, it needs to properly take care of nested <'s
1419    
1420     1 while
1421     # replace B<>, I<>, U<> etc.
1422     s/B<([^\>]*)>/<b>$1<\/b>/
1423     || s/I<([^\>]*)>/<i>$1<\/i>/
1424     || s/U<([^\>]*)>/<u>$1<\/u>/
1425     # replace G<male|female> tags
1426     || s{G<([^>|]*)\|([^>]*)>}{
1427     $self->gender ? $2 : $1
1428     }ge
1429     # replace H<hint text>
1430 root 1.291 || s{H<([^\>]*)>}
1431     {
1432     ("<fg name=\"lightblue\">[$1 (Use hintmode to suppress hints)]</fg>",
1433     "<fg name=\"lightblue\">[Hint suppressed, see hintmode]</fg>",
1434     "")
1435     [$self->{hintmode}]
1436     }ge;
1437 root 1.283
1438     # create single paragraphs (very hackish)
1439     s/(?<=\S)\n(?=\w)/ /g;
1440    
1441 root 1.291 # compress some whitespace
1442 root 1.295 s/\s+\n/\n/g; # ws line-ends
1443     s/\n\n+/\n/g; # double lines
1444     s/^\n+//; # beginning lines
1445     s/\n+$//; # ending lines
1446 root 1.293
1447 root 1.283 $_
1448     }
1449    
1450 root 1.291 sub hintmode {
1451     $_[0]{hintmode} = $_[1] if @_ > 1;
1452     $_[0]{hintmode}
1453     }
1454    
1455 root 1.231 =item $player->ext_reply ($msgid, %msg)
1456 root 1.95
1457     Sends an ext reply to the player.
1458    
1459     =cut
1460    
1461 root 1.231 sub ext_reply($$%) {
1462 root 1.95 my ($self, $id, %msg) = @_;
1463    
1464     $msg{msgid} = $id;
1465 root 1.290 $self->send ("ext " . $self->ns->{json_coder}->encode (\%msg));
1466 root 1.95 }
1467    
1468 root 1.231 =item $player->ext_event ($type, %msg)
1469    
1470     Sends an ext event to the client.
1471    
1472     =cut
1473    
1474     sub ext_event($$%) {
1475     my ($self, $type, %msg) = @_;
1476    
1477 root 1.232 $self->ns->ext_event ($type, %msg);
1478 root 1.231 }
1479    
1480 root 1.238 =head3 cf::region
1481    
1482     =over 4
1483    
1484     =cut
1485    
1486     package cf::region;
1487    
1488     =item cf::region::find_by_path $path
1489    
1490 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1491 root 1.238
1492     =cut
1493    
1494     sub find_by_path($) {
1495     my ($path) = @_;
1496    
1497     my ($match, $specificity);
1498    
1499     for my $region (list) {
1500 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1501 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1502     if $region->specificity > $specificity;
1503     }
1504     }
1505    
1506     $match
1507     }
1508 root 1.143
1509 root 1.95 =back
1510    
1511 root 1.110 =head3 cf::map
1512    
1513     =over 4
1514    
1515     =cut
1516    
1517     package cf::map;
1518    
1519     use Fcntl;
1520     use Coro::AIO;
1521    
1522 root 1.166 use overload
1523 root 1.173 '""' => \&as_string,
1524     fallback => 1;
1525 root 1.166
1526 root 1.133 our $MAX_RESET = 3600;
1527     our $DEFAULT_RESET = 3000;
1528 root 1.110
1529     sub generate_random_map {
1530 root 1.166 my ($self, $rmp) = @_;
1531 root 1.110 # mit "rum" bekleckern, nicht
1532 root 1.166 $self->_create_random_map (
1533 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1534     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1535     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1536     $rmp->{exit_on_final_map},
1537     $rmp->{xsize}, $rmp->{ysize},
1538     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1539     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1540     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1541     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1542     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1543 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1544     )
1545 root 1.110 }
1546    
1547 root 1.187 =item cf::map->register ($regex, $prio)
1548    
1549     Register a handler for the map path matching the given regex at the
1550     givne priority (higher is better, built-in handlers have priority 0, the
1551     default).
1552    
1553     =cut
1554    
1555 root 1.166 sub register {
1556 root 1.187 my (undef, $regex, $prio) = @_;
1557 root 1.166 my $pkg = caller;
1558    
1559     no strict;
1560     push @{"$pkg\::ISA"}, __PACKAGE__;
1561    
1562 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1563 root 1.166 }
1564    
1565     # also paths starting with '/'
1566 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1567 root 1.166
1568 root 1.170 sub thawer_merge {
1569 root 1.172 my ($self, $merge) = @_;
1570    
1571 root 1.170 # we have to keep some variables in memory intact
1572 root 1.172 local $self->{path};
1573     local $self->{load_path};
1574 root 1.170
1575 root 1.172 $self->SUPER::thawer_merge ($merge);
1576 root 1.170 }
1577    
1578 root 1.166 sub normalise {
1579     my ($path, $base) = @_;
1580    
1581 root 1.192 $path = "$path"; # make sure its a string
1582    
1583 root 1.199 $path =~ s/\.map$//;
1584    
1585 root 1.166 # map plan:
1586     #
1587     # /! non-realised random map exit (special hack!)
1588     # {... are special paths that are not being touched
1589     # ?xxx/... are special absolute paths
1590     # ?random/... random maps
1591     # /... normal maps
1592     # ~user/... per-player map of a specific user
1593    
1594     $path =~ s/$PATH_SEP/\//go;
1595    
1596     # treat it as relative path if it starts with
1597     # something that looks reasonable
1598     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1599     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1600    
1601     $base =~ s{[^/]+/?$}{};
1602     $path = "$base/$path";
1603     }
1604    
1605     for ($path) {
1606     redo if s{//}{/};
1607     redo if s{/\.?/}{/};
1608     redo if s{/[^/]+/\.\./}{/};
1609     }
1610    
1611     $path
1612     }
1613    
1614     sub new_from_path {
1615     my (undef, $path, $base) = @_;
1616    
1617     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1618    
1619     $path = normalise $path, $base;
1620    
1621 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1622     if ($path =~ $EXT_MAP{$pkg}[1]) {
1623 root 1.166 my $self = bless cf::map::new, $pkg;
1624     $self->{path} = $path; $self->path ($path);
1625     $self->init; # pass $1 etc.
1626     return $self;
1627     }
1628     }
1629    
1630 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1631 root 1.166 ()
1632     }
1633    
1634     sub init {
1635     my ($self) = @_;
1636    
1637     $self
1638     }
1639    
1640     sub as_string {
1641     my ($self) = @_;
1642    
1643     "$self->{path}"
1644     }
1645    
1646     # the displayed name, this is a one way mapping
1647     sub visible_name {
1648     &as_string
1649     }
1650    
1651     # the original (read-only) location
1652     sub load_path {
1653     my ($self) = @_;
1654    
1655 root 1.254 "$MAPDIR/$self->{path}.map"
1656 root 1.166 }
1657    
1658     # the temporary/swap location
1659     sub save_path {
1660     my ($self) = @_;
1661    
1662 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1663 root 1.254 "$TMPDIR/$path.map"
1664 root 1.166 }
1665    
1666     # the unique path, undef == no special unique path
1667     sub uniq_path {
1668     my ($self) = @_;
1669    
1670 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1671 root 1.253 "$UNIQUEDIR/$path"
1672 root 1.166 }
1673    
1674 root 1.110 # and all this just because we cannot iterate over
1675     # all maps in C++...
1676     sub change_all_map_light {
1677     my ($change) = @_;
1678    
1679 root 1.122 $_->change_map_light ($change)
1680     for grep $_->outdoor, values %cf::MAP;
1681 root 1.110 }
1682    
1683 root 1.275 sub decay_objects {
1684     my ($self) = @_;
1685    
1686     return if $self->{deny_reset};
1687    
1688     $self->do_decay_objects;
1689     }
1690    
1691 root 1.166 sub unlink_save {
1692     my ($self) = @_;
1693    
1694     utf8::encode (my $save = $self->save_path);
1695 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1696     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1697 root 1.166 }
1698    
1699     sub load_header_from($) {
1700     my ($self, $path) = @_;
1701 root 1.110
1702     utf8::encode $path;
1703 root 1.200 #aio_open $path, O_RDONLY, 0
1704     # or return;
1705 root 1.110
1706 root 1.166 $self->_load_header ($path)
1707 root 1.110 or return;
1708    
1709 root 1.166 $self->{load_path} = $path;
1710 root 1.135
1711 root 1.166 1
1712     }
1713 root 1.110
1714 root 1.188 sub load_header_orig {
1715 root 1.166 my ($self) = @_;
1716 root 1.110
1717 root 1.166 $self->load_header_from ($self->load_path)
1718 root 1.110 }
1719    
1720 root 1.188 sub load_header_temp {
1721 root 1.166 my ($self) = @_;
1722 root 1.110
1723 root 1.166 $self->load_header_from ($self->save_path)
1724     }
1725 root 1.110
1726 root 1.188 sub prepare_temp {
1727     my ($self) = @_;
1728    
1729     $self->last_access ((delete $self->{last_access})
1730     || $cf::RUNTIME); #d#
1731     # safety
1732     $self->{instantiate_time} = $cf::RUNTIME
1733     if $self->{instantiate_time} > $cf::RUNTIME;
1734     }
1735    
1736     sub prepare_orig {
1737     my ($self) = @_;
1738    
1739     $self->{load_original} = 1;
1740     $self->{instantiate_time} = $cf::RUNTIME;
1741     $self->last_access ($cf::RUNTIME);
1742     $self->instantiate;
1743     }
1744    
1745 root 1.166 sub load_header {
1746     my ($self) = @_;
1747 root 1.110
1748 root 1.188 if ($self->load_header_temp) {
1749     $self->prepare_temp;
1750 root 1.166 } else {
1751 root 1.188 $self->load_header_orig
1752 root 1.166 or return;
1753 root 1.188 $self->prepare_orig;
1754 root 1.166 }
1755 root 1.120
1756 root 1.275 $self->{deny_reset} = 1
1757     if $self->no_reset;
1758    
1759 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1760     unless $self->default_region;
1761    
1762 root 1.166 1
1763     }
1764 root 1.110
1765 root 1.166 sub find;
1766     sub find {
1767     my ($path, $origin) = @_;
1768 root 1.134
1769 root 1.166 $path = normalise $path, $origin && $origin->path;
1770 root 1.110
1771 root 1.166 cf::lock_wait "map_find:$path";
1772 root 1.110
1773 root 1.166 $cf::MAP{$path} || do {
1774     my $guard = cf::lock_acquire "map_find:$path";
1775     my $map = new_from_path cf::map $path
1776     or return;
1777 root 1.110
1778 root 1.116 $map->{last_save} = $cf::RUNTIME;
1779 root 1.110
1780 root 1.166 $map->load_header
1781     or return;
1782 root 1.134
1783 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
1784 root 1.185 # doing this can freeze the server in a sync job, obviously
1785     #$cf::WAIT_FOR_TICK->wait;
1786 root 1.112 $map->reset;
1787 root 1.123 undef $guard;
1788 root 1.192 return find $path;
1789 root 1.112 }
1790 root 1.110
1791 root 1.166 $cf::MAP{$path} = $map
1792 root 1.110 }
1793     }
1794    
1795 root 1.188 sub pre_load { }
1796     sub post_load { }
1797    
1798 root 1.110 sub load {
1799     my ($self) = @_;
1800    
1801 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
1802    
1803 root 1.120 my $path = $self->{path};
1804    
1805 root 1.256 {
1806     my $guard = cf::lock_acquire "map_load:$path";
1807    
1808     return if $self->in_memory != cf::MAP_SWAPPED;
1809 root 1.110
1810 root 1.256 $self->in_memory (cf::MAP_LOADING);
1811 root 1.110
1812 root 1.256 $self->alloc;
1813 root 1.188
1814 root 1.256 $self->pre_load;
1815     Coro::cede;
1816 root 1.188
1817 root 1.256 $self->_load_objects ($self->{load_path}, 1)
1818     or return;
1819 root 1.110
1820 root 1.256 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
1821     if delete $self->{load_original};
1822 root 1.111
1823 root 1.256 if (my $uniq = $self->uniq_path) {
1824     utf8::encode $uniq;
1825     if (aio_open $uniq, O_RDONLY, 0) {
1826     $self->clear_unique_items;
1827     $self->_load_objects ($uniq, 0);
1828     }
1829 root 1.110 }
1830    
1831 root 1.166 Coro::cede;
1832 root 1.256 # now do the right thing for maps
1833     $self->link_multipart_objects;
1834 root 1.110 $self->difficulty ($self->estimate_difficulty)
1835     unless $self->difficulty;
1836 root 1.166 Coro::cede;
1837 root 1.256
1838     unless ($self->{deny_activate}) {
1839     $self->decay_objects;
1840     $self->fix_auto_apply;
1841     $self->update_buttons;
1842     Coro::cede;
1843     $self->set_darkness_map;
1844     Coro::cede;
1845     $self->activate;
1846     }
1847    
1848     $self->in_memory (cf::MAP_IN_MEMORY);
1849 root 1.110 }
1850    
1851 root 1.188 $self->post_load;
1852 root 1.166 }
1853    
1854     sub customise_for {
1855     my ($self, $ob) = @_;
1856    
1857     return find "~" . $ob->name . "/" . $self->{path}
1858     if $self->per_player;
1859 root 1.134
1860 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
1861     # if $self->per_party;
1862    
1863 root 1.166 $self
1864 root 1.110 }
1865    
1866 root 1.157 # find and load all maps in the 3x3 area around a map
1867     sub load_diag {
1868     my ($map) = @_;
1869    
1870     my @diag; # diagonal neighbours
1871    
1872     for (0 .. 3) {
1873     my $neigh = $map->tile_path ($_)
1874     or next;
1875     $neigh = find $neigh, $map
1876     or next;
1877     $neigh->load;
1878    
1879     push @diag, [$neigh->tile_path (($_ + 3) % 4), $neigh],
1880     [$neigh->tile_path (($_ + 1) % 4), $neigh];
1881     }
1882    
1883     for (@diag) {
1884     my $neigh = find @$_
1885     or next;
1886     $neigh->load;
1887     }
1888     }
1889    
1890 root 1.133 sub find_sync {
1891 root 1.110 my ($path, $origin) = @_;
1892    
1893 root 1.157 cf::sync_job { find $path, $origin }
1894 root 1.133 }
1895    
1896     sub do_load_sync {
1897     my ($map) = @_;
1898 root 1.110
1899 root 1.133 cf::sync_job { $map->load };
1900 root 1.110 }
1901    
1902 root 1.157 our %MAP_PREFETCH;
1903 root 1.183 our $MAP_PREFETCHER = undef;
1904 root 1.157
1905     sub find_async {
1906     my ($path, $origin) = @_;
1907    
1908 root 1.166 $path = normalise $path, $origin && $origin->{path};
1909 root 1.157
1910 root 1.166 if (my $map = $cf::MAP{$path}) {
1911 root 1.157 return $map if $map->in_memory == cf::MAP_IN_MEMORY;
1912     }
1913    
1914 root 1.183 undef $MAP_PREFETCH{$path};
1915     $MAP_PREFETCHER ||= cf::async {
1916     while (%MAP_PREFETCH) {
1917     for my $path (keys %MAP_PREFETCH) {
1918 root 1.308 if (my $map = find $path) {
1919     $map->load;
1920     }
1921 root 1.183
1922     delete $MAP_PREFETCH{$path};
1923     }
1924     }
1925     undef $MAP_PREFETCHER;
1926     };
1927 root 1.189 $MAP_PREFETCHER->prio (6);
1928 root 1.157
1929     ()
1930     }
1931    
1932 root 1.110 sub save {
1933     my ($self) = @_;
1934    
1935 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1936    
1937 root 1.110 $self->{last_save} = $cf::RUNTIME;
1938    
1939     return unless $self->dirty;
1940    
1941 root 1.166 my $save = $self->save_path; utf8::encode $save;
1942     my $uniq = $self->uniq_path; utf8::encode $uniq;
1943 root 1.117
1944 root 1.110 $self->{load_path} = $save;
1945    
1946     return if $self->{deny_save};
1947    
1948 root 1.132 local $self->{last_access} = $self->last_access;#d#
1949    
1950 root 1.143 cf::async {
1951     $_->contr->save for $self->players;
1952     };
1953    
1954 root 1.110 if ($uniq) {
1955 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
1956     $self->_save_objects ($uniq, cf::IO_UNIQUES);
1957 root 1.110 } else {
1958 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
1959 root 1.110 }
1960     }
1961    
1962     sub swap_out {
1963     my ($self) = @_;
1964    
1965 root 1.130 # save first because save cedes
1966     $self->save;
1967    
1968 root 1.137 my $lock = cf::lock_acquire "map_data:" . $self->path;
1969    
1970 root 1.110 return if $self->players;
1971     return if $self->in_memory != cf::MAP_IN_MEMORY;
1972     return if $self->{deny_save};
1973    
1974     $self->clear;
1975     $self->in_memory (cf::MAP_SWAPPED);
1976     }
1977    
1978 root 1.112 sub reset_at {
1979     my ($self) = @_;
1980 root 1.110
1981     # TODO: safety, remove and allow resettable per-player maps
1982 root 1.114 return 1e99 if $self->{deny_reset};
1983 root 1.110
1984 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
1985 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
1986 root 1.110
1987 root 1.112 $time + $to
1988     }
1989    
1990     sub should_reset {
1991     my ($self) = @_;
1992    
1993     $self->reset_at <= $cf::RUNTIME
1994 root 1.111 }
1995    
1996 root 1.110 sub reset {
1997     my ($self) = @_;
1998    
1999 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2000 root 1.137
2001 root 1.110 return if $self->players;
2002    
2003 root 1.274 warn "resetting map ", $self->path;
2004 root 1.110
2005 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2006    
2007     # need to save uniques path
2008     unless ($self->{deny_save}) {
2009     my $uniq = $self->uniq_path; utf8::encode $uniq;
2010    
2011     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2012     if $uniq;
2013     }
2014    
2015 root 1.111 delete $cf::MAP{$self->path};
2016 root 1.110
2017 root 1.167 $self->clear;
2018    
2019 root 1.110 $_->clear_links_to ($self) for values %cf::MAP;
2020    
2021 root 1.166 $self->unlink_save;
2022 root 1.111 $self->destroy;
2023 root 1.110 }
2024    
2025 root 1.114 my $nuke_counter = "aaaa";
2026    
2027     sub nuke {
2028     my ($self) = @_;
2029    
2030 root 1.174 delete $cf::MAP{$self->path};
2031    
2032     $self->unlink_save;
2033    
2034     bless $self, "cf::map";
2035     delete $self->{deny_reset};
2036 root 1.114 $self->{deny_save} = 1;
2037     $self->reset_timeout (1);
2038 root 1.174 $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2039    
2040     $cf::MAP{$self->path} = $self;
2041    
2042 root 1.114 $self->reset; # polite request, might not happen
2043     }
2044    
2045 root 1.276 =item $maps = cf::map::tmp_maps
2046    
2047     Returns an arrayref with all map paths of currently instantiated and saved
2048 root 1.277 maps. May block.
2049 root 1.276
2050     =cut
2051    
2052     sub tmp_maps() {
2053     [
2054     map {
2055     utf8::decode $_;
2056 root 1.277 /\.map$/
2057 root 1.276 ? normalise $_
2058     : ()
2059     } @{ aio_readdir $TMPDIR or [] }
2060     ]
2061     }
2062    
2063 root 1.277 =item $maps = cf::map::random_maps
2064    
2065     Returns an arrayref with all map paths of currently instantiated and saved
2066     random maps. May block.
2067    
2068     =cut
2069    
2070     sub random_maps() {
2071     [
2072     map {
2073     utf8::decode $_;
2074     /\.map$/
2075     ? normalise "?random/$_"
2076     : ()
2077     } @{ aio_readdir $RANDOMDIR or [] }
2078     ]
2079     }
2080    
2081 root 1.158 =item cf::map::unique_maps
2082    
2083 root 1.166 Returns an arrayref of paths of all shared maps that have
2084 root 1.158 instantiated unique items. May block.
2085    
2086     =cut
2087    
2088     sub unique_maps() {
2089 root 1.276 [
2090     map {
2091     utf8::decode $_;
2092 root 1.277 /\.map$/
2093 root 1.276 ? normalise $_
2094     : ()
2095     } @{ aio_readdir $UNIQUEDIR or [] }
2096     ]
2097 root 1.158 }
2098    
2099 root 1.155 package cf;
2100    
2101     =back
2102    
2103     =head3 cf::object
2104    
2105     =cut
2106    
2107     package cf::object;
2108    
2109     =over 4
2110    
2111     =item $ob->inv_recursive
2112 root 1.110
2113 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
2114 root 1.110
2115 root 1.155 =cut
2116 root 1.144
2117 root 1.155 sub inv_recursive_;
2118     sub inv_recursive_ {
2119     map { $_, inv_recursive_ $_->inv } @_
2120     }
2121 root 1.110
2122 root 1.155 sub inv_recursive {
2123     inv_recursive_ inv $_[0]
2124 root 1.110 }
2125    
2126     package cf;
2127    
2128     =back
2129    
2130 root 1.95 =head3 cf::object::player
2131    
2132     =over 4
2133    
2134 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2135 root 1.28
2136     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2137     can be C<undef>. Does the right thing when the player is currently in a
2138     dialogue with the given NPC character.
2139    
2140     =cut
2141    
2142 root 1.22 # rough implementation of a future "reply" method that works
2143     # with dialog boxes.
2144 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2145 root 1.23 sub cf::object::player::reply($$$;$) {
2146     my ($self, $npc, $msg, $flags) = @_;
2147    
2148     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2149 root 1.22
2150 root 1.24 if ($self->{record_replies}) {
2151     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2152 elmex 1.282
2153 root 1.24 } else {
2154 elmex 1.282 my $pl = $self->contr;
2155    
2156     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2157     my $diag = $pl->{npc_dialog};
2158     $diag->{pl}->ext_reply (
2159 root 1.283 $diag->{id},
2160     msgtype => "reply",
2161     msg => $diag->{pl}->expand_cfpod ($msg),
2162     add_topics => []
2163 elmex 1.282 );
2164    
2165     } else {
2166     $msg = $npc->name . " says: $msg" if $npc;
2167     $self->message ($msg, $flags);
2168     }
2169 root 1.24 }
2170 root 1.22 }
2171    
2172 root 1.79 =item $player_object->may ("access")
2173    
2174     Returns wether the given player is authorized to access resource "access"
2175     (e.g. "command_wizcast").
2176    
2177     =cut
2178    
2179     sub cf::object::player::may {
2180     my ($self, $access) = @_;
2181    
2182     $self->flag (cf::FLAG_WIZ) ||
2183     (ref $cf::CFG{"may_$access"}
2184     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2185     : $cf::CFG{"may_$access"})
2186     }
2187 root 1.70
2188 root 1.115 =item $player_object->enter_link
2189    
2190     Freezes the player and moves him/her to a special map (C<{link}>).
2191    
2192 root 1.166 The player should be reasonably safe there for short amounts of time. You
2193 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
2194    
2195 root 1.166 Will never block.
2196    
2197 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2198    
2199 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2200     map. If the map is not valid (or omitted), the player will be moved back
2201     to the location he/she was before the call to C<enter_link>, or, if that
2202     fails, to the emergency map position.
2203 root 1.115
2204     Might block.
2205    
2206     =cut
2207    
2208 root 1.166 sub link_map {
2209     unless ($LINK_MAP) {
2210     $LINK_MAP = cf::map::find "{link}"
2211 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2212 root 1.166 $LINK_MAP->load;
2213     }
2214    
2215     $LINK_MAP
2216     }
2217    
2218 root 1.110 sub cf::object::player::enter_link {
2219     my ($self) = @_;
2220    
2221 root 1.259 $self->deactivate_recursive;
2222 root 1.258
2223 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2224 root 1.110
2225 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2226 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2227 root 1.110
2228 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
2229 root 1.110 }
2230    
2231     sub cf::object::player::leave_link {
2232     my ($self, $map, $x, $y) = @_;
2233    
2234 root 1.270 return unless $self->contr->active;
2235    
2236 root 1.110 my $link_pos = delete $self->{_link_pos};
2237    
2238     unless ($map) {
2239     # restore original map position
2240     ($map, $x, $y) = @{ $link_pos || [] };
2241 root 1.133 $map = cf::map::find $map;
2242 root 1.110
2243     unless ($map) {
2244     ($map, $x, $y) = @$EMERGENCY_POSITION;
2245 root 1.133 $map = cf::map::find $map
2246 root 1.110 or die "FATAL: cannot load emergency map\n";
2247     }
2248     }
2249    
2250     ($x, $y) = (-1, -1)
2251     unless (defined $x) && (defined $y);
2252    
2253     # use -1 or undef as default coordinates, not 0, 0
2254     ($x, $y) = ($map->enter_x, $map->enter_y)
2255     if $x <=0 && $y <= 0;
2256    
2257     $map->load;
2258 root 1.157 $map->load_diag;
2259 root 1.110
2260 root 1.143 return unless $self->contr->active;
2261 root 1.110 $self->activate_recursive;
2262 root 1.215
2263     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2264 root 1.110 $self->enter_map ($map, $x, $y);
2265     }
2266    
2267 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2268 root 1.268
2269     Moves the player to the given map-path and coordinates by first freezing
2270     her, loading and preparing them map, calling the provided $check callback
2271     that has to return the map if sucecssful, and then unfreezes the player on
2272 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2273     be called at the end of this process.
2274 root 1.110
2275     =cut
2276    
2277 root 1.270 our $GOTOGEN;
2278    
2279 root 1.136 sub cf::object::player::goto {
2280 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2281 root 1.268
2282 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2283     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2284    
2285 root 1.110 $self->enter_link;
2286    
2287 root 1.140 (async {
2288 root 1.197 my $map = eval {
2289     my $map = cf::map::find $path;
2290 root 1.268
2291     if ($map) {
2292     $map = $map->customise_for ($self);
2293     $map = $check->($map) if $check && $map;
2294     } else {
2295     $self->message ("The exit to '$path' is closed", cf::NDI_UNIQUE | cf::NDI_RED);
2296     }
2297    
2298 root 1.197 $map
2299 root 1.268 };
2300    
2301     if ($@) {
2302     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2303     LOG llevError | logBacktrace, Carp::longmess $@;
2304     }
2305 root 1.115
2306 root 1.270 if ($gen == $self->{_goto_generation}) {
2307     delete $self->{_goto_generation};
2308     $self->leave_link ($map, $x, $y);
2309     }
2310 root 1.306
2311     $done->() if $done;
2312 root 1.110 })->prio (1);
2313     }
2314    
2315     =item $player_object->enter_exit ($exit_object)
2316    
2317     =cut
2318    
2319     sub parse_random_map_params {
2320     my ($spec) = @_;
2321    
2322     my $rmp = { # defaults
2323 root 1.181 xsize => (cf::rndm 15, 40),
2324     ysize => (cf::rndm 15, 40),
2325     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2326 root 1.182 #layout => string,
2327 root 1.110 };
2328    
2329     for (split /\n/, $spec) {
2330     my ($k, $v) = split /\s+/, $_, 2;
2331    
2332     $rmp->{lc $k} = $v if (length $k) && (length $v);
2333     }
2334    
2335     $rmp
2336     }
2337    
2338     sub prepare_random_map {
2339     my ($exit) = @_;
2340    
2341 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
2342    
2343 root 1.110 # all this does is basically replace the /! path by
2344     # a new random map path (?random/...) with a seed
2345     # that depends on the exit object
2346    
2347     my $rmp = parse_random_map_params $exit->msg;
2348    
2349     if ($exit->map) {
2350 root 1.198 $rmp->{region} = $exit->region->name;
2351 root 1.110 $rmp->{origin_map} = $exit->map->path;
2352     $rmp->{origin_x} = $exit->x;
2353     $rmp->{origin_y} = $exit->y;
2354     }
2355    
2356     $rmp->{random_seed} ||= $exit->random_seed;
2357    
2358     my $data = cf::to_json $rmp;
2359     my $md5 = Digest::MD5::md5_hex $data;
2360 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2361 root 1.110
2362 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2363 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2364 root 1.177 undef $fh;
2365     aio_rename "$meta~", $meta;
2366 root 1.110
2367     $exit->slaying ("?random/$md5");
2368     $exit->msg (undef);
2369     }
2370     }
2371    
2372     sub cf::object::player::enter_exit {
2373     my ($self, $exit) = @_;
2374    
2375     return unless $self->type == cf::PLAYER;
2376    
2377 root 1.195 if ($exit->slaying eq "/!") {
2378     #TODO: this should de-fi-ni-te-ly not be a sync-job
2379 root 1.233 # the problem is that $exit might not survive long enough
2380     # so it needs to be done right now, right here
2381 root 1.195 cf::sync_job { prepare_random_map $exit };
2382     }
2383    
2384     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2385     my $hp = $exit->stats->hp;
2386     my $sp = $exit->stats->sp;
2387    
2388 root 1.110 $self->enter_link;
2389    
2390 root 1.296 # if exit is damned, update players death & WoR home-position
2391     $self->contr->savebed ($slaying, $hp, $sp)
2392     if $exit->flag (FLAG_DAMNED);
2393    
2394 root 1.140 (async {
2395 root 1.133 $self->deactivate_recursive; # just to be sure
2396 root 1.110 unless (eval {
2397 root 1.195 $self->goto ($slaying, $hp, $sp);
2398 root 1.110
2399     1;
2400     }) {
2401     $self->message ("Something went wrong deep within the crossfire server. "
2402 root 1.233 . "I'll try to bring you back to the map you were before. "
2403     . "Please report this to the dungeon master!",
2404     cf::NDI_UNIQUE | cf::NDI_RED);
2405 root 1.110
2406     warn "ERROR in enter_exit: $@";
2407     $self->leave_link;
2408     }
2409     })->prio (1);
2410     }
2411    
2412 root 1.95 =head3 cf::client
2413    
2414     =over 4
2415    
2416     =item $client->send_drawinfo ($text, $flags)
2417    
2418     Sends a drawinfo packet to the client. Circumvents output buffering so
2419     should not be used under normal circumstances.
2420    
2421 root 1.70 =cut
2422    
2423 root 1.95 sub cf::client::send_drawinfo {
2424     my ($self, $text, $flags) = @_;
2425    
2426     utf8::encode $text;
2427 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2428 root 1.95 }
2429    
2430 root 1.283 =item $client->send_msg ($color, $type, $msg, [extra...])
2431    
2432     Send a drawinfo or msg packet to the client, formatting the msg for the
2433     client if neccessary. C<$type> should be a string identifying the type of
2434     the message, with C<log> being the default. If C<$color> is negative, suppress
2435     the message unless the client supports the msg packet.
2436    
2437     =cut
2438    
2439     sub cf::client::send_msg {
2440     my ($self, $color, $type, $msg, @extra) = @_;
2441    
2442     $msg = $self->pl->expand_cfpod ($msg);
2443    
2444 root 1.294 return unless @extra || length $msg;
2445    
2446 root 1.283 if ($self->can_msg) {
2447 root 1.290 $self->send_packet ("msg " . $self->{json_coder}->encode ([$color, $type, $msg, @extra]));
2448 root 1.283 } else {
2449     # replace some tags by gcfclient-compatible ones
2450     for ($msg) {
2451     1 while
2452     s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2453     || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2454     || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2455     || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2456 root 1.285 || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2457 root 1.283 }
2458    
2459     if ($color >= 0) {
2460 root 1.284 if (0 && $msg =~ /\[/) {
2461 root 1.283 $self->send_packet ("drawextinfo $color 4 0 $msg")
2462     } else {
2463 root 1.286 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2464 root 1.283 $self->send_packet ("drawinfo $color $msg")
2465     }
2466     }
2467     }
2468     }
2469    
2470 root 1.232 =item $client->ext_event ($type, %msg)
2471    
2472 root 1.287 Sends an ext event to the client.
2473 root 1.232
2474     =cut
2475    
2476     sub cf::client::ext_event($$%) {
2477     my ($self, $type, %msg) = @_;
2478    
2479 root 1.305 return unless $self->extcmd;
2480    
2481 root 1.232 $msg{msgtype} = "event_$type";
2482 root 1.290 $self->send_packet ("ext " . $self->{json_coder}->encode (\%msg));
2483 root 1.232 }
2484 root 1.95
2485     =item $success = $client->query ($flags, "text", \&cb)
2486    
2487     Queues a query to the client, calling the given callback with
2488     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2489     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2490    
2491 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
2492     become reliable at some point in the future.
2493 root 1.95
2494     =cut
2495    
2496     sub cf::client::query {
2497     my ($self, $flags, $text, $cb) = @_;
2498    
2499     return unless $self->state == ST_PLAYING
2500     || $self->state == ST_SETUP
2501     || $self->state == ST_CUSTOM;
2502    
2503     $self->state (ST_CUSTOM);
2504    
2505     utf8::encode $text;
2506     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2507    
2508     $self->send_packet ($self->{query_queue}[0][0])
2509     if @{ $self->{query_queue} } == 1;
2510 root 1.287
2511     1
2512 root 1.95 }
2513    
2514     cf::client->attach (
2515 root 1.290 on_connect => sub {
2516     my ($ns) = @_;
2517    
2518     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2519     },
2520 root 1.95 on_reply => sub {
2521     my ($ns, $msg) = @_;
2522    
2523     # this weird shuffling is so that direct followup queries
2524     # get handled first
2525 root 1.128 my $queue = delete $ns->{query_queue}
2526 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2527 root 1.95
2528     (shift @$queue)->[1]->($msg);
2529 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2530 root 1.95
2531     push @{ $ns->{query_queue} }, @$queue;
2532    
2533     if (@{ $ns->{query_queue} } == @$queue) {
2534     if (@$queue) {
2535     $ns->send_packet ($ns->{query_queue}[0][0]);
2536     } else {
2537 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2538 root 1.95 }
2539     }
2540     },
2541 root 1.287 on_exticmd => sub {
2542     my ($ns, $buf) = @_;
2543    
2544 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2545 root 1.287
2546     if (ref $msg) {
2547     if (my $cb = $EXTICMD{$msg->{msgtype}}) {
2548     if (my %reply = $cb->($ns, $msg)) {
2549     $reply{msgid} = $msg->{msgid};
2550 root 1.290 $ns->send ("ext " . $ns->{json_coder}->encode (\%reply));
2551 root 1.287 }
2552     }
2553     } else {
2554     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2555     }
2556    
2557     cf::override;
2558     },
2559 root 1.95 );
2560    
2561 root 1.140 =item $client->async (\&cb)
2562 root 1.96
2563     Create a new coroutine, running the specified callback. The coroutine will
2564     be automatically cancelled when the client gets destroyed (e.g. on logout,
2565     or loss of connection).
2566    
2567     =cut
2568    
2569 root 1.140 sub cf::client::async {
2570 root 1.96 my ($self, $cb) = @_;
2571    
2572 root 1.140 my $coro = &Coro::async ($cb);
2573 root 1.103
2574     $coro->on_destroy (sub {
2575 root 1.96 delete $self->{_coro}{$coro+0};
2576 root 1.103 });
2577 root 1.96
2578     $self->{_coro}{$coro+0} = $coro;
2579 root 1.103
2580     $coro
2581 root 1.96 }
2582    
2583     cf::client->attach (
2584     on_destroy => sub {
2585     my ($ns) = @_;
2586    
2587 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2588 root 1.96 },
2589     );
2590    
2591 root 1.95 =back
2592    
2593 root 1.70
2594     =head2 SAFE SCRIPTING
2595    
2596     Functions that provide a safe environment to compile and execute
2597     snippets of perl code without them endangering the safety of the server
2598     itself. Looping constructs, I/O operators and other built-in functionality
2599     is not available in the safe scripting environment, and the number of
2600 root 1.79 functions and methods that can be called is greatly reduced.
2601 root 1.70
2602     =cut
2603 root 1.23
2604 root 1.42 our $safe = new Safe "safe";
2605 root 1.23 our $safe_hole = new Safe::Hole;
2606    
2607     $SIG{FPE} = 'IGNORE';
2608    
2609     $safe->permit_only (Opcode::opset qw(:base_core :base_mem :base_orig :base_math sort time));
2610    
2611 root 1.25 # here we export the classes and methods available to script code
2612    
2613 root 1.70 =pod
2614    
2615 root 1.228 The following functions and methods are available within a safe environment:
2616 root 1.70
2617 root 1.297 cf::object
2618     contr pay_amount pay_player map x y force_find force_add
2619     insert remove
2620    
2621     cf::object::player
2622     player
2623    
2624     cf::player
2625     peaceful
2626    
2627     cf::map
2628     trigger
2629 root 1.70
2630     =cut
2631    
2632 root 1.25 for (
2633 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
2634     insert remove)],
2635 root 1.25 ["cf::object::player" => qw(player)],
2636     ["cf::player" => qw(peaceful)],
2637 elmex 1.91 ["cf::map" => qw(trigger)],
2638 root 1.25 ) {
2639     no strict 'refs';
2640     my ($pkg, @funs) = @$_;
2641 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
2642 root 1.25 for @funs;
2643     }
2644 root 1.23
2645 root 1.70 =over 4
2646    
2647     =item @retval = safe_eval $code, [var => value, ...]
2648    
2649     Compiled and executes the given perl code snippet. additional var/value
2650     pairs result in temporary local (my) scalar variables of the given name
2651     that are available in the code snippet. Example:
2652    
2653     my $five = safe_eval '$first + $second', first => 1, second => 4;
2654    
2655     =cut
2656    
2657 root 1.23 sub safe_eval($;@) {
2658     my ($code, %vars) = @_;
2659    
2660     my $qcode = $code;
2661     $qcode =~ s/"/‟/g; # not allowed in #line filenames
2662     $qcode =~ s/\n/\\n/g;
2663    
2664     local $_;
2665 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
2666 root 1.23
2667 root 1.42 my $eval =
2668 root 1.23 "do {\n"
2669     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
2670     . "#line 0 \"{$qcode}\"\n"
2671     . $code
2672     . "\n}"
2673 root 1.25 ;
2674    
2675     sub_generation_inc;
2676 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
2677 root 1.25 sub_generation_inc;
2678    
2679 root 1.42 if ($@) {
2680     warn "$@";
2681     warn "while executing safe code '$code'\n";
2682     warn "with arguments " . (join " ", %vars) . "\n";
2683     }
2684    
2685 root 1.25 wantarray ? @res : $res[0]
2686 root 1.23 }
2687    
2688 root 1.69 =item cf::register_script_function $function => $cb
2689    
2690     Register a function that can be called from within map/npc scripts. The
2691     function should be reasonably secure and should be put into a package name
2692     like the extension.
2693    
2694     Example: register a function that gets called whenever a map script calls
2695     C<rent::overview>, as used by the C<rent> extension.
2696    
2697     cf::register_script_function "rent::overview" => sub {
2698     ...
2699     };
2700    
2701     =cut
2702    
2703 root 1.23 sub register_script_function {
2704     my ($fun, $cb) = @_;
2705    
2706     no strict 'refs';
2707 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
2708 root 1.23 }
2709    
2710 root 1.70 =back
2711    
2712 root 1.71 =cut
2713    
2714 root 1.23 #############################################################################
2715 root 1.203 # the server's init and main functions
2716    
2717 root 1.246 sub load_facedata($) {
2718     my ($path) = @_;
2719 root 1.223
2720 root 1.229 warn "loading facedata from $path\n";
2721 root 1.223
2722 root 1.236 my $facedata;
2723     0 < aio_load $path, $facedata
2724 root 1.223 or die "$path: $!";
2725    
2726 root 1.237 $facedata = Coro::Storable::thaw $facedata;
2727 root 1.223
2728 root 1.236 $facedata->{version} == 2
2729 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
2730    
2731 root 1.236 {
2732     my $faces = $facedata->{faceinfo};
2733    
2734     while (my ($face, $info) = each %$faces) {
2735     my $idx = (cf::face::find $face) || cf::face::alloc $face;
2736 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
2737     cf::face::set_magicmap $idx, $info->{magicmap};
2738 root 1.236 cf::face::set_data $idx, 0, $info->{data32}, $info->{chksum32};
2739     cf::face::set_data $idx, 1, $info->{data64}, $info->{chksum64};
2740 root 1.302
2741     cf::cede_to_tick;
2742 root 1.236 }
2743    
2744     while (my ($face, $info) = each %$faces) {
2745     next unless $info->{smooth};
2746     my $idx = cf::face::find $face
2747     or next;
2748     if (my $smooth = cf::face::find $info->{smooth}) {
2749 root 1.302 cf::face::set_smooth $idx, $smooth;
2750     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
2751 root 1.236 } else {
2752     warn "smooth face '$info->{smooth}' not found for face '$face'";
2753     }
2754 root 1.302
2755     cf::cede_to_tick;
2756 root 1.236 }
2757 root 1.223 }
2758    
2759 root 1.236 {
2760     my $anims = $facedata->{animinfo};
2761    
2762     while (my ($anim, $info) = each %$anims) {
2763     cf::anim::set $anim, $info->{frames}, $info->{facings};
2764 root 1.302 cf::cede_to_tick;
2765 root 1.225 }
2766 root 1.236
2767     cf::anim::invalidate_all; # d'oh
2768 root 1.225 }
2769    
2770 root 1.302 {
2771     # TODO: for gcfclient pleasure, we should give resources
2772     # that gcfclient doesn't grok a >10000 face index.
2773     my $res = $facedata->{resource};
2774     my $enc = JSON::XS->new->utf8->canonical;
2775    
2776     while (my ($name, $info) = each %$res) {
2777     my $meta = $enc->encode ({
2778     name => $name,
2779     type => $info->{type},
2780 root 1.303 copyright => $info->{copyright}, #TODO#
2781 root 1.302 });
2782    
2783     my $idx = (cf::face::find $name) || cf::face::alloc $name;
2784 root 1.307
2785     if ($name =~ /\.jpg$/) {
2786     cf::face::set_data $idx, 0, $info->{data}, $info->{chksum};#d# temp hack
2787     cf::face::set_data $idx, 1, $info->{data}, $info->{chksum};#d# temp hack
2788     } else {
2789     my $data = pack "(w/a*)*", $meta, $info->{data};
2790     my $chk = Digest::MD5::md5 "$info->{chksum},$meta"; # mangle data checksum and metadata
2791    
2792     cf::face::set_type $idx, 1;
2793     cf::face::set_data $idx, 0, $data, $chk;
2794     }
2795 root 1.302
2796     cf::cede_to_tick;
2797     }
2798     }
2799    
2800 root 1.223 1
2801     }
2802    
2803 root 1.253 sub reload_regions {
2804     load_resource_file "$MAPDIR/regions"
2805     or die "unable to load regions file\n";
2806 root 1.304
2807     for (cf::region::list) {
2808     $_->{match} = qr/$_->{match}/
2809     if exists $_->{match};
2810     }
2811 root 1.253 }
2812    
2813 root 1.246 sub reload_facedata {
2814 root 1.253 load_facedata "$DATADIR/facedata"
2815 root 1.246 or die "unable to load facedata\n";
2816     }
2817    
2818     sub reload_archetypes {
2819 root 1.253 load_resource_file "$DATADIR/archetypes"
2820 root 1.246 or die "unable to load archetypes\n";
2821 root 1.289 #d# NEED to laod twice to resolve forward references
2822     # this really needs to be done in an extra post-pass
2823     # (which needs to be synchronous, so solve it differently)
2824     load_resource_file "$DATADIR/archetypes"
2825     or die "unable to load archetypes\n";
2826 root 1.241 }
2827    
2828 root 1.246 sub reload_treasures {
2829 root 1.253 load_resource_file "$DATADIR/treasures"
2830 root 1.246 or die "unable to load treasurelists\n";
2831 root 1.241 }
2832    
2833 root 1.223 sub reload_resources {
2834 root 1.245 warn "reloading resource files...\n";
2835    
2836 root 1.246 reload_regions;
2837     reload_facedata;
2838 root 1.274 #reload_archetypes;#d#
2839 root 1.246 reload_archetypes;
2840     reload_treasures;
2841 root 1.245
2842     warn "finished reloading resource files\n";
2843 root 1.223 }
2844    
2845     sub init {
2846     reload_resources;
2847 root 1.203 }
2848 root 1.34
2849 root 1.73 sub cfg_load {
2850 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
2851 root 1.72 or return;
2852    
2853     local $/;
2854     *CFG = YAML::Syck::Load <$fh>;
2855 root 1.131
2856     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
2857    
2858 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
2859     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
2860    
2861 root 1.131 if (exists $CFG{mlockall}) {
2862     eval {
2863 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
2864 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
2865     };
2866     warn $@ if $@;
2867     }
2868 root 1.72 }
2869    
2870 root 1.39 sub main {
2871 root 1.108 # we must not ever block the main coroutine
2872     local $Coro::idle = sub {
2873 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
2874 root 1.175 (async {
2875     Event::one_event;
2876     })->prio (Coro::PRIO_MAX);
2877 root 1.108 };
2878    
2879 root 1.73 cfg_load;
2880 root 1.210 db_init;
2881 root 1.61 load_extensions;
2882 root 1.183
2883     $TICK_WATCHER->start;
2884 root 1.34 Event::loop;
2885     }
2886    
2887     #############################################################################
2888 root 1.155 # initialisation and cleanup
2889    
2890     # install some emergency cleanup handlers
2891     BEGIN {
2892     for my $signal (qw(INT HUP TERM)) {
2893     Event->signal (
2894 root 1.189 reentrant => 0,
2895     data => WF_AUTOCANCEL,
2896     signal => $signal,
2897 root 1.191 prio => 0,
2898 root 1.189 cb => sub {
2899 root 1.155 cf::cleanup "SIG$signal";
2900     },
2901     );
2902     }
2903     }
2904    
2905 root 1.281 sub write_runtime {
2906     my $runtime = "$LOCALDIR/runtime";
2907    
2908     # first touch the runtime file to show we are still running:
2909     # the fsync below can take a very very long time.
2910    
2911     IO::AIO::aio_utime $runtime, undef, undef;
2912    
2913     my $guard = cf::lock_acquire "write_runtime";
2914    
2915     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
2916     or return;
2917    
2918     my $value = $cf::RUNTIME + 90 + 10;
2919     # 10 is the runtime save interval, for a monotonic clock
2920     # 60 allows for the watchdog to kill the server.
2921    
2922     (aio_write $fh, 0, (length $value), $value, 0) <= 0
2923     and return;
2924    
2925     # always fsync - this file is important
2926     aio_fsync $fh
2927     and return;
2928    
2929     # touch it again to show we are up-to-date
2930     aio_utime $fh, undef, undef;
2931    
2932     close $fh
2933     or return;
2934    
2935     aio_rename "$runtime~", $runtime
2936     and return;
2937    
2938     warn "runtime file written.\n";
2939    
2940     1
2941     }
2942    
2943 root 1.156 sub emergency_save() {
2944 root 1.155 my $freeze_guard = cf::freeze_mainloop;
2945    
2946     warn "enter emergency perl save\n";
2947    
2948     cf::sync_job {
2949     # use a peculiar iteration method to avoid tripping on perl
2950     # refcount bugs in for. also avoids problems with players
2951 root 1.167 # and maps saved/destroyed asynchronously.
2952 root 1.155 warn "begin emergency player save\n";
2953     for my $login (keys %cf::PLAYER) {
2954     my $pl = $cf::PLAYER{$login} or next;
2955     $pl->valid or next;
2956     $pl->save;
2957     }
2958     warn "end emergency player save\n";
2959    
2960     warn "begin emergency map save\n";
2961     for my $path (keys %cf::MAP) {
2962     my $map = $cf::MAP{$path} or next;
2963     $map->valid or next;
2964     $map->save;
2965     }
2966     warn "end emergency map save\n";
2967 root 1.208
2968     warn "begin emergency database checkpoint\n";
2969     BDB::db_env_txn_checkpoint $DB_ENV;
2970     warn "end emergency database checkpoint\n";
2971 root 1.155 };
2972    
2973     warn "leave emergency perl save\n";
2974     }
2975 root 1.22
2976 root 1.211 sub post_cleanup {
2977     my ($make_core) = @_;
2978    
2979     warn Carp::longmess "post_cleanup backtrace"
2980     if $make_core;
2981     }
2982    
2983 root 1.246 sub do_reload_perl() {
2984 root 1.106 # can/must only be called in main
2985     if ($Coro::current != $Coro::main) {
2986 root 1.183 warn "can only reload from main coroutine";
2987 root 1.106 return;
2988     }
2989    
2990 root 1.103 warn "reloading...";
2991    
2992 root 1.212 warn "entering sync_job";
2993    
2994 root 1.213 cf::sync_job {
2995 root 1.214 cf::write_runtime; # external watchdog should not bark
2996 root 1.212 cf::emergency_save;
2997 root 1.214 cf::write_runtime; # external watchdog should not bark
2998 root 1.183
2999 root 1.212 warn "syncing database to disk";
3000     BDB::db_env_txn_checkpoint $DB_ENV;
3001 root 1.106
3002     # if anything goes wrong in here, we should simply crash as we already saved
3003 root 1.65
3004 root 1.183 warn "cancelling all WF_AUTOCANCEL watchers";
3005 root 1.87 for (Event::all_watchers) {
3006     $_->cancel if $_->data & WF_AUTOCANCEL;
3007     }
3008 root 1.65
3009 root 1.183 warn "flushing outstanding aio requests";
3010     for (;;) {
3011 root 1.208 BDB::flush;
3012 root 1.183 IO::AIO::flush;
3013     Coro::cede;
3014 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
3015 root 1.183 warn "iterate...";
3016     }
3017    
3018 root 1.223 ++$RELOAD;
3019    
3020 root 1.183 warn "cancelling all extension coros";
3021 root 1.103 $_->cancel for values %EXT_CORO;
3022     %EXT_CORO = ();
3023    
3024 root 1.183 warn "removing commands";
3025 root 1.159 %COMMAND = ();
3026    
3027 root 1.287 warn "removing ext/exti commands";
3028     %EXTCMD = ();
3029     %EXTICMD = ();
3030 root 1.159
3031 root 1.183 warn "unloading/nuking all extensions";
3032 root 1.159 for my $pkg (@EXTS) {
3033 root 1.160 warn "... unloading $pkg";
3034 root 1.159
3035     if (my $cb = $pkg->can ("unload")) {
3036     eval {
3037     $cb->($pkg);
3038     1
3039     } or warn "$pkg unloaded, but with errors: $@";
3040     }
3041    
3042 root 1.160 warn "... nuking $pkg";
3043 root 1.159 Symbol::delete_package $pkg;
3044 root 1.65 }
3045    
3046 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
3047 root 1.65 while (my ($k, $v) = each %INC) {
3048     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3049    
3050 root 1.183 warn "... unloading $k";
3051 root 1.65 delete $INC{$k};
3052    
3053     $k =~ s/\.pm$//;
3054     $k =~ s/\//::/g;
3055    
3056     if (my $cb = $k->can ("unload_module")) {
3057     $cb->();
3058     }
3059    
3060     Symbol::delete_package $k;
3061     }
3062    
3063 root 1.183 warn "getting rid of safe::, as good as possible";
3064 root 1.65 Symbol::delete_package "safe::$_"
3065 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3066 root 1.65
3067 root 1.183 warn "unloading cf.pm \"a bit\"";
3068 root 1.65 delete $INC{"cf.pm"};
3069 root 1.252 delete $INC{"cf/pod.pm"};
3070 root 1.65
3071     # don't, removes xs symbols, too,
3072     # and global variables created in xs
3073     #Symbol::delete_package __PACKAGE__;
3074    
3075 root 1.183 warn "unload completed, starting to reload now";
3076    
3077 root 1.103 warn "reloading cf.pm";
3078 root 1.65 require cf;
3079 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3080    
3081 root 1.183 warn "loading config and database again";
3082 root 1.73 cf::cfg_load;
3083 root 1.65
3084 root 1.183 warn "loading extensions";
3085 root 1.65 cf::load_extensions;
3086    
3087 root 1.183 warn "reattaching attachments to objects/players";
3088 root 1.222 _global_reattach; # objects, sockets
3089 root 1.183 warn "reattaching attachments to maps";
3090 root 1.144 reattach $_ for values %MAP;
3091 root 1.222 warn "reattaching attachments to players";
3092     reattach $_ for values %PLAYER;
3093 root 1.183
3094 root 1.212 warn "leaving sync_job";
3095 root 1.183
3096 root 1.212 1
3097     } or do {
3098 root 1.106 warn $@;
3099     warn "error while reloading, exiting.";
3100     exit 1;
3101 root 1.212 };
3102 root 1.106
3103 root 1.159 warn "reloaded";
3104 root 1.65 };
3105    
3106 root 1.175 our $RELOAD_WATCHER; # used only during reload
3107    
3108 root 1.246 sub reload_perl() {
3109     # doing reload synchronously and two reloads happen back-to-back,
3110     # coro crashes during coro_state_free->destroy here.
3111    
3112     $RELOAD_WATCHER ||= Event->timer (
3113     reentrant => 0,
3114     after => 0,
3115     data => WF_AUTOCANCEL,
3116     cb => sub {
3117     do_reload_perl;
3118     undef $RELOAD_WATCHER;
3119     },
3120     );
3121     }
3122    
3123 root 1.111 register_command "reload" => sub {
3124 root 1.65 my ($who, $arg) = @_;
3125    
3126     if ($who->flag (FLAG_WIZ)) {
3127 root 1.175 $who->message ("reloading server.");
3128 root 1.246 async { reload_perl };
3129 root 1.65 }
3130     };
3131    
3132 root 1.27 unshift @INC, $LIBDIR;
3133 root 1.17
3134 root 1.183 my $bug_warning = 0;
3135    
3136 root 1.239 our @WAIT_FOR_TICK;
3137     our @WAIT_FOR_TICK_BEGIN;
3138    
3139     sub wait_for_tick {
3140 root 1.240 return unless $TICK_WATCHER->is_active;
3141 root 1.241 return if $Coro::current == $Coro::main;
3142    
3143 root 1.239 my $signal = new Coro::Signal;
3144     push @WAIT_FOR_TICK, $signal;
3145     $signal->wait;
3146     }
3147    
3148     sub wait_for_tick_begin {
3149 root 1.240 return unless $TICK_WATCHER->is_active;
3150 root 1.241 return if $Coro::current == $Coro::main;
3151    
3152 root 1.239 my $signal = new Coro::Signal;
3153     push @WAIT_FOR_TICK_BEGIN, $signal;
3154     $signal->wait;
3155     }
3156    
3157 root 1.268 my $min = 1e6;#d#
3158     my $avg = 10;
3159 root 1.35 $TICK_WATCHER = Event->timer (
3160 root 1.104 reentrant => 0,
3161 root 1.183 parked => 1,
3162 root 1.191 prio => 0,
3163 root 1.104 at => $NEXT_TICK || $TICK,
3164     data => WF_AUTOCANCEL,
3165     cb => sub {
3166 root 1.183 if ($Coro::current != $Coro::main) {
3167     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3168     unless ++$bug_warning > 10;
3169     return;
3170     }
3171    
3172 root 1.265 $NOW = $tick_start = Event::time;
3173 root 1.163
3174 root 1.133 cf::server_tick; # one server iteration
3175 root 1.245
3176 root 1.268 0 && sync_job {#d#
3177     for(1..10) {
3178     my $t = Event::time;
3179     my $map = my $map = new_from_path cf::map "/tmp/x.map"
3180     or die;
3181    
3182     $map->width (50);
3183     $map->height (50);
3184     $map->alloc;
3185     $map->_load_objects ("/tmp/x.map", 1);
3186     my $t = Event::time - $t;
3187    
3188     #next unless $t < 0.0013;#d#
3189     if ($t < $min) {
3190     $min = $t;
3191     }
3192     $avg = $avg * 0.99 + $t * 0.01;
3193     }
3194     warn "XXXXXXXXXXXXXXXXXX min $min avg $avg\n";#d#
3195     exit 0;
3196     # 2007-05-22 02:33:04.569 min 0.00112509727478027 avg 0.0012259249572477
3197     };
3198    
3199 root 1.133 $RUNTIME += $TICK;
3200 root 1.35 $NEXT_TICK += $TICK;
3201    
3202 root 1.214 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3203     $NEXT_RUNTIME_WRITE = $NOW + 10;
3204     Coro::async_pool {
3205     write_runtime
3206     or warn "ERROR: unable to write runtime file: $!";
3207     };
3208     }
3209    
3210 root 1.191 # my $AFTER = Event::time;
3211     # warn $AFTER - $NOW;#d#
3212 root 1.190
3213 root 1.245 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3214     $sig->send;
3215     }
3216     while (my $sig = shift @WAIT_FOR_TICK) {
3217     $sig->send;
3218     }
3219    
3220 root 1.265 $NOW = Event::time;
3221    
3222     # if we are delayed by four ticks or more, skip them all
3223     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
3224    
3225     $TICK_WATCHER->at ($NEXT_TICK);
3226     $TICK_WATCHER->start;
3227    
3228     $LOAD = ($NOW - $tick_start) / $TICK;
3229     $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3230    
3231 root 1.245 _post_tick;
3232 root 1.265
3233    
3234 root 1.35 },
3235     );
3236    
3237 root 1.206 {
3238     BDB::max_poll_time $TICK * 0.1;
3239     $BDB_POLL_WATCHER = Event->io (
3240     reentrant => 0,
3241     fd => BDB::poll_fileno,
3242     poll => 'r',
3243     prio => 0,
3244     data => WF_AUTOCANCEL,
3245     cb => \&BDB::poll_cb,
3246     );
3247     BDB::min_parallel 8;
3248    
3249     BDB::set_sync_prepare {
3250     my $status;
3251     my $current = $Coro::current;
3252     (
3253     sub {
3254     $status = $!;
3255     $current->ready; undef $current;
3256     },
3257     sub {
3258     Coro::schedule while defined $current;
3259     $! = $status;
3260     },
3261     )
3262     };
3263 root 1.77
3264 root 1.206 unless ($DB_ENV) {
3265     $DB_ENV = BDB::db_env_create;
3266    
3267     cf::sync_job {
3268 root 1.208 eval {
3269     BDB::db_env_open
3270     $DB_ENV,
3271 root 1.253 $BDBDIR,
3272 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3273     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3274     0666;
3275    
3276 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3277 root 1.208
3278     $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC, 1);
3279     $DB_ENV->set_lk_detect;
3280     };
3281    
3282     cf::cleanup "db_env_open(db): $@" if $@;
3283 root 1.206 };
3284     }
3285     }
3286    
3287     {
3288     IO::AIO::min_parallel 8;
3289    
3290     undef $Coro::AIO::WATCHER;
3291     IO::AIO::max_poll_time $TICK * 0.1;
3292     $AIO_POLL_WATCHER = Event->io (
3293     reentrant => 0,
3294 root 1.214 data => WF_AUTOCANCEL,
3295 root 1.206 fd => IO::AIO::poll_fileno,
3296     poll => 'r',
3297     prio => 6,
3298     cb => \&IO::AIO::poll_cb,
3299     );
3300     }
3301 root 1.108
3302 root 1.262 my $_log_backtrace;
3303    
3304 root 1.260 sub _log_backtrace {
3305     my ($msg, @addr) = @_;
3306    
3307 root 1.262 $msg =~ s/\n//;
3308 root 1.260
3309 root 1.262 # limit the # of concurrent backtraces
3310     if ($_log_backtrace < 2) {
3311     ++$_log_backtrace;
3312     async {
3313     my @bt = fork_call {
3314     @addr = map { sprintf "%x", $_ } @addr;
3315     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3316     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3317     or die "addr2line: $!";
3318    
3319     my @funcs;
3320     my @res = <$fh>;
3321     chomp for @res;
3322     while (@res) {
3323     my ($func, $line) = splice @res, 0, 2, ();
3324     push @funcs, "[$func] $line";
3325     }
3326 root 1.260
3327 root 1.262 @funcs
3328     };
3329 root 1.260
3330 root 1.262 LOG llevInfo, "[ABT] $msg\n";
3331     LOG llevInfo, "[ABT] $_\n" for @bt;
3332     --$_log_backtrace;
3333     };
3334     } else {
3335 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3336 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3337     }
3338 root 1.260 }
3339    
3340 root 1.249 # load additional modules
3341     use cf::pod;
3342    
3343 root 1.125 END { cf::emergency_save }
3344    
3345 root 1.1 1
3346