ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.393
Committed: Fri Oct 26 04:47:00 2007 UTC (16 years, 7 months ago) by root
Branch: MAIN
CVS Tags: rel-2_3
Changes since 1.392: +4 -1 lines
Log Message:
minorfix

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.393 use re 'eval';
1554 root 1.391
1555     my $group;
1556     my $interior; $interior = qr{
1557 root 1.393 # match a pod interior sequence sans C<< >>
1558 root 1.391 (?:
1559     \ (.*?)\ (?{ $group = $^N })
1560     | < (??{$interior}) >
1561     )
1562     }x;
1563    
1564 root 1.283 sub expand_cfpod {
1565 root 1.391 my ($self, $pod) = @_;
1566    
1567     my $xml;
1568 root 1.283
1569 root 1.391 while () {
1570 root 1.392 if ($pod =~ /\G( (?: [^BCGHITU]+ | .(?!<) )+ )/xgcs) {
1571 root 1.391 $group = $1;
1572    
1573     $group =~ s/&/&amp;/g;
1574     $group =~ s/</&lt;/g;
1575    
1576     $xml .= $group;
1577     } elsif ($pod =~ m%\G
1578     ([BCGHITU])
1579     <
1580     (?:
1581     ([^<>]*) (?{ $group = $^N })
1582     | < $interior >
1583     )
1584     >
1585     %gcsx
1586     ) {
1587     my ($code, $data) = ($1, $group);
1588    
1589     if ($code eq "B") {
1590     $xml .= "<b>" . expand_cfpod ($self, $data) . "</b>";
1591     } elsif ($code eq "I") {
1592     $xml .= "<i>" . expand_cfpod ($self, $data) . "</i>";
1593     } elsif ($code eq "U") {
1594     $xml .= "<u>" . expand_cfpod ($self, $data) . "</u>";
1595     } elsif ($code eq "C") {
1596     $xml .= "<tt>" . expand_cfpod ($self, $data) . "</tt>";
1597     } elsif ($code eq "T") {
1598     $xml .= "<big><b>" . expand_cfpod ($self, $data) . "</b></big>";
1599     } elsif ($code eq "G") {
1600     my ($male, $female) = split /\|/, $data;
1601     $data = $self->gender ? $female : $male;
1602     $xml .= expand_cfpod ($self, $data);
1603     } elsif ($code eq "H") {
1604     $xml .= ("<fg name=\"lightblue\">[" . expand_cfpod ($self, $data) . " (Use hintmode to suppress hints)]</fg>",
1605     "<fg name=\"lightblue\">[Hint suppressed, see hintmode]</fg>",
1606     "")
1607     [$self->{hintmode}];
1608     } else {
1609     $xml .= "error processing '$code($data)' directive";
1610     }
1611     } else {
1612     if ($pod =~ /\G(.+)/) {
1613     warn "parse error while expanding $pod (at $1)";
1614     }
1615     last;
1616     }
1617     }
1618    
1619     for ($xml) {
1620     # create single paragraphs (very hackish)
1621     s/(?<=\S)\n(?=\w)/ /g;
1622    
1623     # compress some whitespace
1624     s/\s+\n/\n/g; # ws line-ends
1625     s/\n\n+/\n/g; # double lines
1626     s/^\n+//; # beginning lines
1627     s/\n+$//; # ending lines
1628     }
1629 root 1.293
1630 root 1.391 $xml
1631 root 1.283 }
1632    
1633 root 1.393 no re 'eval';
1634    
1635 root 1.291 sub hintmode {
1636     $_[0]{hintmode} = $_[1] if @_ > 1;
1637     $_[0]{hintmode}
1638     }
1639    
1640 root 1.316 =item $player->ext_reply ($msgid, @msg)
1641 root 1.95
1642     Sends an ext reply to the player.
1643    
1644     =cut
1645    
1646 root 1.316 sub ext_reply($$@) {
1647     my ($self, $id, @msg) = @_;
1648 root 1.95
1649 root 1.336 $self->ns->ext_reply ($id, @msg)
1650 root 1.95 }
1651    
1652 root 1.316 =item $player->ext_msg ($type, @msg)
1653 root 1.231
1654     Sends an ext event to the client.
1655    
1656     =cut
1657    
1658 root 1.316 sub ext_msg($$@) {
1659     my ($self, $type, @msg) = @_;
1660 root 1.231
1661 root 1.316 $self->ns->ext_msg ($type, @msg);
1662 root 1.231 }
1663    
1664 root 1.238 =head3 cf::region
1665    
1666     =over 4
1667    
1668     =cut
1669    
1670     package cf::region;
1671    
1672     =item cf::region::find_by_path $path
1673    
1674 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1675 root 1.238
1676     =cut
1677    
1678     sub find_by_path($) {
1679     my ($path) = @_;
1680    
1681     my ($match, $specificity);
1682    
1683     for my $region (list) {
1684 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1685 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1686     if $region->specificity > $specificity;
1687     }
1688     }
1689    
1690     $match
1691     }
1692 root 1.143
1693 root 1.95 =back
1694    
1695 root 1.110 =head3 cf::map
1696    
1697     =over 4
1698    
1699     =cut
1700    
1701     package cf::map;
1702    
1703     use Fcntl;
1704     use Coro::AIO;
1705    
1706 root 1.166 use overload
1707 root 1.173 '""' => \&as_string,
1708     fallback => 1;
1709 root 1.166
1710 root 1.133 our $MAX_RESET = 3600;
1711     our $DEFAULT_RESET = 3000;
1712 root 1.110
1713     sub generate_random_map {
1714 root 1.166 my ($self, $rmp) = @_;
1715 root 1.110 # mit "rum" bekleckern, nicht
1716 root 1.166 $self->_create_random_map (
1717 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1718     $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle},
1719     $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1720     $rmp->{exit_on_final_map},
1721     $rmp->{xsize}, $rmp->{ysize},
1722     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1723     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1724     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1725     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1726     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1727 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1728     )
1729 root 1.110 }
1730    
1731 root 1.187 =item cf::map->register ($regex, $prio)
1732    
1733     Register a handler for the map path matching the given regex at the
1734     givne priority (higher is better, built-in handlers have priority 0, the
1735     default).
1736    
1737     =cut
1738    
1739 root 1.166 sub register {
1740 root 1.187 my (undef, $regex, $prio) = @_;
1741 root 1.166 my $pkg = caller;
1742    
1743     no strict;
1744     push @{"$pkg\::ISA"}, __PACKAGE__;
1745    
1746 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1747 root 1.166 }
1748    
1749     # also paths starting with '/'
1750 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1751 root 1.166
1752 root 1.170 sub thawer_merge {
1753 root 1.172 my ($self, $merge) = @_;
1754    
1755 root 1.170 # we have to keep some variables in memory intact
1756 root 1.172 local $self->{path};
1757     local $self->{load_path};
1758 root 1.170
1759 root 1.172 $self->SUPER::thawer_merge ($merge);
1760 root 1.170 }
1761    
1762 root 1.166 sub normalise {
1763     my ($path, $base) = @_;
1764    
1765 root 1.192 $path = "$path"; # make sure its a string
1766    
1767 root 1.199 $path =~ s/\.map$//;
1768    
1769 root 1.166 # map plan:
1770     #
1771     # /! non-realised random map exit (special hack!)
1772     # {... are special paths that are not being touched
1773     # ?xxx/... are special absolute paths
1774     # ?random/... random maps
1775     # /... normal maps
1776     # ~user/... per-player map of a specific user
1777    
1778     $path =~ s/$PATH_SEP/\//go;
1779    
1780     # treat it as relative path if it starts with
1781     # something that looks reasonable
1782     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1783     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1784    
1785     $base =~ s{[^/]+/?$}{};
1786     $path = "$base/$path";
1787     }
1788    
1789     for ($path) {
1790     redo if s{//}{/};
1791     redo if s{/\.?/}{/};
1792     redo if s{/[^/]+/\.\./}{/};
1793     }
1794    
1795     $path
1796     }
1797    
1798     sub new_from_path {
1799     my (undef, $path, $base) = @_;
1800    
1801     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1802    
1803     $path = normalise $path, $base;
1804    
1805 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1806     if ($path =~ $EXT_MAP{$pkg}[1]) {
1807 root 1.166 my $self = bless cf::map::new, $pkg;
1808     $self->{path} = $path; $self->path ($path);
1809     $self->init; # pass $1 etc.
1810     return $self;
1811     }
1812     }
1813    
1814 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1815 root 1.166 ()
1816     }
1817    
1818     sub init {
1819     my ($self) = @_;
1820    
1821     $self
1822     }
1823    
1824     sub as_string {
1825     my ($self) = @_;
1826    
1827     "$self->{path}"
1828     }
1829    
1830     # the displayed name, this is a one way mapping
1831     sub visible_name {
1832     &as_string
1833     }
1834    
1835     # the original (read-only) location
1836     sub load_path {
1837     my ($self) = @_;
1838    
1839 root 1.254 "$MAPDIR/$self->{path}.map"
1840 root 1.166 }
1841    
1842     # the temporary/swap location
1843     sub save_path {
1844     my ($self) = @_;
1845    
1846 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1847 root 1.254 "$TMPDIR/$path.map"
1848 root 1.166 }
1849    
1850     # the unique path, undef == no special unique path
1851     sub uniq_path {
1852     my ($self) = @_;
1853    
1854 root 1.167 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/g;
1855 root 1.253 "$UNIQUEDIR/$path"
1856 root 1.166 }
1857    
1858 root 1.110 # and all this just because we cannot iterate over
1859     # all maps in C++...
1860     sub change_all_map_light {
1861     my ($change) = @_;
1862    
1863 root 1.122 $_->change_map_light ($change)
1864     for grep $_->outdoor, values %cf::MAP;
1865 root 1.110 }
1866    
1867 root 1.275 sub decay_objects {
1868     my ($self) = @_;
1869    
1870     return if $self->{deny_reset};
1871    
1872     $self->do_decay_objects;
1873     }
1874    
1875 root 1.166 sub unlink_save {
1876     my ($self) = @_;
1877    
1878     utf8::encode (my $save = $self->save_path);
1879 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1880     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1881 root 1.166 }
1882    
1883     sub load_header_from($) {
1884     my ($self, $path) = @_;
1885 root 1.110
1886     utf8::encode $path;
1887 root 1.356 my $f = new_from_file cf::object::thawer $path
1888     or return;
1889 root 1.110
1890 root 1.356 $self->_load_header ($f)
1891 root 1.110 or return;
1892    
1893 root 1.356 local $MAP_LOADING{$self->{path}} = $self;
1894     $f->resolve_delayed_derefs;
1895    
1896 root 1.166 $self->{load_path} = $path;
1897 root 1.135
1898 root 1.166 1
1899     }
1900 root 1.110
1901 root 1.188 sub load_header_orig {
1902 root 1.166 my ($self) = @_;
1903 root 1.110
1904 root 1.166 $self->load_header_from ($self->load_path)
1905 root 1.110 }
1906    
1907 root 1.188 sub load_header_temp {
1908 root 1.166 my ($self) = @_;
1909 root 1.110
1910 root 1.166 $self->load_header_from ($self->save_path)
1911     }
1912 root 1.110
1913 root 1.188 sub prepare_temp {
1914     my ($self) = @_;
1915    
1916     $self->last_access ((delete $self->{last_access})
1917     || $cf::RUNTIME); #d#
1918     # safety
1919     $self->{instantiate_time} = $cf::RUNTIME
1920     if $self->{instantiate_time} > $cf::RUNTIME;
1921     }
1922    
1923     sub prepare_orig {
1924     my ($self) = @_;
1925    
1926     $self->{load_original} = 1;
1927     $self->{instantiate_time} = $cf::RUNTIME;
1928     $self->last_access ($cf::RUNTIME);
1929     $self->instantiate;
1930     }
1931    
1932 root 1.166 sub load_header {
1933     my ($self) = @_;
1934 root 1.110
1935 root 1.188 if ($self->load_header_temp) {
1936     $self->prepare_temp;
1937 root 1.166 } else {
1938 root 1.188 $self->load_header_orig
1939 root 1.166 or return;
1940 root 1.188 $self->prepare_orig;
1941 root 1.166 }
1942 root 1.120
1943 root 1.275 $self->{deny_reset} = 1
1944     if $self->no_reset;
1945    
1946 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
1947     unless $self->default_region;
1948    
1949 root 1.166 1
1950     }
1951 root 1.110
1952 root 1.166 sub find;
1953     sub find {
1954     my ($path, $origin) = @_;
1955 root 1.134
1956 root 1.166 $path = normalise $path, $origin && $origin->path;
1957 root 1.110
1958 root 1.358 cf::lock_wait "map_data:$path";#d#remove
1959 root 1.166 cf::lock_wait "map_find:$path";
1960 root 1.110
1961 root 1.166 $cf::MAP{$path} || do {
1962 root 1.358 my $guard1 = cf::lock_acquire "map_find:$path";
1963     my $guard2 = cf::lock_acquire "map_data:$path"; # just for the fun of it
1964    
1965 root 1.166 my $map = new_from_path cf::map $path
1966     or return;
1967 root 1.110
1968 root 1.116 $map->{last_save} = $cf::RUNTIME;
1969 root 1.110
1970 root 1.166 $map->load_header
1971     or return;
1972 root 1.134
1973 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
1974 root 1.185 # doing this can freeze the server in a sync job, obviously
1975     #$cf::WAIT_FOR_TICK->wait;
1976 root 1.358 undef $guard1;
1977     undef $guard2;
1978 root 1.112 $map->reset;
1979 root 1.192 return find $path;
1980 root 1.112 }
1981 root 1.110
1982 root 1.166 $cf::MAP{$path} = $map
1983 root 1.110 }
1984     }
1985    
1986 root 1.188 sub pre_load { }
1987     sub post_load { }
1988    
1989 root 1.110 sub load {
1990     my ($self) = @_;
1991    
1992 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
1993    
1994 root 1.120 my $path = $self->{path};
1995    
1996 root 1.256 {
1997 root 1.357 my $guard = cf::lock_acquire "map_data:$path";
1998 root 1.256
1999 root 1.357 return unless $self->valid;
2000 root 1.360 return unless $self->in_memory == cf::MAP_SWAPPED;
2001 root 1.110
2002 root 1.256 $self->in_memory (cf::MAP_LOADING);
2003 root 1.110
2004 root 1.256 $self->alloc;
2005 root 1.188
2006 root 1.256 $self->pre_load;
2007 root 1.346 cf::cede_to_tick;
2008 root 1.188
2009 root 1.356 my $f = new_from_file cf::object::thawer $self->{load_path};
2010     $f->skip_block;
2011     $self->_load_objects ($f)
2012 root 1.256 or return;
2013 root 1.110
2014 root 1.256 $self->set_object_flag (cf::FLAG_OBJ_ORIGINAL, 1)
2015     if delete $self->{load_original};
2016 root 1.111
2017 root 1.256 if (my $uniq = $self->uniq_path) {
2018     utf8::encode $uniq;
2019 root 1.356 unless (aio_stat $uniq) {
2020     if (my $f = new_from_file cf::object::thawer $uniq) {
2021     $self->clear_unique_items;
2022     $self->_load_objects ($f);
2023     $f->resolve_delayed_derefs;
2024     }
2025 root 1.256 }
2026 root 1.110 }
2027    
2028 root 1.356 $f->resolve_delayed_derefs;
2029    
2030 root 1.346 cf::cede_to_tick;
2031 root 1.256 # now do the right thing for maps
2032     $self->link_multipart_objects;
2033 root 1.110 $self->difficulty ($self->estimate_difficulty)
2034     unless $self->difficulty;
2035 root 1.346 cf::cede_to_tick;
2036 root 1.256
2037     unless ($self->{deny_activate}) {
2038     $self->decay_objects;
2039     $self->fix_auto_apply;
2040     $self->update_buttons;
2041 root 1.346 cf::cede_to_tick;
2042 root 1.256 $self->set_darkness_map;
2043 root 1.346 cf::cede_to_tick;
2044 root 1.256 $self->activate;
2045     }
2046    
2047 root 1.325 $self->{last_save} = $cf::RUNTIME;
2048     $self->last_access ($cf::RUNTIME);
2049 root 1.324
2050 root 1.256 $self->in_memory (cf::MAP_IN_MEMORY);
2051 root 1.110 }
2052    
2053 root 1.188 $self->post_load;
2054 root 1.166 }
2055    
2056     sub customise_for {
2057     my ($self, $ob) = @_;
2058    
2059     return find "~" . $ob->name . "/" . $self->{path}
2060     if $self->per_player;
2061 root 1.134
2062 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
2063     # if $self->per_party;
2064    
2065 root 1.166 $self
2066 root 1.110 }
2067    
2068 root 1.157 # find and load all maps in the 3x3 area around a map
2069 root 1.333 sub load_neighbours {
2070 root 1.157 my ($map) = @_;
2071    
2072 root 1.333 my @neigh; # diagonal neighbours
2073 root 1.157
2074     for (0 .. 3) {
2075     my $neigh = $map->tile_path ($_)
2076     or next;
2077     $neigh = find $neigh, $map
2078     or next;
2079     $neigh->load;
2080    
2081 root 1.333 push @neigh,
2082     [$neigh->tile_path (($_ + 3) % 4), $neigh],
2083     [$neigh->tile_path (($_ + 1) % 4), $neigh];
2084 root 1.157 }
2085    
2086 root 1.333 for (grep defined $_->[0], @neigh) {
2087     my ($path, $origin) = @$_;
2088     my $neigh = find $path, $origin
2089 root 1.157 or next;
2090     $neigh->load;
2091     }
2092     }
2093    
2094 root 1.133 sub find_sync {
2095 root 1.110 my ($path, $origin) = @_;
2096    
2097 root 1.157 cf::sync_job { find $path, $origin }
2098 root 1.133 }
2099    
2100     sub do_load_sync {
2101     my ($map) = @_;
2102 root 1.110
2103 root 1.339 cf::LOG cf::llevDebug | cf::logBacktrace, "do_load_sync"
2104 root 1.342 if $Coro::current == $Coro::main;
2105 root 1.339
2106 root 1.133 cf::sync_job { $map->load };
2107 root 1.110 }
2108    
2109 root 1.157 our %MAP_PREFETCH;
2110 root 1.183 our $MAP_PREFETCHER = undef;
2111 root 1.157
2112     sub find_async {
2113 root 1.339 my ($path, $origin, $load) = @_;
2114 root 1.157
2115 root 1.166 $path = normalise $path, $origin && $origin->{path};
2116 root 1.157
2117 root 1.166 if (my $map = $cf::MAP{$path}) {
2118 root 1.340 return $map if !$load || $map->in_memory == cf::MAP_IN_MEMORY;
2119 root 1.157 }
2120    
2121 root 1.339 $MAP_PREFETCH{$path} |= $load;
2122    
2123 root 1.183 $MAP_PREFETCHER ||= cf::async {
2124 root 1.374 $Coro::current->{desc} = "map prefetcher";
2125    
2126 root 1.183 while (%MAP_PREFETCH) {
2127 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
2128     if (my $map = find $k) {
2129     $map->load if $v;
2130 root 1.308 }
2131 root 1.183
2132 root 1.339 delete $MAP_PREFETCH{$k};
2133 root 1.183 }
2134     }
2135     undef $MAP_PREFETCHER;
2136     };
2137 root 1.189 $MAP_PREFETCHER->prio (6);
2138 root 1.157
2139     ()
2140     }
2141    
2142 root 1.110 sub save {
2143     my ($self) = @_;
2144    
2145 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2146 root 1.137
2147 root 1.110 $self->{last_save} = $cf::RUNTIME;
2148    
2149     return unless $self->dirty;
2150    
2151 root 1.166 my $save = $self->save_path; utf8::encode $save;
2152     my $uniq = $self->uniq_path; utf8::encode $uniq;
2153 root 1.117
2154 root 1.110 $self->{load_path} = $save;
2155    
2156     return if $self->{deny_save};
2157    
2158 root 1.132 local $self->{last_access} = $self->last_access;#d#
2159    
2160 root 1.143 cf::async {
2161 root 1.374 $Coro::current->{desc} = "map player save";
2162 root 1.143 $_->contr->save for $self->players;
2163     };
2164    
2165 root 1.110 if ($uniq) {
2166 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2167     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2168 root 1.110 } else {
2169 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2170 root 1.110 }
2171     }
2172    
2173     sub swap_out {
2174     my ($self) = @_;
2175    
2176 root 1.130 # save first because save cedes
2177     $self->save;
2178    
2179 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2180 root 1.137
2181 root 1.110 return if $self->players;
2182     return if $self->in_memory != cf::MAP_IN_MEMORY;
2183     return if $self->{deny_save};
2184    
2185 root 1.359 $self->in_memory (cf::MAP_SWAPPED);
2186    
2187 root 1.358 $self->deactivate;
2188 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2189 root 1.110 $self->clear;
2190     }
2191    
2192 root 1.112 sub reset_at {
2193     my ($self) = @_;
2194 root 1.110
2195     # TODO: safety, remove and allow resettable per-player maps
2196 root 1.114 return 1e99 if $self->{deny_reset};
2197 root 1.110
2198 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2199 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2200 root 1.110
2201 root 1.112 $time + $to
2202     }
2203    
2204     sub should_reset {
2205     my ($self) = @_;
2206    
2207     $self->reset_at <= $cf::RUNTIME
2208 root 1.111 }
2209    
2210 root 1.110 sub reset {
2211     my ($self) = @_;
2212    
2213 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2214 root 1.137
2215 root 1.110 return if $self->players;
2216    
2217 root 1.274 warn "resetting map ", $self->path;
2218 root 1.110
2219 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2220    
2221     # need to save uniques path
2222     unless ($self->{deny_save}) {
2223     my $uniq = $self->uniq_path; utf8::encode $uniq;
2224    
2225     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2226     if $uniq;
2227     }
2228    
2229 root 1.111 delete $cf::MAP{$self->path};
2230 root 1.110
2231 root 1.358 $self->deactivate;
2232 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2233 root 1.167 $self->clear;
2234    
2235 root 1.166 $self->unlink_save;
2236 root 1.111 $self->destroy;
2237 root 1.110 }
2238    
2239 root 1.114 my $nuke_counter = "aaaa";
2240    
2241     sub nuke {
2242     my ($self) = @_;
2243    
2244 root 1.349 {
2245     my $lock = cf::lock_acquire "map_data:$self->{path}";
2246    
2247     delete $cf::MAP{$self->path};
2248 root 1.174
2249 root 1.351 $self->unlink_save;
2250    
2251 root 1.349 bless $self, "cf::map";
2252     delete $self->{deny_reset};
2253     $self->{deny_save} = 1;
2254     $self->reset_timeout (1);
2255     $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2256 root 1.174
2257 root 1.349 $cf::MAP{$self->path} = $self;
2258     }
2259 root 1.174
2260 root 1.114 $self->reset; # polite request, might not happen
2261     }
2262    
2263 root 1.276 =item $maps = cf::map::tmp_maps
2264    
2265     Returns an arrayref with all map paths of currently instantiated and saved
2266 root 1.277 maps. May block.
2267 root 1.276
2268     =cut
2269    
2270     sub tmp_maps() {
2271     [
2272     map {
2273     utf8::decode $_;
2274 root 1.277 /\.map$/
2275 root 1.276 ? normalise $_
2276     : ()
2277     } @{ aio_readdir $TMPDIR or [] }
2278     ]
2279     }
2280    
2281 root 1.277 =item $maps = cf::map::random_maps
2282    
2283     Returns an arrayref with all map paths of currently instantiated and saved
2284     random maps. May block.
2285    
2286     =cut
2287    
2288     sub random_maps() {
2289     [
2290     map {
2291     utf8::decode $_;
2292     /\.map$/
2293     ? normalise "?random/$_"
2294     : ()
2295     } @{ aio_readdir $RANDOMDIR or [] }
2296     ]
2297     }
2298    
2299 root 1.158 =item cf::map::unique_maps
2300    
2301 root 1.166 Returns an arrayref of paths of all shared maps that have
2302 root 1.158 instantiated unique items. May block.
2303    
2304     =cut
2305    
2306     sub unique_maps() {
2307 root 1.276 [
2308     map {
2309     utf8::decode $_;
2310 root 1.277 /\.map$/
2311 root 1.276 ? normalise $_
2312     : ()
2313     } @{ aio_readdir $UNIQUEDIR or [] }
2314     ]
2315 root 1.158 }
2316    
2317 root 1.155 package cf;
2318    
2319     =back
2320    
2321     =head3 cf::object
2322    
2323     =cut
2324    
2325     package cf::object;
2326    
2327     =over 4
2328    
2329     =item $ob->inv_recursive
2330 root 1.110
2331 root 1.155 Returns the inventory of the object _and_ their inventories, recursively.
2332 root 1.110
2333 root 1.155 =cut
2334 root 1.144
2335 root 1.155 sub inv_recursive_;
2336     sub inv_recursive_ {
2337     map { $_, inv_recursive_ $_->inv } @_
2338     }
2339 root 1.110
2340 root 1.155 sub inv_recursive {
2341     inv_recursive_ inv $_[0]
2342 root 1.110 }
2343    
2344 root 1.356 =item $ref = $ob->ref
2345    
2346     creates and returns a persistent reference to an objetc that can be stored as a string.
2347    
2348     =item $ob = cf::object::deref ($refstring)
2349    
2350     returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2351     even if the object actually exists. May block.
2352    
2353     =cut
2354    
2355     sub deref {
2356     my ($ref) = @_;
2357    
2358 root 1.377 if ($ref =~ m{^player\/(<1\.[0-9a-f]+>)/(.*)$}) {
2359 root 1.356 my ($uuid, $name) = ($1, $2);
2360     my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2361     or return;
2362     $pl->ob->uuid eq $uuid
2363     or return;
2364    
2365     $pl->ob
2366     } else {
2367     warn "$ref: cannot resolve object reference\n";
2368     undef
2369     }
2370     }
2371    
2372 root 1.110 package cf;
2373    
2374     =back
2375    
2376 root 1.95 =head3 cf::object::player
2377    
2378     =over 4
2379    
2380 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2381 root 1.28
2382     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2383     can be C<undef>. Does the right thing when the player is currently in a
2384     dialogue with the given NPC character.
2385    
2386     =cut
2387    
2388 root 1.22 # rough implementation of a future "reply" method that works
2389     # with dialog boxes.
2390 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2391 root 1.23 sub cf::object::player::reply($$$;$) {
2392     my ($self, $npc, $msg, $flags) = @_;
2393    
2394     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2395 root 1.22
2396 root 1.24 if ($self->{record_replies}) {
2397     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2398 elmex 1.282
2399 root 1.24 } else {
2400 elmex 1.282 my $pl = $self->contr;
2401    
2402     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2403 root 1.316 my $dialog = $pl->{npc_dialog};
2404     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2405 elmex 1.282
2406     } else {
2407     $msg = $npc->name . " says: $msg" if $npc;
2408     $self->message ($msg, $flags);
2409     }
2410 root 1.24 }
2411 root 1.22 }
2412    
2413 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2414    
2415     =cut
2416    
2417     sub cf::object::send_msg {
2418     my $pl = shift->contr
2419     or return;
2420     $pl->send_msg (@_);
2421     }
2422    
2423 root 1.79 =item $player_object->may ("access")
2424    
2425     Returns wether the given player is authorized to access resource "access"
2426     (e.g. "command_wizcast").
2427    
2428     =cut
2429    
2430     sub cf::object::player::may {
2431     my ($self, $access) = @_;
2432    
2433     $self->flag (cf::FLAG_WIZ) ||
2434     (ref $cf::CFG{"may_$access"}
2435     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2436     : $cf::CFG{"may_$access"})
2437     }
2438 root 1.70
2439 root 1.115 =item $player_object->enter_link
2440    
2441     Freezes the player and moves him/her to a special map (C<{link}>).
2442    
2443 root 1.166 The player should be reasonably safe there for short amounts of time. You
2444 root 1.115 I<MUST> call C<leave_link> as soon as possible, though.
2445    
2446 root 1.166 Will never block.
2447    
2448 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2449    
2450 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2451     map. If the map is not valid (or omitted), the player will be moved back
2452     to the location he/she was before the call to C<enter_link>, or, if that
2453     fails, to the emergency map position.
2454 root 1.115
2455     Might block.
2456    
2457     =cut
2458    
2459 root 1.166 sub link_map {
2460     unless ($LINK_MAP) {
2461     $LINK_MAP = cf::map::find "{link}"
2462 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2463 root 1.166 $LINK_MAP->load;
2464     }
2465    
2466     $LINK_MAP
2467     }
2468    
2469 root 1.110 sub cf::object::player::enter_link {
2470     my ($self) = @_;
2471    
2472 root 1.259 $self->deactivate_recursive;
2473 root 1.258
2474 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2475 root 1.110
2476 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2477 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2478 root 1.110
2479 root 1.194 $self->enter_map ($LINK_MAP || link_map, 10, 10);
2480 root 1.110 }
2481    
2482     sub cf::object::player::leave_link {
2483     my ($self, $map, $x, $y) = @_;
2484    
2485 root 1.270 return unless $self->contr->active;
2486    
2487 root 1.110 my $link_pos = delete $self->{_link_pos};
2488    
2489     unless ($map) {
2490     # restore original map position
2491     ($map, $x, $y) = @{ $link_pos || [] };
2492 root 1.133 $map = cf::map::find $map;
2493 root 1.110
2494     unless ($map) {
2495     ($map, $x, $y) = @$EMERGENCY_POSITION;
2496 root 1.133 $map = cf::map::find $map
2497 root 1.110 or die "FATAL: cannot load emergency map\n";
2498     }
2499     }
2500    
2501     ($x, $y) = (-1, -1)
2502     unless (defined $x) && (defined $y);
2503    
2504     # use -1 or undef as default coordinates, not 0, 0
2505     ($x, $y) = ($map->enter_x, $map->enter_y)
2506     if $x <=0 && $y <= 0;
2507    
2508     $map->load;
2509 root 1.333 $map->load_neighbours;
2510 root 1.110
2511 root 1.143 return unless $self->contr->active;
2512 root 1.110 $self->activate_recursive;
2513 root 1.215
2514     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2515 root 1.110 $self->enter_map ($map, $x, $y);
2516     }
2517    
2518 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2519 root 1.268
2520     Moves the player to the given map-path and coordinates by first freezing
2521     her, loading and preparing them map, calling the provided $check callback
2522     that has to return the map if sucecssful, and then unfreezes the player on
2523 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2524     be called at the end of this process.
2525 root 1.110
2526     =cut
2527    
2528 root 1.270 our $GOTOGEN;
2529    
2530 root 1.136 sub cf::object::player::goto {
2531 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2532 root 1.268
2533 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2534     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2535    
2536 root 1.110 $self->enter_link;
2537    
2538 root 1.140 (async {
2539 root 1.374 $Coro::current->{desc} = "player::goto $path $x $y";
2540    
2541 root 1.365 # *tag paths override both path and x|y
2542     if ($path =~ /^\*(.*)$/) {
2543     if (my @obs = grep $_->map, ext::map_tags::find $1) {
2544     my $ob = $obs[rand @obs];
2545 root 1.366
2546 root 1.367 # see if we actually can go there
2547 root 1.368 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2548     $ob = $obs[rand @obs];
2549 root 1.369 } else {
2550     $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2551 root 1.368 }
2552 root 1.369 # else put us there anyways for now #d#
2553 root 1.366
2554 root 1.365 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2555 root 1.369 } else {
2556     ($path, $x, $y) = (undef, undef, undef);
2557 root 1.365 }
2558     }
2559    
2560 root 1.197 my $map = eval {
2561 root 1.369 my $map = defined $path ? cf::map::find $path : undef;
2562 root 1.268
2563     if ($map) {
2564     $map = $map->customise_for ($self);
2565     $map = $check->($map) if $check && $map;
2566     } else {
2567 root 1.369 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2568 root 1.268 }
2569    
2570 root 1.197 $map
2571 root 1.268 };
2572    
2573     if ($@) {
2574     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2575     LOG llevError | logBacktrace, Carp::longmess $@;
2576     }
2577 root 1.115
2578 root 1.270 if ($gen == $self->{_goto_generation}) {
2579     delete $self->{_goto_generation};
2580     $self->leave_link ($map, $x, $y);
2581     }
2582 root 1.306
2583     $done->() if $done;
2584 root 1.110 })->prio (1);
2585     }
2586    
2587     =item $player_object->enter_exit ($exit_object)
2588    
2589     =cut
2590    
2591     sub parse_random_map_params {
2592     my ($spec) = @_;
2593    
2594     my $rmp = { # defaults
2595 root 1.181 xsize => (cf::rndm 15, 40),
2596     ysize => (cf::rndm 15, 40),
2597     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2598 root 1.182 #layout => string,
2599 root 1.110 };
2600    
2601     for (split /\n/, $spec) {
2602     my ($k, $v) = split /\s+/, $_, 2;
2603    
2604     $rmp->{lc $k} = $v if (length $k) && (length $v);
2605     }
2606    
2607     $rmp
2608     }
2609    
2610     sub prepare_random_map {
2611     my ($exit) = @_;
2612    
2613 root 1.179 my $guard = cf::lock_acquire "exit_prepare:$exit";
2614    
2615 root 1.110 # all this does is basically replace the /! path by
2616     # a new random map path (?random/...) with a seed
2617     # that depends on the exit object
2618    
2619     my $rmp = parse_random_map_params $exit->msg;
2620    
2621     if ($exit->map) {
2622 root 1.198 $rmp->{region} = $exit->region->name;
2623 root 1.110 $rmp->{origin_map} = $exit->map->path;
2624     $rmp->{origin_x} = $exit->x;
2625     $rmp->{origin_y} = $exit->y;
2626     }
2627    
2628     $rmp->{random_seed} ||= $exit->random_seed;
2629    
2630     my $data = cf::to_json $rmp;
2631     my $md5 = Digest::MD5::md5_hex $data;
2632 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2633 root 1.110
2634 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2635 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2636 root 1.177 undef $fh;
2637     aio_rename "$meta~", $meta;
2638 root 1.110
2639     $exit->slaying ("?random/$md5");
2640     $exit->msg (undef);
2641     }
2642     }
2643    
2644     sub cf::object::player::enter_exit {
2645     my ($self, $exit) = @_;
2646    
2647     return unless $self->type == cf::PLAYER;
2648    
2649 root 1.195 if ($exit->slaying eq "/!") {
2650     #TODO: this should de-fi-ni-te-ly not be a sync-job
2651 root 1.233 # the problem is that $exit might not survive long enough
2652     # so it needs to be done right now, right here
2653 root 1.195 cf::sync_job { prepare_random_map $exit };
2654     }
2655    
2656     my $slaying = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2657     my $hp = $exit->stats->hp;
2658     my $sp = $exit->stats->sp;
2659    
2660 root 1.110 $self->enter_link;
2661    
2662 root 1.296 # if exit is damned, update players death & WoR home-position
2663     $self->contr->savebed ($slaying, $hp, $sp)
2664     if $exit->flag (FLAG_DAMNED);
2665    
2666 root 1.140 (async {
2667 root 1.374 $Coro::current->{desc} = "enter_exit $slaying $hp $sp";
2668    
2669 root 1.133 $self->deactivate_recursive; # just to be sure
2670 root 1.110 unless (eval {
2671 root 1.195 $self->goto ($slaying, $hp, $sp);
2672 root 1.110
2673     1;
2674     }) {
2675     $self->message ("Something went wrong deep within the crossfire server. "
2676 root 1.233 . "I'll try to bring you back to the map you were before. "
2677     . "Please report this to the dungeon master!",
2678     cf::NDI_UNIQUE | cf::NDI_RED);
2679 root 1.110
2680     warn "ERROR in enter_exit: $@";
2681     $self->leave_link;
2682     }
2683     })->prio (1);
2684     }
2685    
2686 root 1.95 =head3 cf::client
2687    
2688     =over 4
2689    
2690     =item $client->send_drawinfo ($text, $flags)
2691    
2692     Sends a drawinfo packet to the client. Circumvents output buffering so
2693     should not be used under normal circumstances.
2694    
2695 root 1.70 =cut
2696    
2697 root 1.95 sub cf::client::send_drawinfo {
2698     my ($self, $text, $flags) = @_;
2699    
2700     utf8::encode $text;
2701 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2702 root 1.95 }
2703    
2704 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2705 root 1.283
2706     Send a drawinfo or msg packet to the client, formatting the msg for the
2707     client if neccessary. C<$type> should be a string identifying the type of
2708     the message, with C<log> being the default. If C<$color> is negative, suppress
2709     the message unless the client supports the msg packet.
2710    
2711     =cut
2712    
2713 root 1.391 # non-persistent channels (usually the info channel)
2714 root 1.350 our %CHANNEL = (
2715     "c/identify" => {
2716 root 1.375 id => "infobox",
2717 root 1.350 title => "Identify",
2718     reply => undef,
2719     tooltip => "Items recently identified",
2720     },
2721 root 1.352 "c/examine" => {
2722 root 1.375 id => "infobox",
2723 root 1.352 title => "Examine",
2724     reply => undef,
2725     tooltip => "Signs and other items you examined",
2726     },
2727 root 1.389 "c/book" => {
2728     id => "infobox",
2729     title => "Book",
2730     reply => undef,
2731     tooltip => "The contents of a note or book",
2732     },
2733 root 1.375 "c/lookat" => {
2734     id => "infobox",
2735     title => "Look",
2736     reply => undef,
2737     tooltip => "What you saw there",
2738     },
2739 root 1.390 "c/who" => {
2740     id => "infobox",
2741     title => "Players",
2742     reply => undef,
2743     tooltip => "Shows players who are currently online",
2744     },
2745     "c/body" => {
2746     id => "infobox",
2747     title => "Body Parts",
2748     reply => undef,
2749     tooltip => "Shows which body parts you posess and are available",
2750     },
2751     "c/uptime" => {
2752     id => "infobox",
2753     title => "Uptime",
2754     reply => undef,
2755 root 1.391 tooltip => "How long the server has been running since last restart",
2756 root 1.390 },
2757     "c/mapinfo" => {
2758     id => "infobox",
2759     title => "Map Info",
2760     reply => undef,
2761     tooltip => "Information related to the maps",
2762     },
2763 root 1.350 );
2764    
2765 root 1.283 sub cf::client::send_msg {
2766 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
2767 root 1.283
2768     $msg = $self->pl->expand_cfpod ($msg);
2769    
2770 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
2771 root 1.311
2772 root 1.350 # check predefined channels, for the benefit of C
2773 root 1.375 if ($CHANNEL{$channel}) {
2774     $channel = $CHANNEL{$channel};
2775    
2776     $self->ext_msg (channel_info => $channel)
2777     if $self->can_msg;
2778    
2779     $channel = $channel->{id};
2780 root 1.350
2781 root 1.375 } elsif (ref $channel) {
2782 root 1.311 # send meta info to client, if not yet sent
2783     unless (exists $self->{channel}{$channel->{id}}) {
2784     $self->{channel}{$channel->{id}} = $channel;
2785 root 1.353 $self->ext_msg (channel_info => $channel)
2786     if $self->can_msg;
2787 root 1.311 }
2788    
2789     $channel = $channel->{id};
2790     }
2791    
2792 root 1.313 return unless @extra || length $msg;
2793    
2794 root 1.283 if ($self->can_msg) {
2795 root 1.323 # default colour, mask it out
2796     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
2797     if $color & cf::NDI_DEF;
2798    
2799     $self->send_packet ("msg " . $self->{json_coder}->encode (
2800     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]));
2801 root 1.283 } else {
2802 root 1.323 if ($color >= 0) {
2803     # replace some tags by gcfclient-compatible ones
2804     for ($msg) {
2805     1 while
2806     s/<b>([^<]*)<\/b>/[b]${1}[\/b]/
2807     || s/<i>([^<]*)<\/i>/[i]${1}[\/i]/
2808     || s/<u>([^<]*)<\/u>/[ul]${1}[\/ul]/
2809     || s/<tt>([^<]*)<\/tt>/[fixed]${1}[\/fixed]/
2810     || s/<fg name=\"([^"]+)\">([^<]*)<\/fg>/[color=$1]${2}[\/color]/;
2811     }
2812    
2813     $color &= cf::NDI_COLOR_MASK;
2814 root 1.283
2815 root 1.327 utf8::encode $msg;
2816    
2817 root 1.284 if (0 && $msg =~ /\[/) {
2818 root 1.331 # COMMAND/INFO
2819     $self->send_packet ("drawextinfo $color 10 8 $msg")
2820 root 1.283 } else {
2821 root 1.286 $msg =~ s/\[\/?(?:b|i|u|fixed|color)[^\]]*\]//g;
2822 root 1.283 $self->send_packet ("drawinfo $color $msg")
2823     }
2824     }
2825     }
2826     }
2827    
2828 root 1.316 =item $client->ext_msg ($type, @msg)
2829 root 1.232
2830 root 1.287 Sends an ext event to the client.
2831 root 1.232
2832     =cut
2833    
2834 root 1.316 sub cf::client::ext_msg($$@) {
2835     my ($self, $type, @msg) = @_;
2836 root 1.232
2837 root 1.343 if ($self->extcmd == 2) {
2838 root 1.316 $self->send_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
2839 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
2840 root 1.316 push @msg, msgtype => "event_$type";
2841     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2842     }
2843 root 1.232 }
2844 root 1.95
2845 root 1.336 =item $client->ext_reply ($msgid, @msg)
2846    
2847     Sends an ext reply to the client.
2848    
2849     =cut
2850    
2851     sub cf::client::ext_reply($$@) {
2852     my ($self, $id, @msg) = @_;
2853    
2854     if ($self->extcmd == 2) {
2855     $self->send_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
2856 root 1.343 } elsif ($self->extcmd == 1) {
2857 root 1.336 #TODO: version 1, remove
2858     unshift @msg, msgtype => "reply", msgid => $id;
2859     $self->send_packet ("ext " . $self->{json_coder}->encode ({@msg}));
2860     }
2861     }
2862    
2863 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
2864    
2865     Queues a query to the client, calling the given callback with
2866     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
2867     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
2868    
2869 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
2870     become reliable at some point in the future.
2871 root 1.95
2872     =cut
2873    
2874     sub cf::client::query {
2875     my ($self, $flags, $text, $cb) = @_;
2876    
2877     return unless $self->state == ST_PLAYING
2878     || $self->state == ST_SETUP
2879     || $self->state == ST_CUSTOM;
2880    
2881     $self->state (ST_CUSTOM);
2882    
2883     utf8::encode $text;
2884     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
2885    
2886     $self->send_packet ($self->{query_queue}[0][0])
2887     if @{ $self->{query_queue} } == 1;
2888 root 1.287
2889     1
2890 root 1.95 }
2891    
2892     cf::client->attach (
2893 root 1.290 on_connect => sub {
2894     my ($ns) = @_;
2895    
2896     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
2897     },
2898 root 1.95 on_reply => sub {
2899     my ($ns, $msg) = @_;
2900    
2901     # this weird shuffling is so that direct followup queries
2902     # get handled first
2903 root 1.128 my $queue = delete $ns->{query_queue}
2904 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
2905 root 1.95
2906     (shift @$queue)->[1]->($msg);
2907 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
2908 root 1.95
2909     push @{ $ns->{query_queue} }, @$queue;
2910    
2911     if (@{ $ns->{query_queue} } == @$queue) {
2912     if (@$queue) {
2913     $ns->send_packet ($ns->{query_queue}[0][0]);
2914     } else {
2915 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
2916 root 1.95 }
2917     }
2918     },
2919 root 1.287 on_exticmd => sub {
2920     my ($ns, $buf) = @_;
2921    
2922 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
2923 root 1.287
2924     if (ref $msg) {
2925 root 1.316 my ($type, $reply, @payload) =
2926     "ARRAY" eq ref $msg
2927     ? @$msg
2928     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
2929    
2930 root 1.338 my @reply;
2931    
2932 root 1.316 if (my $cb = $EXTICMD{$type}) {
2933 root 1.338 @reply = $cb->($ns, @payload);
2934     }
2935    
2936     $ns->ext_reply ($reply, @reply)
2937     if $reply;
2938 root 1.316
2939 root 1.287 } else {
2940     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
2941     }
2942    
2943     cf::override;
2944     },
2945 root 1.95 );
2946    
2947 root 1.140 =item $client->async (\&cb)
2948 root 1.96
2949     Create a new coroutine, running the specified callback. The coroutine will
2950     be automatically cancelled when the client gets destroyed (e.g. on logout,
2951     or loss of connection).
2952    
2953     =cut
2954    
2955 root 1.140 sub cf::client::async {
2956 root 1.96 my ($self, $cb) = @_;
2957    
2958 root 1.140 my $coro = &Coro::async ($cb);
2959 root 1.103
2960     $coro->on_destroy (sub {
2961 root 1.96 delete $self->{_coro}{$coro+0};
2962 root 1.103 });
2963 root 1.96
2964     $self->{_coro}{$coro+0} = $coro;
2965 root 1.103
2966     $coro
2967 root 1.96 }
2968    
2969     cf::client->attach (
2970     on_destroy => sub {
2971     my ($ns) = @_;
2972    
2973 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
2974 root 1.96 },
2975     );
2976    
2977 root 1.95 =back
2978    
2979 root 1.70
2980     =head2 SAFE SCRIPTING
2981    
2982     Functions that provide a safe environment to compile and execute
2983     snippets of perl code without them endangering the safety of the server
2984     itself. Looping constructs, I/O operators and other built-in functionality
2985     is not available in the safe scripting environment, and the number of
2986 root 1.79 functions and methods that can be called is greatly reduced.
2987 root 1.70
2988     =cut
2989 root 1.23
2990 root 1.42 our $safe = new Safe "safe";
2991 root 1.23 our $safe_hole = new Safe::Hole;
2992    
2993     $SIG{FPE} = 'IGNORE';
2994    
2995 root 1.328 $safe->permit_only (Opcode::opset qw(
2996     :base_core :base_mem :base_orig :base_math
2997     grepstart grepwhile mapstart mapwhile
2998     sort time
2999     ));
3000 root 1.23
3001 root 1.25 # here we export the classes and methods available to script code
3002    
3003 root 1.70 =pod
3004    
3005 root 1.228 The following functions and methods are available within a safe environment:
3006 root 1.70
3007 root 1.297 cf::object
3008 root 1.383 contr pay_amount pay_player map x y force_find force_add destroy
3009 elmex 1.341 insert remove name archname title slaying race decrease_ob_nr
3010 root 1.297
3011     cf::object::player
3012     player
3013    
3014     cf::player
3015     peaceful
3016    
3017     cf::map
3018     trigger
3019 root 1.70
3020     =cut
3021    
3022 root 1.25 for (
3023 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
3024 elmex 1.341 insert remove inv name archname title slaying race
3025 root 1.383 decrease_ob_nr destroy)],
3026 root 1.25 ["cf::object::player" => qw(player)],
3027     ["cf::player" => qw(peaceful)],
3028 elmex 1.91 ["cf::map" => qw(trigger)],
3029 root 1.25 ) {
3030     no strict 'refs';
3031     my ($pkg, @funs) = @$_;
3032 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3033 root 1.25 for @funs;
3034     }
3035 root 1.23
3036 root 1.70 =over 4
3037    
3038     =item @retval = safe_eval $code, [var => value, ...]
3039    
3040     Compiled and executes the given perl code snippet. additional var/value
3041     pairs result in temporary local (my) scalar variables of the given name
3042     that are available in the code snippet. Example:
3043    
3044     my $five = safe_eval '$first + $second', first => 1, second => 4;
3045    
3046     =cut
3047    
3048 root 1.23 sub safe_eval($;@) {
3049     my ($code, %vars) = @_;
3050    
3051     my $qcode = $code;
3052     $qcode =~ s/"/‟/g; # not allowed in #line filenames
3053     $qcode =~ s/\n/\\n/g;
3054    
3055     local $_;
3056 root 1.41 local @safe::cf::_safe_eval_args = values %vars;
3057 root 1.23
3058 root 1.42 my $eval =
3059 root 1.23 "do {\n"
3060     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3061     . "#line 0 \"{$qcode}\"\n"
3062     . $code
3063     . "\n}"
3064 root 1.25 ;
3065    
3066     sub_generation_inc;
3067 root 1.42 my @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3068 root 1.25 sub_generation_inc;
3069    
3070 root 1.42 if ($@) {
3071     warn "$@";
3072     warn "while executing safe code '$code'\n";
3073     warn "with arguments " . (join " ", %vars) . "\n";
3074     }
3075    
3076 root 1.25 wantarray ? @res : $res[0]
3077 root 1.23 }
3078    
3079 root 1.69 =item cf::register_script_function $function => $cb
3080    
3081     Register a function that can be called from within map/npc scripts. The
3082     function should be reasonably secure and should be put into a package name
3083     like the extension.
3084    
3085     Example: register a function that gets called whenever a map script calls
3086     C<rent::overview>, as used by the C<rent> extension.
3087    
3088     cf::register_script_function "rent::overview" => sub {
3089     ...
3090     };
3091    
3092     =cut
3093    
3094 root 1.23 sub register_script_function {
3095     my ($fun, $cb) = @_;
3096    
3097     no strict 'refs';
3098 root 1.41 *{"safe::$fun"} = $safe_hole->wrap ($cb);
3099 root 1.23 }
3100    
3101 root 1.70 =back
3102    
3103 root 1.71 =cut
3104    
3105 root 1.23 #############################################################################
3106 root 1.203 # the server's init and main functions
3107    
3108 root 1.246 sub load_facedata($) {
3109     my ($path) = @_;
3110 root 1.223
3111 root 1.348 # HACK to clear player env face cache, we need some signal framework
3112     # for this (global event?)
3113     %ext::player_env::MUSIC_FACE_CACHE = ();
3114    
3115 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3116 root 1.334
3117 root 1.229 warn "loading facedata from $path\n";
3118 root 1.223
3119 root 1.236 my $facedata;
3120     0 < aio_load $path, $facedata
3121 root 1.223 or die "$path: $!";
3122    
3123 root 1.237 $facedata = Coro::Storable::thaw $facedata;
3124 root 1.223
3125 root 1.236 $facedata->{version} == 2
3126 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3127    
3128 root 1.334 # patch in the exptable
3129     $facedata->{resource}{"res/exp_table"} = {
3130     type => FT_RSRC,
3131 root 1.337 data => $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]),
3132 root 1.334 };
3133     cf::cede_to_tick;
3134    
3135 root 1.236 {
3136     my $faces = $facedata->{faceinfo};
3137    
3138     while (my ($face, $info) = each %$faces) {
3139     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3140 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3141     cf::face::set_magicmap $idx, $info->{magicmap};
3142 root 1.334 cf::face::set_data $idx, 0, $info->{data32}, Digest::MD5::md5 $info->{data32};
3143     cf::face::set_data $idx, 1, $info->{data64}, Digest::MD5::md5 $info->{data64};
3144 root 1.302
3145     cf::cede_to_tick;
3146 root 1.236 }
3147    
3148     while (my ($face, $info) = each %$faces) {
3149     next unless $info->{smooth};
3150     my $idx = cf::face::find $face
3151     or next;
3152     if (my $smooth = cf::face::find $info->{smooth}) {
3153 root 1.302 cf::face::set_smooth $idx, $smooth;
3154     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3155 root 1.236 } else {
3156     warn "smooth face '$info->{smooth}' not found for face '$face'";
3157     }
3158 root 1.302
3159     cf::cede_to_tick;
3160 root 1.236 }
3161 root 1.223 }
3162    
3163 root 1.236 {
3164     my $anims = $facedata->{animinfo};
3165    
3166     while (my ($anim, $info) = each %$anims) {
3167     cf::anim::set $anim, $info->{frames}, $info->{facings};
3168 root 1.302 cf::cede_to_tick;
3169 root 1.225 }
3170 root 1.236
3171     cf::anim::invalidate_all; # d'oh
3172 root 1.225 }
3173    
3174 root 1.302 {
3175     # TODO: for gcfclient pleasure, we should give resources
3176     # that gcfclient doesn't grok a >10000 face index.
3177     my $res = $facedata->{resource};
3178    
3179 root 1.321 my $soundconf = delete $res->{"res/sound.conf"};
3180 root 1.320
3181 root 1.302 while (my ($name, $info) = each %$res) {
3182     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3183 root 1.334 my $data;
3184 root 1.307
3185 root 1.318 if ($info->{type} & 1) {
3186     # prepend meta info
3187    
3188 root 1.334 my $meta = $enc->encode ({
3189     name => $name,
3190     %{ $info->{meta} || {} },
3191     });
3192 root 1.307
3193 root 1.334 $data = pack "(w/a*)*", $meta, $info->{data};
3194 root 1.337 } else {
3195     $data = $info->{data};
3196 root 1.307 }
3197 root 1.302
3198 root 1.334 cf::face::set_data $idx, 0, $data, Digest::MD5::md5 $data;
3199 root 1.318 cf::face::set_type $idx, $info->{type};
3200    
3201 root 1.302 cf::cede_to_tick;
3202     }
3203 root 1.321
3204     if ($soundconf) {
3205     $soundconf = $enc->decode (delete $soundconf->{data});
3206    
3207     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3208     my $sound = $soundconf->{compat}[$_]
3209     or next;
3210    
3211     my $face = cf::face::find "sound/$sound->[1]";
3212     cf::sound::set $sound->[0] => $face;
3213     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3214     }
3215    
3216 root 1.326 while (my ($k, $v) = each %{$soundconf->{event}}) {
3217     my $face = cf::face::find "sound/$v";
3218     cf::sound::set $k => $face;
3219     }
3220 root 1.321 }
3221 root 1.302 }
3222    
3223 root 1.223 1
3224     }
3225    
3226 root 1.318 register_exticmd fx_want => sub {
3227     my ($ns, $want) = @_;
3228    
3229     while (my ($k, $v) = each %$want) {
3230     $ns->fx_want ($k, $v);
3231     }
3232     };
3233    
3234 root 1.253 sub reload_regions {
3235 root 1.348 # HACK to clear player env face cache, we need some signal framework
3236     # for this (global event?)
3237     %ext::player_env::MUSIC_FACE_CACHE = ();
3238    
3239 root 1.253 load_resource_file "$MAPDIR/regions"
3240     or die "unable to load regions file\n";
3241 root 1.304
3242     for (cf::region::list) {
3243     $_->{match} = qr/$_->{match}/
3244     if exists $_->{match};
3245     }
3246 root 1.253 }
3247    
3248 root 1.246 sub reload_facedata {
3249 root 1.253 load_facedata "$DATADIR/facedata"
3250 root 1.246 or die "unable to load facedata\n";
3251     }
3252    
3253     sub reload_archetypes {
3254 root 1.253 load_resource_file "$DATADIR/archetypes"
3255 root 1.246 or die "unable to load archetypes\n";
3256 root 1.289 #d# NEED to laod twice to resolve forward references
3257     # this really needs to be done in an extra post-pass
3258     # (which needs to be synchronous, so solve it differently)
3259     load_resource_file "$DATADIR/archetypes"
3260     or die "unable to load archetypes\n";
3261 root 1.241 }
3262    
3263 root 1.246 sub reload_treasures {
3264 root 1.253 load_resource_file "$DATADIR/treasures"
3265 root 1.246 or die "unable to load treasurelists\n";
3266 root 1.241 }
3267    
3268 root 1.223 sub reload_resources {
3269 root 1.245 warn "reloading resource files...\n";
3270    
3271 root 1.246 reload_regions;
3272     reload_facedata;
3273 root 1.274 #reload_archetypes;#d#
3274 root 1.246 reload_archetypes;
3275     reload_treasures;
3276 root 1.245
3277     warn "finished reloading resource files\n";
3278 root 1.223 }
3279    
3280     sub init {
3281     reload_resources;
3282 root 1.203 }
3283 root 1.34
3284 root 1.345 sub reload_config {
3285 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3286 root 1.72 or return;
3287    
3288     local $/;
3289     *CFG = YAML::Syck::Load <$fh>;
3290 root 1.131
3291     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
3292    
3293 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3294     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3295    
3296 root 1.131 if (exists $CFG{mlockall}) {
3297     eval {
3298 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3299 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3300     };
3301     warn $@ if $@;
3302     }
3303 root 1.72 }
3304    
3305 root 1.39 sub main {
3306 root 1.108 # we must not ever block the main coroutine
3307     local $Coro::idle = sub {
3308 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3309 root 1.175 (async {
3310 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3311 root 1.175 Event::one_event;
3312     })->prio (Coro::PRIO_MAX);
3313 root 1.108 };
3314    
3315 root 1.345 reload_config;
3316 root 1.210 db_init;
3317 root 1.61 load_extensions;
3318 root 1.183
3319     $TICK_WATCHER->start;
3320 root 1.34 Event::loop;
3321     }
3322    
3323     #############################################################################
3324 root 1.155 # initialisation and cleanup
3325    
3326     # install some emergency cleanup handlers
3327     BEGIN {
3328     for my $signal (qw(INT HUP TERM)) {
3329     Event->signal (
3330 root 1.189 reentrant => 0,
3331     data => WF_AUTOCANCEL,
3332     signal => $signal,
3333 root 1.191 prio => 0,
3334 root 1.189 cb => sub {
3335 root 1.155 cf::cleanup "SIG$signal";
3336     },
3337     );
3338     }
3339     }
3340    
3341 root 1.281 sub write_runtime {
3342     my $runtime = "$LOCALDIR/runtime";
3343    
3344     # first touch the runtime file to show we are still running:
3345     # the fsync below can take a very very long time.
3346    
3347     IO::AIO::aio_utime $runtime, undef, undef;
3348    
3349     my $guard = cf::lock_acquire "write_runtime";
3350    
3351     my $fh = aio_open "$runtime~", O_WRONLY | O_CREAT, 0644
3352     or return;
3353    
3354     my $value = $cf::RUNTIME + 90 + 10;
3355     # 10 is the runtime save interval, for a monotonic clock
3356     # 60 allows for the watchdog to kill the server.
3357    
3358     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3359     and return;
3360    
3361     # always fsync - this file is important
3362     aio_fsync $fh
3363     and return;
3364    
3365     # touch it again to show we are up-to-date
3366     aio_utime $fh, undef, undef;
3367    
3368     close $fh
3369     or return;
3370    
3371     aio_rename "$runtime~", $runtime
3372     and return;
3373    
3374     warn "runtime file written.\n";
3375    
3376     1
3377     }
3378    
3379 root 1.156 sub emergency_save() {
3380 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3381    
3382     warn "enter emergency perl save\n";
3383    
3384     cf::sync_job {
3385     # use a peculiar iteration method to avoid tripping on perl
3386     # refcount bugs in for. also avoids problems with players
3387 root 1.167 # and maps saved/destroyed asynchronously.
3388 root 1.155 warn "begin emergency player save\n";
3389     for my $login (keys %cf::PLAYER) {
3390     my $pl = $cf::PLAYER{$login} or next;
3391     $pl->valid or next;
3392 root 1.382 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3393 root 1.155 $pl->save;
3394     }
3395     warn "end emergency player save\n";
3396    
3397     warn "begin emergency map save\n";
3398     for my $path (keys %cf::MAP) {
3399     my $map = $cf::MAP{$path} or next;
3400     $map->valid or next;
3401     $map->save;
3402     }
3403     warn "end emergency map save\n";
3404 root 1.208
3405     warn "begin emergency database checkpoint\n";
3406     BDB::db_env_txn_checkpoint $DB_ENV;
3407     warn "end emergency database checkpoint\n";
3408 root 1.155 };
3409    
3410     warn "leave emergency perl save\n";
3411     }
3412 root 1.22
3413 root 1.211 sub post_cleanup {
3414     my ($make_core) = @_;
3415    
3416     warn Carp::longmess "post_cleanup backtrace"
3417     if $make_core;
3418     }
3419    
3420 root 1.246 sub do_reload_perl() {
3421 root 1.106 # can/must only be called in main
3422     if ($Coro::current != $Coro::main) {
3423 root 1.183 warn "can only reload from main coroutine";
3424 root 1.106 return;
3425     }
3426    
3427 root 1.103 warn "reloading...";
3428    
3429 root 1.212 warn "entering sync_job";
3430    
3431 root 1.213 cf::sync_job {
3432 root 1.214 cf::write_runtime; # external watchdog should not bark
3433 root 1.212 cf::emergency_save;
3434 root 1.214 cf::write_runtime; # external watchdog should not bark
3435 root 1.183
3436 root 1.212 warn "syncing database to disk";
3437     BDB::db_env_txn_checkpoint $DB_ENV;
3438 root 1.106
3439     # if anything goes wrong in here, we should simply crash as we already saved
3440 root 1.65
3441 root 1.183 warn "cancelling all WF_AUTOCANCEL watchers";
3442 root 1.87 for (Event::all_watchers) {
3443     $_->cancel if $_->data & WF_AUTOCANCEL;
3444     }
3445 root 1.65
3446 root 1.183 warn "flushing outstanding aio requests";
3447     for (;;) {
3448 root 1.208 BDB::flush;
3449 root 1.183 IO::AIO::flush;
3450 root 1.387 Coro::cede_notself;
3451 root 1.208 last unless IO::AIO::nreqs || BDB::nreqs;
3452 root 1.183 warn "iterate...";
3453     }
3454    
3455 root 1.223 ++$RELOAD;
3456    
3457 root 1.183 warn "cancelling all extension coros";
3458 root 1.103 $_->cancel for values %EXT_CORO;
3459     %EXT_CORO = ();
3460    
3461 root 1.183 warn "removing commands";
3462 root 1.159 %COMMAND = ();
3463    
3464 root 1.287 warn "removing ext/exti commands";
3465     %EXTCMD = ();
3466     %EXTICMD = ();
3467 root 1.159
3468 root 1.183 warn "unloading/nuking all extensions";
3469 root 1.159 for my $pkg (@EXTS) {
3470 root 1.160 warn "... unloading $pkg";
3471 root 1.159
3472     if (my $cb = $pkg->can ("unload")) {
3473     eval {
3474     $cb->($pkg);
3475     1
3476     } or warn "$pkg unloaded, but with errors: $@";
3477     }
3478    
3479 root 1.160 warn "... nuking $pkg";
3480 root 1.159 Symbol::delete_package $pkg;
3481 root 1.65 }
3482    
3483 root 1.183 warn "unloading all perl modules loaded from $LIBDIR";
3484 root 1.65 while (my ($k, $v) = each %INC) {
3485     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3486    
3487 root 1.183 warn "... unloading $k";
3488 root 1.65 delete $INC{$k};
3489    
3490     $k =~ s/\.pm$//;
3491     $k =~ s/\//::/g;
3492    
3493     if (my $cb = $k->can ("unload_module")) {
3494     $cb->();
3495     }
3496    
3497     Symbol::delete_package $k;
3498     }
3499    
3500 root 1.183 warn "getting rid of safe::, as good as possible";
3501 root 1.65 Symbol::delete_package "safe::$_"
3502 root 1.103 for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3503 root 1.65
3504 root 1.183 warn "unloading cf.pm \"a bit\"";
3505 root 1.65 delete $INC{"cf.pm"};
3506 root 1.252 delete $INC{"cf/pod.pm"};
3507 root 1.65
3508     # don't, removes xs symbols, too,
3509     # and global variables created in xs
3510     #Symbol::delete_package __PACKAGE__;
3511    
3512 root 1.183 warn "unload completed, starting to reload now";
3513    
3514 root 1.103 warn "reloading cf.pm";
3515 root 1.65 require cf;
3516 root 1.100 cf::_connect_to_perl; # nominally unnecessary, but cannot hurt
3517    
3518 root 1.183 warn "loading config and database again";
3519 root 1.345 cf::reload_config;
3520 root 1.65
3521 root 1.183 warn "loading extensions";
3522 root 1.65 cf::load_extensions;
3523    
3524 root 1.183 warn "reattaching attachments to objects/players";
3525 root 1.222 _global_reattach; # objects, sockets
3526 root 1.183 warn "reattaching attachments to maps";
3527 root 1.144 reattach $_ for values %MAP;
3528 root 1.222 warn "reattaching attachments to players";
3529     reattach $_ for values %PLAYER;
3530 root 1.183
3531 root 1.212 warn "leaving sync_job";
3532 root 1.183
3533 root 1.212 1
3534     } or do {
3535 root 1.106 warn $@;
3536     warn "error while reloading, exiting.";
3537     exit 1;
3538 root 1.212 };
3539 root 1.106
3540 root 1.159 warn "reloaded";
3541 root 1.65 };
3542    
3543 root 1.175 our $RELOAD_WATCHER; # used only during reload
3544    
3545 root 1.246 sub reload_perl() {
3546     # doing reload synchronously and two reloads happen back-to-back,
3547     # coro crashes during coro_state_free->destroy here.
3548    
3549     $RELOAD_WATCHER ||= Event->timer (
3550     reentrant => 0,
3551     after => 0,
3552     data => WF_AUTOCANCEL,
3553     cb => sub {
3554     do_reload_perl;
3555     undef $RELOAD_WATCHER;
3556     },
3557     );
3558     }
3559    
3560 root 1.111 register_command "reload" => sub {
3561 root 1.65 my ($who, $arg) = @_;
3562    
3563     if ($who->flag (FLAG_WIZ)) {
3564 root 1.175 $who->message ("reloading server.");
3565 root 1.374 async {
3566     $Coro::current->{desc} = "perl_reload";
3567     reload_perl;
3568     };
3569 root 1.65 }
3570     };
3571    
3572 root 1.27 unshift @INC, $LIBDIR;
3573 root 1.17
3574 root 1.183 my $bug_warning = 0;
3575    
3576 root 1.239 our @WAIT_FOR_TICK;
3577     our @WAIT_FOR_TICK_BEGIN;
3578    
3579     sub wait_for_tick {
3580 root 1.240 return unless $TICK_WATCHER->is_active;
3581 root 1.241 return if $Coro::current == $Coro::main;
3582    
3583 root 1.239 my $signal = new Coro::Signal;
3584     push @WAIT_FOR_TICK, $signal;
3585     $signal->wait;
3586     }
3587    
3588     sub wait_for_tick_begin {
3589 root 1.240 return unless $TICK_WATCHER->is_active;
3590 root 1.241 return if $Coro::current == $Coro::main;
3591    
3592 root 1.239 my $signal = new Coro::Signal;
3593     push @WAIT_FOR_TICK_BEGIN, $signal;
3594     $signal->wait;
3595     }
3596    
3597 root 1.35 $TICK_WATCHER = Event->timer (
3598 root 1.104 reentrant => 0,
3599 root 1.183 parked => 1,
3600 root 1.191 prio => 0,
3601 root 1.104 at => $NEXT_TICK || $TICK,
3602     data => WF_AUTOCANCEL,
3603     cb => sub {
3604 root 1.183 if ($Coro::current != $Coro::main) {
3605     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3606     unless ++$bug_warning > 10;
3607     return;
3608     }
3609    
3610 root 1.265 $NOW = $tick_start = Event::time;
3611 root 1.163
3612 root 1.133 cf::server_tick; # one server iteration
3613 root 1.245
3614 root 1.133 $RUNTIME += $TICK;
3615 root 1.35 $NEXT_TICK += $TICK;
3616    
3617 root 1.214 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3618     $NEXT_RUNTIME_WRITE = $NOW + 10;
3619     Coro::async_pool {
3620 root 1.374 $Coro::current->{desc} = "runtime saver";
3621 root 1.214 write_runtime
3622     or warn "ERROR: unable to write runtime file: $!";
3623     };
3624     }
3625    
3626 root 1.245 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3627     $sig->send;
3628     }
3629     while (my $sig = shift @WAIT_FOR_TICK) {
3630     $sig->send;
3631     }
3632    
3633 root 1.265 $NOW = Event::time;
3634    
3635     # if we are delayed by four ticks or more, skip them all
3636     $NEXT_TICK = $NOW if $NOW >= $NEXT_TICK + $TICK * 4;
3637    
3638     $TICK_WATCHER->at ($NEXT_TICK);
3639     $TICK_WATCHER->start;
3640    
3641     $LOAD = ($NOW - $tick_start) / $TICK;
3642     $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
3643    
3644 root 1.245 _post_tick;
3645 root 1.35 },
3646     );
3647    
3648 root 1.206 {
3649 root 1.363 BDB::min_parallel 8;
3650 root 1.206 BDB::max_poll_time $TICK * 0.1;
3651     $BDB_POLL_WATCHER = Event->io (
3652     reentrant => 0,
3653     fd => BDB::poll_fileno,
3654     poll => 'r',
3655     prio => 0,
3656     data => WF_AUTOCANCEL,
3657     cb => \&BDB::poll_cb,
3658     );
3659    
3660     BDB::set_sync_prepare {
3661     my $status;
3662     my $current = $Coro::current;
3663     (
3664     sub {
3665     $status = $!;
3666     $current->ready; undef $current;
3667     },
3668     sub {
3669     Coro::schedule while defined $current;
3670     $! = $status;
3671     },
3672     )
3673     };
3674 root 1.77
3675 root 1.206 unless ($DB_ENV) {
3676     $DB_ENV = BDB::db_env_create;
3677 root 1.371 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT | BDB::TXN_NOSYNC
3678     | BDB::LOG_AUTOREMOVE, 1);
3679     $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
3680     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
3681 root 1.206
3682     cf::sync_job {
3683 root 1.208 eval {
3684     BDB::db_env_open
3685     $DB_ENV,
3686 root 1.253 $BDBDIR,
3687 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
3688     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
3689     0666;
3690    
3691 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
3692 root 1.208 };
3693    
3694     cf::cleanup "db_env_open(db): $@" if $@;
3695 root 1.206 };
3696     }
3697 root 1.363
3698 root 1.371 $BDB_DEADLOCK_WATCHER = Event->timer (
3699     after => 3,
3700     interval => 1,
3701     hard => 1,
3702     prio => 0,
3703     data => WF_AUTOCANCEL,
3704     cb => sub {
3705     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
3706     },
3707     );
3708 root 1.363 $BDB_CHECKPOINT_WATCHER = Event->timer (
3709     after => 11,
3710     interval => 60,
3711     hard => 1,
3712     prio => 0,
3713     data => WF_AUTOCANCEL,
3714     cb => sub {
3715     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
3716     },
3717     );
3718     $BDB_TRICKLE_WATCHER = Event->timer (
3719     after => 5,
3720     interval => 10,
3721     hard => 1,
3722     prio => 0,
3723     data => WF_AUTOCANCEL,
3724     cb => sub {
3725     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
3726     },
3727     );
3728 root 1.206 }
3729    
3730     {
3731     IO::AIO::min_parallel 8;
3732    
3733     undef $Coro::AIO::WATCHER;
3734     IO::AIO::max_poll_time $TICK * 0.1;
3735     $AIO_POLL_WATCHER = Event->io (
3736     reentrant => 0,
3737 root 1.214 data => WF_AUTOCANCEL,
3738 root 1.206 fd => IO::AIO::poll_fileno,
3739     poll => 'r',
3740 root 1.376 prio => 0,
3741 root 1.206 cb => \&IO::AIO::poll_cb,
3742     );
3743     }
3744 root 1.108
3745 root 1.262 my $_log_backtrace;
3746    
3747 root 1.260 sub _log_backtrace {
3748     my ($msg, @addr) = @_;
3749    
3750 root 1.262 $msg =~ s/\n//;
3751 root 1.260
3752 root 1.262 # limit the # of concurrent backtraces
3753     if ($_log_backtrace < 2) {
3754     ++$_log_backtrace;
3755     async {
3756 root 1.374 $Coro::current->{desc} = "abt $msg";
3757    
3758 root 1.262 my @bt = fork_call {
3759     @addr = map { sprintf "%x", $_ } @addr;
3760     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
3761     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
3762     or die "addr2line: $!";
3763    
3764     my @funcs;
3765     my @res = <$fh>;
3766     chomp for @res;
3767     while (@res) {
3768     my ($func, $line) = splice @res, 0, 2, ();
3769     push @funcs, "[$func] $line";
3770     }
3771 root 1.260
3772 root 1.262 @funcs
3773     };
3774 root 1.260
3775 root 1.262 LOG llevInfo, "[ABT] $msg\n";
3776     LOG llevInfo, "[ABT] $_\n" for @bt;
3777     --$_log_backtrace;
3778     };
3779     } else {
3780 root 1.260 LOG llevInfo, "[ABT] $msg\n";
3781 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
3782     }
3783 root 1.260 }
3784    
3785 root 1.249 # load additional modules
3786     use cf::pod;
3787    
3788 root 1.125 END { cf::emergency_save }
3789    
3790 root 1.1 1
3791