ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.522
Committed: Sat Apr 17 02:22:14 2010 UTC (14 years, 1 month ago) by root
Branch: MAIN
Changes since 1.521: +6 -2 lines
Log Message:
in_main

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