ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.295
Committed: Wed Jul 4 05:08:15 2007 UTC (16 years, 10 months ago) by root
Branch: MAIN
Changes since 1.294: +4 -4 lines
Log Message:
*** empty log message ***

File Contents

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