ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.287
Committed: Mon Jun 25 05:43:45 2007 UTC (16 years, 11 months ago) by root
Branch: MAIN
Changes since 1.286: +57 -8 lines
Log Message:
first round of implementing server-side widgets. the framework is there, but hasn't been used for anything realistic yet, so likely not yet fully usable

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