ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.407
Committed: Sun Jan 13 09:53:53 2008 UTC (16 years, 4 months ago) by root
Branch: MAIN
CVS Tags: rel-2_4
Changes since 1.406: +5 -0 lines
Log Message:
*** empty log message ***

File Contents

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