ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.516
Committed: Thu Apr 15 06:05:52 2010 UTC (14 years, 1 month ago) by root
Branch: MAIN
Changes since 1.515: +2 -0 lines
Log Message:
attackmess parser/file format change

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