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