ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.408
Committed: Mon Jan 14 10:12:12 2008 UTC (16 years, 4 months ago) by root
Branch: MAIN
Changes since 1.407: +2 -5 lines
Log Message:
*** empty log message ***

File Contents

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