ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.391
Committed: Fri Oct 26 04:22:05 2007 UTC (16 years, 7 months ago) by root
Branch: MAIN
Changes since 1.390: +76 -36 lines
Log Message:
much improved cfpod to xml converter

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