ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.286
Committed: Sun Jun 24 04:27:20 2007 UTC (16 years, 11 months ago) by root
Branch: MAIN
Changes since 1.285: +1 -1 lines
Log Message:
*** empty log message ***

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