ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.527
Committed: Fri Apr 23 04:32:47 2010 UTC (14 years, 1 month ago) by root
Branch: MAIN
Changes since 1.526: +29 -13 lines
Log Message:
indent

File Contents

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