ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.298
Committed: Sun Jul 8 14:50:07 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.297: +15 -2 lines
Log Message:
temporary hack

File Contents

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