ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.521
Committed: Fri Apr 16 23:28:42 2010 UTC (14 years, 1 month ago) by root
Branch: MAIN
Changes since 1.520: +3 -0 lines
Log Message:
*** empty log message ***

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 root 1.521
1442     warn "$v->{base}: optional extension cannot be loaded, skipping.\n";
1443     delete $todo{$k};
1444 root 1.505 } else {
1445     $done{$k} = delete $todo{$k};
1446     push @EXTS, $v->{pkg};
1447     $progress = 1;
1448 root 1.278
1449 root 1.505 warn "$v->{base}: extension inactive.\n"
1450     unless $active;
1451 root 1.278 }
1452 root 1.505 }
1453    
1454     unless ($progress) {
1455     warn "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n";
1456 root 1.278
1457 root 1.505 while (my ($k, $v) = each %todo) {
1458     cf::cleanup "mandatory extension '$k' has unresolved dependencies, exiting."
1459     if exists $v->{meta}{mandatory};
1460     }
1461 root 1.278 }
1462     }
1463     };
1464 root 1.1 }
1465    
1466 root 1.8 #############################################################################
1467 root 1.70
1468 root 1.281 =back
1469    
1470 root 1.70 =head2 CORE EXTENSIONS
1471    
1472 root 1.447 Functions and methods that extend core deliantra objects.
1473 root 1.70
1474 root 1.143 =cut
1475    
1476     package cf::player;
1477    
1478 root 1.154 use Coro::AIO;
1479    
1480 root 1.95 =head3 cf::player
1481    
1482 root 1.70 =over 4
1483 root 1.22
1484 root 1.361 =item cf::player::num_playing
1485    
1486     Returns the official number of playing players, as per the Crossfire metaserver rules.
1487    
1488     =cut
1489    
1490     sub num_playing {
1491     scalar grep
1492     $_->ob->map
1493     && !$_->hidden
1494     && !$_->ob->flag (cf::FLAG_WIZ),
1495     cf::player::list
1496     }
1497    
1498 root 1.143 =item cf::player::find $login
1499 root 1.23
1500 root 1.143 Returns the given player object, loading it if necessary (might block).
1501 root 1.23
1502     =cut
1503    
1504 root 1.145 sub playerdir($) {
1505 root 1.253 "$PLAYERDIR/"
1506 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1507     }
1508    
1509 root 1.143 sub path($) {
1510 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1511    
1512 root 1.234 (playerdir $login) . "/playerdata"
1513 root 1.143 }
1514    
1515     sub find_active($) {
1516     $cf::PLAYER{$_[0]}
1517     and $cf::PLAYER{$_[0]}->active
1518     and $cf::PLAYER{$_[0]}
1519     }
1520    
1521     sub exists($) {
1522     my ($login) = @_;
1523    
1524     $cf::PLAYER{$login}
1525 root 1.452 or !aio_stat path $login
1526 root 1.143 }
1527    
1528     sub find($) {
1529     return $cf::PLAYER{$_[0]} || do {
1530     my $login = $_[0];
1531    
1532     my $guard = cf::lock_acquire "user_find:$login";
1533    
1534 root 1.151 $cf::PLAYER{$_[0]} || do {
1535 root 1.234 # rename old playerfiles to new ones
1536     #TODO: remove when no longer required
1537     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1538     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1539     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1540     aio_unlink +(playerdir $login) . "/$login.pl";
1541    
1542 root 1.356 my $f = new_from_file cf::object::thawer path $login
1543 root 1.151 or return;
1544 root 1.356
1545     my $pl = cf::player::load_pl $f
1546     or return;
1547 root 1.427
1548 root 1.356 local $cf::PLAYER_LOADING{$login} = $pl;
1549     $f->resolve_delayed_derefs;
1550 root 1.151 $cf::PLAYER{$login} = $pl
1551     }
1552     }
1553 root 1.143 }
1554    
1555 root 1.511 cf::player->attach (
1556     on_load => sub {
1557     my ($pl, $path) = @_;
1558    
1559     # restore slots saved in save, below
1560     my $slots = delete $pl->{_slots};
1561    
1562     $pl->ob->current_weapon ($slots->[0]);
1563     $pl->combat_ob ($slots->[1]);
1564     $pl->ranged_ob ($slots->[2]);
1565     },
1566     );
1567    
1568 root 1.143 sub save($) {
1569     my ($pl) = @_;
1570    
1571     return if $pl->{deny_save};
1572    
1573     my $path = path $pl;
1574     my $guard = cf::lock_acquire "user_save:$path";
1575    
1576     return if $pl->{deny_save};
1577 root 1.146
1578 root 1.154 aio_mkdir playerdir $pl, 0770;
1579 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1580    
1581 root 1.420 cf::get_slot 0.01;
1582    
1583 root 1.511 # save slots, to be restored later
1584     local $pl->{_slots} = [$pl->ob->current_weapon, $pl->combat_ob, $pl->ranged_ob];
1585    
1586 root 1.143 $pl->save_pl ($path);
1587 root 1.346 cf::cede_to_tick;
1588 root 1.143 }
1589    
1590     sub new($) {
1591     my ($login) = @_;
1592    
1593     my $self = create;
1594    
1595     $self->ob->name ($login);
1596     $self->{deny_save} = 1;
1597    
1598     $cf::PLAYER{$login} = $self;
1599    
1600     $self
1601 root 1.23 }
1602    
1603 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1604    
1605     =cut
1606    
1607     sub send_msg {
1608     my $ns = shift->ns
1609     or return;
1610     $ns->send_msg (@_);
1611     }
1612    
1613 root 1.154 =item $pl->quit_character
1614    
1615     Nukes the player without looking back. If logged in, the connection will
1616     be destroyed. May block for a long time.
1617    
1618     =cut
1619    
1620 root 1.145 sub quit_character {
1621     my ($pl) = @_;
1622    
1623 root 1.220 my $name = $pl->ob->name;
1624    
1625 root 1.145 $pl->{deny_save} = 1;
1626 root 1.443 $pl->password ("*"); # this should lock out the player until we have nuked the dir
1627 root 1.145
1628     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1629     $pl->deactivate;
1630 root 1.432 my $killer = cf::arch::get "killer_quit"; $pl->killer ($killer); $killer->destroy;
1631     $pl->ob->check_score;
1632 root 1.145 $pl->invoke (cf::EVENT_PLAYER_QUIT);
1633     $pl->ns->destroy if $pl->ns;
1634    
1635     my $path = playerdir $pl;
1636     my $temp = "$path~$cf::RUNTIME~deleting~";
1637 root 1.154 aio_rename $path, $temp;
1638 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1639     $pl->destroy;
1640 root 1.220
1641     my $prefix = qr<^~\Q$name\E/>;
1642    
1643     # nuke player maps
1644     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1645    
1646 root 1.150 IO::AIO::aio_rmtree $temp;
1647 root 1.145 }
1648    
1649 pippijn 1.221 =item $pl->kick
1650    
1651     Kicks a player out of the game. This destroys the connection.
1652    
1653     =cut
1654    
1655     sub kick {
1656     my ($pl, $kicker) = @_;
1657    
1658     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1659     $pl->killer ("kicked");
1660     $pl->ns->destroy;
1661     }
1662    
1663 root 1.154 =item cf::player::list_logins
1664    
1665     Returns am arrayref of all valid playernames in the system, can take a
1666     while and may block, so not sync_job-capable, ever.
1667    
1668     =cut
1669    
1670     sub list_logins {
1671 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1672 root 1.154 or return [];
1673    
1674     my @logins;
1675    
1676     for my $login (@$dirs) {
1677 root 1.354 my $path = path $login;
1678    
1679     # a .pst is a dead give-away for a valid player
1680 root 1.427 # if no pst file found, open and chekc for blocked users
1681     if (aio_stat "$path.pst") {
1682 root 1.354 my $fh = aio_open $path, Fcntl::O_RDONLY, 0 or next;
1683     aio_read $fh, 0, 512, my $buf, 0 or next;
1684     $buf !~ /^password -------------$/m or next; # official not-valid tag
1685     }
1686 root 1.154
1687     utf8::decode $login;
1688     push @logins, $login;
1689     }
1690    
1691     \@logins
1692     }
1693    
1694     =item $player->maps
1695    
1696 root 1.166 Returns an arrayref of map paths that are private for this
1697 root 1.154 player. May block.
1698    
1699     =cut
1700    
1701     sub maps($) {
1702     my ($pl) = @_;
1703    
1704 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1705    
1706 root 1.154 my $files = aio_readdir playerdir $pl
1707     or return;
1708    
1709     my @paths;
1710    
1711     for (@$files) {
1712     utf8::decode $_;
1713     next if /\.(?:pl|pst)$/;
1714 root 1.158 next unless /^$PATH_SEP/o;
1715 root 1.154
1716 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1717 root 1.154 }
1718    
1719     \@paths
1720     }
1721    
1722 root 1.447 =item $protocol_xml = $player->expand_cfpod ($cfpod)
1723 root 1.283
1724 root 1.447 Expand deliantra pod fragments into protocol xml.
1725 root 1.283
1726 root 1.316 =item $player->ext_reply ($msgid, @msg)
1727 root 1.95
1728     Sends an ext reply to the player.
1729    
1730     =cut
1731    
1732 root 1.316 sub ext_reply($$@) {
1733     my ($self, $id, @msg) = @_;
1734 root 1.95
1735 root 1.336 $self->ns->ext_reply ($id, @msg)
1736 root 1.95 }
1737    
1738 root 1.316 =item $player->ext_msg ($type, @msg)
1739 root 1.231
1740     Sends an ext event to the client.
1741    
1742     =cut
1743    
1744 root 1.316 sub ext_msg($$@) {
1745     my ($self, $type, @msg) = @_;
1746 root 1.231
1747 root 1.316 $self->ns->ext_msg ($type, @msg);
1748 root 1.231 }
1749    
1750 root 1.238 =head3 cf::region
1751    
1752     =over 4
1753    
1754     =cut
1755    
1756     package cf::region;
1757    
1758     =item cf::region::find_by_path $path
1759    
1760 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1761 root 1.238
1762     =cut
1763    
1764     sub find_by_path($) {
1765     my ($path) = @_;
1766    
1767     my ($match, $specificity);
1768    
1769     for my $region (list) {
1770 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1771 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1772     if $region->specificity > $specificity;
1773     }
1774     }
1775    
1776     $match
1777     }
1778 root 1.143
1779 root 1.95 =back
1780    
1781 root 1.110 =head3 cf::map
1782    
1783     =over 4
1784    
1785     =cut
1786    
1787     package cf::map;
1788    
1789     use Fcntl;
1790     use Coro::AIO;
1791    
1792 root 1.166 use overload
1793 root 1.173 '""' => \&as_string,
1794     fallback => 1;
1795 root 1.166
1796 root 1.133 our $MAX_RESET = 3600;
1797     our $DEFAULT_RESET = 3000;
1798 root 1.110
1799     sub generate_random_map {
1800 root 1.166 my ($self, $rmp) = @_;
1801 root 1.418
1802     my $lock = cf::lock_acquire "generate_random_map"; # the random map generator is NOT reentrant ATM
1803    
1804 root 1.110 # mit "rum" bekleckern, nicht
1805 root 1.166 $self->_create_random_map (
1806 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1807 root 1.508 $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle}, $rmp->{miningstyle},
1808 root 1.110 $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1809     $rmp->{exit_on_final_map},
1810     $rmp->{xsize}, $rmp->{ysize},
1811     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1812     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1813     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1814     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1815     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1816 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1817     )
1818 root 1.110 }
1819    
1820 root 1.187 =item cf::map->register ($regex, $prio)
1821    
1822     Register a handler for the map path matching the given regex at the
1823     givne priority (higher is better, built-in handlers have priority 0, the
1824     default).
1825    
1826     =cut
1827    
1828 root 1.166 sub register {
1829 root 1.187 my (undef, $regex, $prio) = @_;
1830 root 1.166 my $pkg = caller;
1831    
1832     no strict;
1833     push @{"$pkg\::ISA"}, __PACKAGE__;
1834    
1835 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1836 root 1.166 }
1837    
1838     # also paths starting with '/'
1839 root 1.187 $EXT_MAP{"cf::map"} = [0, qr{^(?=/)}];
1840 root 1.166
1841 root 1.170 sub thawer_merge {
1842 root 1.172 my ($self, $merge) = @_;
1843    
1844 root 1.170 # we have to keep some variables in memory intact
1845 root 1.172 local $self->{path};
1846     local $self->{load_path};
1847 root 1.170
1848 root 1.172 $self->SUPER::thawer_merge ($merge);
1849 root 1.170 }
1850    
1851 root 1.166 sub normalise {
1852     my ($path, $base) = @_;
1853    
1854 root 1.192 $path = "$path"; # make sure its a string
1855    
1856 root 1.199 $path =~ s/\.map$//;
1857    
1858 root 1.166 # map plan:
1859     #
1860     # /! non-realised random map exit (special hack!)
1861     # {... are special paths that are not being touched
1862     # ?xxx/... are special absolute paths
1863     # ?random/... random maps
1864     # /... normal maps
1865     # ~user/... per-player map of a specific user
1866    
1867     $path =~ s/$PATH_SEP/\//go;
1868    
1869     # treat it as relative path if it starts with
1870     # something that looks reasonable
1871     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1872     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1873    
1874     $base =~ s{[^/]+/?$}{};
1875     $path = "$base/$path";
1876     }
1877    
1878     for ($path) {
1879     redo if s{//}{/};
1880     redo if s{/\.?/}{/};
1881     redo if s{/[^/]+/\.\./}{/};
1882     }
1883    
1884     $path
1885     }
1886    
1887     sub new_from_path {
1888     my (undef, $path, $base) = @_;
1889    
1890     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1891    
1892     $path = normalise $path, $base;
1893    
1894 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1895     if ($path =~ $EXT_MAP{$pkg}[1]) {
1896 root 1.166 my $self = bless cf::map::new, $pkg;
1897     $self->{path} = $path; $self->path ($path);
1898     $self->init; # pass $1 etc.
1899     return $self;
1900     }
1901     }
1902    
1903 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1904 root 1.166 ()
1905     }
1906    
1907     sub init {
1908     my ($self) = @_;
1909    
1910     $self
1911     }
1912    
1913     sub as_string {
1914     my ($self) = @_;
1915    
1916     "$self->{path}"
1917     }
1918    
1919     # the displayed name, this is a one way mapping
1920     sub visible_name {
1921     &as_string
1922     }
1923    
1924     # the original (read-only) location
1925     sub load_path {
1926     my ($self) = @_;
1927    
1928 root 1.254 "$MAPDIR/$self->{path}.map"
1929 root 1.166 }
1930    
1931     # the temporary/swap location
1932     sub save_path {
1933     my ($self) = @_;
1934    
1935 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
1936 root 1.254 "$TMPDIR/$path.map"
1937 root 1.166 }
1938    
1939     # the unique path, undef == no special unique path
1940     sub uniq_path {
1941     my ($self) = @_;
1942    
1943 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
1944 root 1.253 "$UNIQUEDIR/$path"
1945 root 1.166 }
1946    
1947 root 1.275 sub decay_objects {
1948     my ($self) = @_;
1949    
1950     return if $self->{deny_reset};
1951    
1952     $self->do_decay_objects;
1953     }
1954    
1955 root 1.166 sub unlink_save {
1956     my ($self) = @_;
1957    
1958     utf8::encode (my $save = $self->save_path);
1959 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1960     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1961 root 1.166 }
1962    
1963     sub load_header_from($) {
1964     my ($self, $path) = @_;
1965 root 1.110
1966     utf8::encode $path;
1967 root 1.356 my $f = new_from_file cf::object::thawer $path
1968     or return;
1969 root 1.110
1970 root 1.356 $self->_load_header ($f)
1971 root 1.110 or return;
1972    
1973 root 1.356 local $MAP_LOADING{$self->{path}} = $self;
1974     $f->resolve_delayed_derefs;
1975    
1976 root 1.166 $self->{load_path} = $path;
1977 root 1.135
1978 root 1.166 1
1979     }
1980 root 1.110
1981 root 1.188 sub load_header_orig {
1982 root 1.166 my ($self) = @_;
1983 root 1.110
1984 root 1.166 $self->load_header_from ($self->load_path)
1985 root 1.110 }
1986    
1987 root 1.188 sub load_header_temp {
1988 root 1.166 my ($self) = @_;
1989 root 1.110
1990 root 1.166 $self->load_header_from ($self->save_path)
1991     }
1992 root 1.110
1993 root 1.188 sub prepare_temp {
1994     my ($self) = @_;
1995    
1996     $self->last_access ((delete $self->{last_access})
1997     || $cf::RUNTIME); #d#
1998     # safety
1999     $self->{instantiate_time} = $cf::RUNTIME
2000     if $self->{instantiate_time} > $cf::RUNTIME;
2001     }
2002    
2003     sub prepare_orig {
2004     my ($self) = @_;
2005    
2006     $self->{load_original} = 1;
2007     $self->{instantiate_time} = $cf::RUNTIME;
2008     $self->last_access ($cf::RUNTIME);
2009     $self->instantiate;
2010     }
2011    
2012 root 1.166 sub load_header {
2013     my ($self) = @_;
2014 root 1.110
2015 root 1.188 if ($self->load_header_temp) {
2016     $self->prepare_temp;
2017 root 1.166 } else {
2018 root 1.188 $self->load_header_orig
2019 root 1.166 or return;
2020 root 1.188 $self->prepare_orig;
2021 root 1.166 }
2022 root 1.120
2023 root 1.275 $self->{deny_reset} = 1
2024     if $self->no_reset;
2025    
2026 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
2027     unless $self->default_region;
2028    
2029 root 1.166 1
2030     }
2031 root 1.110
2032 root 1.166 sub find;
2033     sub find {
2034     my ($path, $origin) = @_;
2035 root 1.134
2036 root 1.166 $path = normalise $path, $origin && $origin->path;
2037 root 1.110
2038 root 1.459 my $guard1 = cf::lock_acquire "map_data:$path";#d#remove
2039     my $guard2 = cf::lock_acquire "map_find:$path";
2040 root 1.110
2041 root 1.166 $cf::MAP{$path} || do {
2042     my $map = new_from_path cf::map $path
2043     or return;
2044 root 1.110
2045 root 1.116 $map->{last_save} = $cf::RUNTIME;
2046 root 1.110
2047 root 1.166 $map->load_header
2048     or return;
2049 root 1.134
2050 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
2051 root 1.185 # doing this can freeze the server in a sync job, obviously
2052     #$cf::WAIT_FOR_TICK->wait;
2053 root 1.429 undef $guard2;
2054 root 1.358 undef $guard1;
2055 root 1.112 $map->reset;
2056 root 1.192 return find $path;
2057 root 1.112 }
2058 root 1.110
2059 root 1.166 $cf::MAP{$path} = $map
2060 root 1.110 }
2061     }
2062    
2063 root 1.500 sub pre_load { }
2064     #sub post_load { } # XS
2065 root 1.188
2066 root 1.110 sub load {
2067     my ($self) = @_;
2068    
2069 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
2070    
2071 root 1.120 my $path = $self->{path};
2072    
2073 root 1.256 {
2074 root 1.357 my $guard = cf::lock_acquire "map_data:$path";
2075 root 1.256
2076 root 1.357 return unless $self->valid;
2077 root 1.360 return unless $self->in_memory == cf::MAP_SWAPPED;
2078 root 1.110
2079 root 1.256 $self->in_memory (cf::MAP_LOADING);
2080 root 1.110
2081 root 1.256 $self->alloc;
2082 root 1.188
2083 root 1.256 $self->pre_load;
2084 root 1.346 cf::cede_to_tick;
2085 root 1.188
2086 root 1.356 my $f = new_from_file cf::object::thawer $self->{load_path};
2087     $f->skip_block;
2088     $self->_load_objects ($f)
2089 root 1.256 or return;
2090 root 1.110
2091 root 1.439 $self->post_load_original
2092 root 1.256 if delete $self->{load_original};
2093 root 1.111
2094 root 1.256 if (my $uniq = $self->uniq_path) {
2095     utf8::encode $uniq;
2096 root 1.356 unless (aio_stat $uniq) {
2097     if (my $f = new_from_file cf::object::thawer $uniq) {
2098     $self->clear_unique_items;
2099     $self->_load_objects ($f);
2100     $f->resolve_delayed_derefs;
2101     }
2102 root 1.256 }
2103 root 1.110 }
2104    
2105 root 1.356 $f->resolve_delayed_derefs;
2106    
2107 root 1.346 cf::cede_to_tick;
2108 root 1.256 # now do the right thing for maps
2109     $self->link_multipart_objects;
2110 root 1.110 $self->difficulty ($self->estimate_difficulty)
2111     unless $self->difficulty;
2112 root 1.346 cf::cede_to_tick;
2113 root 1.256
2114     unless ($self->{deny_activate}) {
2115     $self->decay_objects;
2116     $self->fix_auto_apply;
2117     $self->update_buttons;
2118 root 1.346 cf::cede_to_tick;
2119 root 1.256 $self->activate;
2120     }
2121    
2122 root 1.325 $self->{last_save} = $cf::RUNTIME;
2123     $self->last_access ($cf::RUNTIME);
2124 root 1.324
2125 root 1.420 $self->in_memory (cf::MAP_ACTIVE);
2126 root 1.110 }
2127    
2128 root 1.188 $self->post_load;
2129 root 1.166 }
2130    
2131 root 1.507 # customize the map for a given player, i.e.
2132     # return the _real_ map. used by e.g. per-player
2133     # maps to change the path to ~playername/mappath
2134 root 1.166 sub customise_for {
2135     my ($self, $ob) = @_;
2136    
2137     return find "~" . $ob->name . "/" . $self->{path}
2138     if $self->per_player;
2139 root 1.134
2140 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
2141     # if $self->per_party;
2142    
2143 root 1.166 $self
2144 root 1.110 }
2145    
2146 root 1.157 # find and load all maps in the 3x3 area around a map
2147 root 1.333 sub load_neighbours {
2148 root 1.157 my ($map) = @_;
2149    
2150 root 1.333 my @neigh; # diagonal neighbours
2151 root 1.157
2152     for (0 .. 3) {
2153     my $neigh = $map->tile_path ($_)
2154     or next;
2155     $neigh = find $neigh, $map
2156     or next;
2157     $neigh->load;
2158    
2159 root 1.333 push @neigh,
2160     [$neigh->tile_path (($_ + 3) % 4), $neigh],
2161     [$neigh->tile_path (($_ + 1) % 4), $neigh];
2162 root 1.157 }
2163    
2164 root 1.333 for (grep defined $_->[0], @neigh) {
2165     my ($path, $origin) = @$_;
2166     my $neigh = find $path, $origin
2167 root 1.157 or next;
2168     $neigh->load;
2169     }
2170     }
2171    
2172 root 1.133 sub find_sync {
2173 root 1.110 my ($path, $origin) = @_;
2174    
2175 root 1.157 cf::sync_job { find $path, $origin }
2176 root 1.133 }
2177    
2178     sub do_load_sync {
2179     my ($map) = @_;
2180 root 1.110
2181 root 1.339 cf::LOG cf::llevDebug | cf::logBacktrace, "do_load_sync"
2182 root 1.342 if $Coro::current == $Coro::main;
2183 root 1.339
2184 root 1.133 cf::sync_job { $map->load };
2185 root 1.110 }
2186    
2187 root 1.157 our %MAP_PREFETCH;
2188 root 1.183 our $MAP_PREFETCHER = undef;
2189 root 1.157
2190     sub find_async {
2191 root 1.339 my ($path, $origin, $load) = @_;
2192 root 1.157
2193 root 1.166 $path = normalise $path, $origin && $origin->{path};
2194 root 1.157
2195 root 1.166 if (my $map = $cf::MAP{$path}) {
2196 root 1.420 return $map if !$load || $map->in_memory == cf::MAP_ACTIVE;
2197 root 1.157 }
2198    
2199 root 1.339 $MAP_PREFETCH{$path} |= $load;
2200    
2201 root 1.183 $MAP_PREFETCHER ||= cf::async {
2202 root 1.374 $Coro::current->{desc} = "map prefetcher";
2203    
2204 root 1.183 while (%MAP_PREFETCH) {
2205 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
2206     if (my $map = find $k) {
2207     $map->load if $v;
2208 root 1.308 }
2209 root 1.183
2210 root 1.339 delete $MAP_PREFETCH{$k};
2211 root 1.183 }
2212     }
2213     undef $MAP_PREFETCHER;
2214     };
2215 root 1.189 $MAP_PREFETCHER->prio (6);
2216 root 1.157
2217     ()
2218     }
2219    
2220 root 1.518 # common code, used by both ->save and ->swapout
2221     sub _save {
2222 root 1.110 my ($self) = @_;
2223    
2224     $self->{last_save} = $cf::RUNTIME;
2225    
2226     return unless $self->dirty;
2227    
2228 root 1.166 my $save = $self->save_path; utf8::encode $save;
2229     my $uniq = $self->uniq_path; utf8::encode $uniq;
2230 root 1.117
2231 root 1.110 $self->{load_path} = $save;
2232    
2233     return if $self->{deny_save};
2234    
2235 root 1.132 local $self->{last_access} = $self->last_access;#d#
2236    
2237 root 1.143 cf::async {
2238 root 1.374 $Coro::current->{desc} = "map player save";
2239 root 1.143 $_->contr->save for $self->players;
2240     };
2241    
2242 root 1.420 cf::get_slot 0.02;
2243    
2244 root 1.110 if ($uniq) {
2245 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2246     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2247 root 1.110 } else {
2248 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2249 root 1.110 }
2250     }
2251    
2252 root 1.518 sub save {
2253     my ($self) = @_;
2254    
2255     my $lock = cf::lock_acquire "map_data:$self->{path}";
2256    
2257     $self->_save;
2258     }
2259    
2260 root 1.110 sub swap_out {
2261     my ($self) = @_;
2262    
2263 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2264 root 1.137
2265 root 1.420 return if $self->in_memory != cf::MAP_ACTIVE;
2266 root 1.110 return if $self->{deny_save};
2267 root 1.518 return if $self->players;
2268 root 1.110
2269 root 1.518 # first deactivate the map and "unlink" it from the core
2270     $self->deactivate;
2271     $_->clear_links_to ($self) for values %cf::MAP;
2272 root 1.359 $self->in_memory (cf::MAP_SWAPPED);
2273    
2274 root 1.518 # then atomically save
2275     $self->_save;
2276    
2277     # then free the map
2278 root 1.110 $self->clear;
2279     }
2280    
2281 root 1.112 sub reset_at {
2282     my ($self) = @_;
2283 root 1.110
2284     # TODO: safety, remove and allow resettable per-player maps
2285 root 1.114 return 1e99 if $self->{deny_reset};
2286 root 1.110
2287 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2288 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2289 root 1.110
2290 root 1.112 $time + $to
2291     }
2292    
2293     sub should_reset {
2294     my ($self) = @_;
2295    
2296     $self->reset_at <= $cf::RUNTIME
2297 root 1.111 }
2298    
2299 root 1.110 sub reset {
2300     my ($self) = @_;
2301    
2302 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2303 root 1.137
2304 root 1.110 return if $self->players;
2305    
2306 root 1.478 warn "resetting map ", $self->path, "\n";
2307 root 1.110
2308 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2309    
2310     # need to save uniques path
2311     unless ($self->{deny_save}) {
2312     my $uniq = $self->uniq_path; utf8::encode $uniq;
2313    
2314     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2315     if $uniq;
2316     }
2317    
2318 root 1.111 delete $cf::MAP{$self->path};
2319 root 1.110
2320 root 1.358 $self->deactivate;
2321 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2322 root 1.167 $self->clear;
2323    
2324 root 1.166 $self->unlink_save;
2325 root 1.111 $self->destroy;
2326 root 1.110 }
2327    
2328 root 1.114 my $nuke_counter = "aaaa";
2329    
2330     sub nuke {
2331     my ($self) = @_;
2332    
2333 root 1.349 {
2334     my $lock = cf::lock_acquire "map_data:$self->{path}";
2335    
2336     delete $cf::MAP{$self->path};
2337 root 1.174
2338 root 1.351 $self->unlink_save;
2339    
2340 root 1.349 bless $self, "cf::map";
2341     delete $self->{deny_reset};
2342     $self->{deny_save} = 1;
2343     $self->reset_timeout (1);
2344     $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2345 root 1.174
2346 root 1.349 $cf::MAP{$self->path} = $self;
2347     }
2348 root 1.174
2349 root 1.114 $self->reset; # polite request, might not happen
2350     }
2351    
2352 root 1.276 =item $maps = cf::map::tmp_maps
2353    
2354     Returns an arrayref with all map paths of currently instantiated and saved
2355 root 1.277 maps. May block.
2356 root 1.276
2357     =cut
2358    
2359     sub tmp_maps() {
2360     [
2361     map {
2362     utf8::decode $_;
2363 root 1.277 /\.map$/
2364 root 1.276 ? normalise $_
2365     : ()
2366     } @{ aio_readdir $TMPDIR or [] }
2367     ]
2368     }
2369    
2370 root 1.277 =item $maps = cf::map::random_maps
2371    
2372     Returns an arrayref with all map paths of currently instantiated and saved
2373     random maps. May block.
2374    
2375     =cut
2376    
2377     sub random_maps() {
2378     [
2379     map {
2380     utf8::decode $_;
2381     /\.map$/
2382     ? normalise "?random/$_"
2383     : ()
2384     } @{ aio_readdir $RANDOMDIR or [] }
2385     ]
2386     }
2387    
2388 root 1.158 =item cf::map::unique_maps
2389    
2390 root 1.166 Returns an arrayref of paths of all shared maps that have
2391 root 1.158 instantiated unique items. May block.
2392    
2393     =cut
2394    
2395     sub unique_maps() {
2396 root 1.276 [
2397     map {
2398     utf8::decode $_;
2399 root 1.419 s/\.map$//; # TODO future compatibility hack
2400     /\.pst$/ || !/^$PATH_SEP/o # TODO unique maps apparebntly lack the .map suffix :/
2401     ? ()
2402     : normalise $_
2403 root 1.276 } @{ aio_readdir $UNIQUEDIR or [] }
2404     ]
2405 root 1.158 }
2406    
2407 root 1.489 =item cf::map::static_maps
2408    
2409     Returns an arrayref if paths of all static maps (all preinstalled F<.map>
2410 root 1.491 file in the shared directory excluding F</styles> and F</editor>). May
2411     block.
2412 root 1.489
2413     =cut
2414    
2415     sub static_maps() {
2416     my @dirs = "";
2417     my @maps;
2418    
2419     while (@dirs) {
2420     my $dir = shift @dirs;
2421    
2422 root 1.491 next if $dir eq "/styles" || $dir eq "/editor";
2423 root 1.490
2424 root 1.489 my ($dirs, $files) = Coro::AIO::aio_scandir "$MAPDIR$dir", 2
2425     or return;
2426    
2427     for (@$files) {
2428     s/\.map$// or next;
2429     utf8::decode $_;
2430     push @maps, "$dir/$_";
2431     }
2432    
2433     push @dirs, map "$dir/$_", @$dirs;
2434     }
2435    
2436     \@maps
2437     }
2438    
2439 root 1.155 =back
2440    
2441     =head3 cf::object
2442    
2443     =cut
2444    
2445     package cf::object;
2446    
2447     =over 4
2448    
2449     =item $ob->inv_recursive
2450 root 1.110
2451 root 1.419 Returns the inventory of the object I<and> their inventories, recursively,
2452     but I<not> the object itself.
2453 root 1.110
2454 root 1.155 =cut
2455 root 1.144
2456 root 1.155 sub inv_recursive_;
2457     sub inv_recursive_ {
2458     map { $_, inv_recursive_ $_->inv } @_
2459     }
2460 root 1.110
2461 root 1.155 sub inv_recursive {
2462     inv_recursive_ inv $_[0]
2463 root 1.110 }
2464    
2465 root 1.356 =item $ref = $ob->ref
2466    
2467 root 1.419 Creates and returns a persistent reference to an object that can be stored as a string.
2468 root 1.356
2469     =item $ob = cf::object::deref ($refstring)
2470    
2471     returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2472     even if the object actually exists. May block.
2473    
2474     =cut
2475    
2476     sub deref {
2477     my ($ref) = @_;
2478    
2479 root 1.377 if ($ref =~ m{^player\/(<1\.[0-9a-f]+>)/(.*)$}) {
2480 root 1.356 my ($uuid, $name) = ($1, $2);
2481     my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2482     or return;
2483     $pl->ob->uuid eq $uuid
2484     or return;
2485    
2486     $pl->ob
2487     } else {
2488     warn "$ref: cannot resolve object reference\n";
2489     undef
2490     }
2491     }
2492    
2493 root 1.110 package cf;
2494    
2495     =back
2496    
2497 root 1.95 =head3 cf::object::player
2498    
2499     =over 4
2500    
2501 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2502 root 1.28
2503     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2504     can be C<undef>. Does the right thing when the player is currently in a
2505     dialogue with the given NPC character.
2506    
2507     =cut
2508    
2509 root 1.428 our $SAY_CHANNEL = {
2510     id => "say",
2511     title => "Map",
2512     reply => "say ",
2513 root 1.468 tooltip => "Things said to and replied from NPCs near you and other players on the same map only.",
2514 root 1.428 };
2515    
2516     our $CHAT_CHANNEL = {
2517     id => "chat",
2518     title => "Chat",
2519     reply => "chat ",
2520     tooltip => "Player chat and shouts, global to the server.",
2521     };
2522    
2523 root 1.22 # rough implementation of a future "reply" method that works
2524     # with dialog boxes.
2525 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2526 root 1.23 sub cf::object::player::reply($$$;$) {
2527     my ($self, $npc, $msg, $flags) = @_;
2528    
2529     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2530 root 1.22
2531 root 1.24 if ($self->{record_replies}) {
2532     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2533 elmex 1.282
2534 root 1.24 } else {
2535 elmex 1.282 my $pl = $self->contr;
2536    
2537     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2538 root 1.316 my $dialog = $pl->{npc_dialog};
2539     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2540 elmex 1.282
2541     } else {
2542     $msg = $npc->name . " says: $msg" if $npc;
2543 root 1.428 $self->send_msg ($SAY_CHANNEL => $msg, $flags);
2544 elmex 1.282 }
2545 root 1.24 }
2546 root 1.22 }
2547    
2548 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2549    
2550     =cut
2551    
2552     sub cf::object::send_msg {
2553     my $pl = shift->contr
2554     or return;
2555     $pl->send_msg (@_);
2556     }
2557    
2558 root 1.79 =item $player_object->may ("access")
2559    
2560     Returns wether the given player is authorized to access resource "access"
2561     (e.g. "command_wizcast").
2562    
2563     =cut
2564    
2565     sub cf::object::player::may {
2566     my ($self, $access) = @_;
2567    
2568     $self->flag (cf::FLAG_WIZ) ||
2569     (ref $cf::CFG{"may_$access"}
2570     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2571     : $cf::CFG{"may_$access"})
2572     }
2573 root 1.70
2574 root 1.115 =item $player_object->enter_link
2575    
2576     Freezes the player and moves him/her to a special map (C<{link}>).
2577    
2578 root 1.446 The player should be reasonably safe there for short amounts of time (e.g.
2579     for loading a map). You I<MUST> call C<leave_link> as soon as possible,
2580     though, as the palyer cannot control the character while it is on the link
2581     map.
2582 root 1.115
2583 root 1.166 Will never block.
2584    
2585 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2586    
2587 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2588     map. If the map is not valid (or omitted), the player will be moved back
2589     to the location he/she was before the call to C<enter_link>, or, if that
2590     fails, to the emergency map position.
2591 root 1.115
2592     Might block.
2593    
2594     =cut
2595    
2596 root 1.166 sub link_map {
2597     unless ($LINK_MAP) {
2598     $LINK_MAP = cf::map::find "{link}"
2599 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2600 root 1.166 $LINK_MAP->load;
2601     }
2602    
2603     $LINK_MAP
2604     }
2605    
2606 root 1.110 sub cf::object::player::enter_link {
2607     my ($self) = @_;
2608    
2609 root 1.259 $self->deactivate_recursive;
2610 root 1.258
2611 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2612 root 1.110
2613 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2614 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2615 root 1.110
2616 root 1.519 $self->enter_map ($LINK_MAP || link_map, 3, 3);
2617 root 1.110 }
2618    
2619     sub cf::object::player::leave_link {
2620     my ($self, $map, $x, $y) = @_;
2621    
2622 root 1.270 return unless $self->contr->active;
2623    
2624 root 1.110 my $link_pos = delete $self->{_link_pos};
2625    
2626     unless ($map) {
2627     # restore original map position
2628     ($map, $x, $y) = @{ $link_pos || [] };
2629 root 1.133 $map = cf::map::find $map;
2630 root 1.110
2631     unless ($map) {
2632     ($map, $x, $y) = @$EMERGENCY_POSITION;
2633 root 1.133 $map = cf::map::find $map
2634 root 1.110 or die "FATAL: cannot load emergency map\n";
2635     }
2636     }
2637    
2638     ($x, $y) = (-1, -1)
2639     unless (defined $x) && (defined $y);
2640    
2641     # use -1 or undef as default coordinates, not 0, 0
2642     ($x, $y) = ($map->enter_x, $map->enter_y)
2643 root 1.492 if $x <= 0 && $y <= 0;
2644 root 1.110
2645     $map->load;
2646 root 1.333 $map->load_neighbours;
2647 root 1.110
2648 root 1.143 return unless $self->contr->active;
2649 root 1.215
2650     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2651 root 1.110 $self->enter_map ($map, $x, $y);
2652 root 1.476
2653     # only activate afterwards, to support waiting in hooks
2654     $self->activate_recursive;
2655 root 1.110 }
2656    
2657 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2658 root 1.268
2659     Moves the player to the given map-path and coordinates by first freezing
2660     her, loading and preparing them map, calling the provided $check callback
2661     that has to return the map if sucecssful, and then unfreezes the player on
2662 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2663     be called at the end of this process.
2664 root 1.110
2665 root 1.436 Note that $check will be called with a potentially non-loaded map, so if
2666     it needs a loaded map it has to call C<< ->load >>.
2667    
2668 root 1.110 =cut
2669    
2670 root 1.270 our $GOTOGEN;
2671    
2672 root 1.136 sub cf::object::player::goto {
2673 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2674 root 1.268
2675 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2676     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2677    
2678 root 1.110 $self->enter_link;
2679    
2680 root 1.140 (async {
2681 root 1.374 $Coro::current->{desc} = "player::goto $path $x $y";
2682    
2683 root 1.365 # *tag paths override both path and x|y
2684     if ($path =~ /^\*(.*)$/) {
2685     if (my @obs = grep $_->map, ext::map_tags::find $1) {
2686     my $ob = $obs[rand @obs];
2687 root 1.366
2688 root 1.367 # see if we actually can go there
2689 root 1.368 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2690     $ob = $obs[rand @obs];
2691 root 1.369 } else {
2692     $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2693 root 1.368 }
2694 root 1.369 # else put us there anyways for now #d#
2695 root 1.366
2696 root 1.365 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2697 root 1.369 } else {
2698     ($path, $x, $y) = (undef, undef, undef);
2699 root 1.365 }
2700     }
2701    
2702 root 1.197 my $map = eval {
2703 root 1.369 my $map = defined $path ? cf::map::find $path : undef;
2704 root 1.268
2705     if ($map) {
2706     $map = $map->customise_for ($self);
2707     $map = $check->($map) if $check && $map;
2708     } else {
2709 root 1.369 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2710 root 1.268 }
2711    
2712 root 1.197 $map
2713 root 1.268 };
2714    
2715     if ($@) {
2716     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2717     LOG llevError | logBacktrace, Carp::longmess $@;
2718     }
2719 root 1.115
2720 root 1.270 if ($gen == $self->{_goto_generation}) {
2721     delete $self->{_goto_generation};
2722     $self->leave_link ($map, $x, $y);
2723     }
2724 root 1.306
2725     $done->() if $done;
2726 root 1.110 })->prio (1);
2727     }
2728    
2729     =item $player_object->enter_exit ($exit_object)
2730    
2731     =cut
2732    
2733     sub parse_random_map_params {
2734     my ($spec) = @_;
2735    
2736     my $rmp = { # defaults
2737 root 1.181 xsize => (cf::rndm 15, 40),
2738     ysize => (cf::rndm 15, 40),
2739     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2740 root 1.182 #layout => string,
2741 root 1.110 };
2742    
2743     for (split /\n/, $spec) {
2744     my ($k, $v) = split /\s+/, $_, 2;
2745    
2746     $rmp->{lc $k} = $v if (length $k) && (length $v);
2747     }
2748    
2749     $rmp
2750     }
2751    
2752     sub prepare_random_map {
2753     my ($exit) = @_;
2754    
2755     # all this does is basically replace the /! path by
2756     # a new random map path (?random/...) with a seed
2757     # that depends on the exit object
2758    
2759     my $rmp = parse_random_map_params $exit->msg;
2760    
2761     if ($exit->map) {
2762 root 1.198 $rmp->{region} = $exit->region->name;
2763 root 1.110 $rmp->{origin_map} = $exit->map->path;
2764     $rmp->{origin_x} = $exit->x;
2765     $rmp->{origin_y} = $exit->y;
2766 root 1.430
2767     $exit->map->touch;
2768 root 1.110 }
2769    
2770     $rmp->{random_seed} ||= $exit->random_seed;
2771    
2772 root 1.422 my $data = JSON::XS->new->utf8->pretty->canonical->encode ($rmp);
2773 root 1.110 my $md5 = Digest::MD5::md5_hex $data;
2774 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2775 root 1.110
2776 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2777 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2778 root 1.177 undef $fh;
2779     aio_rename "$meta~", $meta;
2780 root 1.110
2781 root 1.430 my $slaying = "?random/$md5";
2782    
2783     if ($exit->valid) {
2784     $exit->slaying ("?random/$md5");
2785     $exit->msg (undef);
2786     }
2787 root 1.110 }
2788     }
2789    
2790     sub cf::object::player::enter_exit {
2791     my ($self, $exit) = @_;
2792    
2793     return unless $self->type == cf::PLAYER;
2794    
2795 root 1.430 $self->enter_link;
2796    
2797     (async {
2798     $Coro::current->{desc} = "enter_exit";
2799    
2800     unless (eval {
2801     $self->deactivate_recursive; # just to be sure
2802 root 1.195
2803 root 1.430 # random map handling
2804     {
2805     my $guard = cf::lock_acquire "exit_prepare:$exit";
2806 root 1.195
2807 root 1.430 prepare_random_map $exit
2808     if $exit->slaying eq "/!";
2809     }
2810 root 1.110
2811 root 1.430 my $map = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2812     my $x = $exit->stats->hp;
2813     my $y = $exit->stats->sp;
2814 root 1.296
2815 root 1.430 $self->goto ($map, $x, $y);
2816 root 1.374
2817 root 1.430 # if exit is damned, update players death & WoR home-position
2818     $self->contr->savebed ($map, $x, $y)
2819     if $exit->flag (cf::FLAG_DAMNED);
2820 root 1.110
2821 root 1.430 1
2822 root 1.110 }) {
2823 root 1.447 $self->message ("Something went wrong deep within the deliantra server. "
2824 root 1.233 . "I'll try to bring you back to the map you were before. "
2825     . "Please report this to the dungeon master!",
2826     cf::NDI_UNIQUE | cf::NDI_RED);
2827 root 1.110
2828     warn "ERROR in enter_exit: $@";
2829     $self->leave_link;
2830     }
2831     })->prio (1);
2832     }
2833    
2834 root 1.95 =head3 cf::client
2835    
2836     =over 4
2837    
2838     =item $client->send_drawinfo ($text, $flags)
2839    
2840     Sends a drawinfo packet to the client. Circumvents output buffering so
2841     should not be used under normal circumstances.
2842    
2843 root 1.70 =cut
2844    
2845 root 1.95 sub cf::client::send_drawinfo {
2846     my ($self, $text, $flags) = @_;
2847    
2848     utf8::encode $text;
2849 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2850 root 1.95 }
2851    
2852 root 1.494 =item $client->send_big_packet ($pkt)
2853    
2854     Like C<send_packet>, but tries to compress large packets, and fragments
2855     them as required.
2856    
2857     =cut
2858    
2859     our $MAXFRAGSIZE = cf::MAXSOCKBUF - 64;
2860    
2861     sub cf::client::send_big_packet {
2862     my ($self, $pkt) = @_;
2863    
2864     # try lzf for large packets
2865     $pkt = "lzf " . Compress::LZF::compress $pkt
2866     if 1024 <= length $pkt and $self->{can_lzf};
2867    
2868     # split very large packets
2869     if ($MAXFRAGSIZE < length $pkt and $self->{can_lzf}) {
2870     $self->send_packet ("frag $_") for unpack "(a$MAXFRAGSIZE)*", $pkt;
2871     $pkt = "frag";
2872     }
2873    
2874     $self->send_packet ($pkt);
2875     }
2876    
2877 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2878 root 1.283
2879     Send a drawinfo or msg packet to the client, formatting the msg for the
2880     client if neccessary. C<$type> should be a string identifying the type of
2881     the message, with C<log> being the default. If C<$color> is negative, suppress
2882     the message unless the client supports the msg packet.
2883    
2884     =cut
2885    
2886 root 1.391 # non-persistent channels (usually the info channel)
2887 root 1.350 our %CHANNEL = (
2888 root 1.486 "c/motd" => {
2889     id => "infobox",
2890     title => "MOTD",
2891     reply => undef,
2892     tooltip => "The message of the day",
2893     },
2894 root 1.350 "c/identify" => {
2895 root 1.375 id => "infobox",
2896 root 1.350 title => "Identify",
2897     reply => undef,
2898     tooltip => "Items recently identified",
2899     },
2900 root 1.352 "c/examine" => {
2901 root 1.375 id => "infobox",
2902 root 1.352 title => "Examine",
2903     reply => undef,
2904     tooltip => "Signs and other items you examined",
2905     },
2906 root 1.487 "c/shopinfo" => {
2907     id => "infobox",
2908     title => "Shop Info",
2909     reply => undef,
2910     tooltip => "What your bargaining skill tells you about the shop",
2911     },
2912 root 1.389 "c/book" => {
2913     id => "infobox",
2914     title => "Book",
2915     reply => undef,
2916     tooltip => "The contents of a note or book",
2917     },
2918 root 1.375 "c/lookat" => {
2919     id => "infobox",
2920     title => "Look",
2921     reply => undef,
2922     tooltip => "What you saw there",
2923     },
2924 root 1.390 "c/who" => {
2925     id => "infobox",
2926     title => "Players",
2927     reply => undef,
2928     tooltip => "Shows players who are currently online",
2929     },
2930     "c/body" => {
2931     id => "infobox",
2932     title => "Body Parts",
2933     reply => undef,
2934     tooltip => "Shows which body parts you posess and are available",
2935     },
2936 root 1.465 "c/statistics" => {
2937     id => "infobox",
2938     title => "Statistics",
2939     reply => undef,
2940     tooltip => "Shows your primary statistics",
2941     },
2942 root 1.450 "c/skills" => {
2943     id => "infobox",
2944     title => "Skills",
2945     reply => undef,
2946     tooltip => "Shows your experience per skill and item power",
2947     },
2948 root 1.470 "c/shopitems" => {
2949     id => "infobox",
2950     title => "Shop Items",
2951     reply => undef,
2952     tooltip => "Shows the items currently for sale in this shop",
2953     },
2954 root 1.465 "c/resistances" => {
2955     id => "infobox",
2956     title => "Resistances",
2957     reply => undef,
2958     tooltip => "Shows your resistances",
2959     },
2960     "c/pets" => {
2961     id => "infobox",
2962     title => "Pets",
2963     reply => undef,
2964     tooltip => "Shows information abotu your pets/a specific pet",
2965     },
2966 root 1.471 "c/perceiveself" => {
2967     id => "infobox",
2968     title => "Perceive Self",
2969     reply => undef,
2970     tooltip => "You gained detailed knowledge about yourself",
2971     },
2972 root 1.390 "c/uptime" => {
2973     id => "infobox",
2974     title => "Uptime",
2975     reply => undef,
2976 root 1.391 tooltip => "How long the server has been running since last restart",
2977 root 1.390 },
2978     "c/mapinfo" => {
2979     id => "infobox",
2980     title => "Map Info",
2981     reply => undef,
2982     tooltip => "Information related to the maps",
2983     },
2984 root 1.426 "c/party" => {
2985     id => "party",
2986     title => "Party",
2987     reply => "gsay ",
2988     tooltip => "Messages and chat related to your party",
2989     },
2990 root 1.464 "c/death" => {
2991     id => "death",
2992     title => "Death",
2993     reply => undef,
2994     tooltip => "Reason for and more info about your most recent death",
2995     },
2996 root 1.462 "c/say" => $SAY_CHANNEL,
2997     "c/chat" => $CHAT_CHANNEL,
2998 root 1.350 );
2999    
3000 root 1.283 sub cf::client::send_msg {
3001 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
3002 root 1.283
3003 root 1.447 $msg = $self->pl->expand_cfpod ($msg)
3004     unless $color & cf::NDI_VERBATIM;
3005 root 1.283
3006 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
3007 root 1.311
3008 root 1.350 # check predefined channels, for the benefit of C
3009 root 1.375 if ($CHANNEL{$channel}) {
3010     $channel = $CHANNEL{$channel};
3011    
3012 root 1.463 $self->ext_msg (channel_info => $channel);
3013 root 1.375 $channel = $channel->{id};
3014 root 1.350
3015 root 1.375 } elsif (ref $channel) {
3016 root 1.311 # send meta info to client, if not yet sent
3017     unless (exists $self->{channel}{$channel->{id}}) {
3018     $self->{channel}{$channel->{id}} = $channel;
3019 root 1.463 $self->ext_msg (channel_info => $channel);
3020 root 1.311 }
3021    
3022     $channel = $channel->{id};
3023     }
3024    
3025 root 1.313 return unless @extra || length $msg;
3026    
3027 root 1.463 # default colour, mask it out
3028     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
3029     if $color & cf::NDI_DEF;
3030    
3031     my $pkt = "msg "
3032     . $self->{json_coder}->encode (
3033     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]
3034     );
3035    
3036 root 1.494 $self->send_big_packet ($pkt);
3037 root 1.283 }
3038    
3039 root 1.316 =item $client->ext_msg ($type, @msg)
3040 root 1.232
3041 root 1.287 Sends an ext event to the client.
3042 root 1.232
3043     =cut
3044    
3045 root 1.316 sub cf::client::ext_msg($$@) {
3046     my ($self, $type, @msg) = @_;
3047 root 1.232
3048 root 1.343 if ($self->extcmd == 2) {
3049 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
3050 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
3051 root 1.316 push @msg, msgtype => "event_$type";
3052 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3053 root 1.316 }
3054 root 1.232 }
3055 root 1.95
3056 root 1.336 =item $client->ext_reply ($msgid, @msg)
3057    
3058     Sends an ext reply to the client.
3059    
3060     =cut
3061    
3062     sub cf::client::ext_reply($$@) {
3063     my ($self, $id, @msg) = @_;
3064    
3065     if ($self->extcmd == 2) {
3066 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
3067 root 1.343 } elsif ($self->extcmd == 1) {
3068 root 1.336 #TODO: version 1, remove
3069     unshift @msg, msgtype => "reply", msgid => $id;
3070 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3071 root 1.336 }
3072     }
3073    
3074 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
3075    
3076     Queues a query to the client, calling the given callback with
3077     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
3078     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
3079    
3080 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
3081     become reliable at some point in the future.
3082 root 1.95
3083     =cut
3084    
3085     sub cf::client::query {
3086     my ($self, $flags, $text, $cb) = @_;
3087    
3088     return unless $self->state == ST_PLAYING
3089     || $self->state == ST_SETUP
3090     || $self->state == ST_CUSTOM;
3091    
3092     $self->state (ST_CUSTOM);
3093    
3094     utf8::encode $text;
3095     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
3096    
3097     $self->send_packet ($self->{query_queue}[0][0])
3098     if @{ $self->{query_queue} } == 1;
3099 root 1.287
3100     1
3101 root 1.95 }
3102    
3103     cf::client->attach (
3104 root 1.290 on_connect => sub {
3105     my ($ns) = @_;
3106    
3107     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
3108     },
3109 root 1.95 on_reply => sub {
3110     my ($ns, $msg) = @_;
3111    
3112     # this weird shuffling is so that direct followup queries
3113     # get handled first
3114 root 1.128 my $queue = delete $ns->{query_queue}
3115 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
3116 root 1.95
3117     (shift @$queue)->[1]->($msg);
3118 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
3119 root 1.95
3120     push @{ $ns->{query_queue} }, @$queue;
3121    
3122     if (@{ $ns->{query_queue} } == @$queue) {
3123     if (@$queue) {
3124     $ns->send_packet ($ns->{query_queue}[0][0]);
3125     } else {
3126 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
3127 root 1.95 }
3128     }
3129     },
3130 root 1.287 on_exticmd => sub {
3131     my ($ns, $buf) = @_;
3132    
3133 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
3134 root 1.287
3135     if (ref $msg) {
3136 root 1.316 my ($type, $reply, @payload) =
3137     "ARRAY" eq ref $msg
3138     ? @$msg
3139     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
3140    
3141 root 1.338 my @reply;
3142    
3143 root 1.316 if (my $cb = $EXTICMD{$type}) {
3144 root 1.338 @reply = $cb->($ns, @payload);
3145     }
3146    
3147     $ns->ext_reply ($reply, @reply)
3148     if $reply;
3149 root 1.316
3150 root 1.287 } else {
3151     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
3152     }
3153    
3154     cf::override;
3155     },
3156 root 1.95 );
3157    
3158 root 1.140 =item $client->async (\&cb)
3159 root 1.96
3160     Create a new coroutine, running the specified callback. The coroutine will
3161     be automatically cancelled when the client gets destroyed (e.g. on logout,
3162     or loss of connection).
3163    
3164     =cut
3165    
3166 root 1.140 sub cf::client::async {
3167 root 1.96 my ($self, $cb) = @_;
3168    
3169 root 1.140 my $coro = &Coro::async ($cb);
3170 root 1.103
3171     $coro->on_destroy (sub {
3172 root 1.96 delete $self->{_coro}{$coro+0};
3173 root 1.103 });
3174 root 1.96
3175     $self->{_coro}{$coro+0} = $coro;
3176 root 1.103
3177     $coro
3178 root 1.96 }
3179    
3180     cf::client->attach (
3181 root 1.509 on_client_destroy => sub {
3182 root 1.96 my ($ns) = @_;
3183    
3184 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
3185 root 1.96 },
3186     );
3187    
3188 root 1.95 =back
3189    
3190 root 1.70
3191     =head2 SAFE SCRIPTING
3192    
3193     Functions that provide a safe environment to compile and execute
3194     snippets of perl code without them endangering the safety of the server
3195     itself. Looping constructs, I/O operators and other built-in functionality
3196     is not available in the safe scripting environment, and the number of
3197 root 1.79 functions and methods that can be called is greatly reduced.
3198 root 1.70
3199     =cut
3200 root 1.23
3201 root 1.42 our $safe = new Safe "safe";
3202 root 1.23 our $safe_hole = new Safe::Hole;
3203    
3204     $SIG{FPE} = 'IGNORE';
3205    
3206 root 1.328 $safe->permit_only (Opcode::opset qw(
3207 elmex 1.498 :base_core :base_mem :base_orig :base_math :base_loop
3208 root 1.328 grepstart grepwhile mapstart mapwhile
3209     sort time
3210     ));
3211 root 1.23
3212 root 1.25 # here we export the classes and methods available to script code
3213    
3214 root 1.70 =pod
3215    
3216 root 1.228 The following functions and methods are available within a safe environment:
3217 root 1.70
3218 root 1.297 cf::object
3219 root 1.383 contr pay_amount pay_player map x y force_find force_add destroy
3220 root 1.425 insert remove name archname title slaying race decrease split
3221 root 1.466 value
3222 root 1.297
3223     cf::object::player
3224     player
3225    
3226     cf::player
3227     peaceful
3228    
3229     cf::map
3230     trigger
3231 root 1.70
3232     =cut
3233    
3234 root 1.25 for (
3235 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
3236 elmex 1.431 insert remove inv nrof name archname title slaying race
3237 root 1.466 decrease split destroy change_exp value msg lore send_msg)],
3238 root 1.25 ["cf::object::player" => qw(player)],
3239 root 1.466 ["cf::player" => qw(peaceful send_msg)],
3240 elmex 1.91 ["cf::map" => qw(trigger)],
3241 root 1.25 ) {
3242     no strict 'refs';
3243     my ($pkg, @funs) = @$_;
3244 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3245 root 1.25 for @funs;
3246     }
3247 root 1.23
3248 root 1.70 =over 4
3249    
3250     =item @retval = safe_eval $code, [var => value, ...]
3251    
3252     Compiled and executes the given perl code snippet. additional var/value
3253     pairs result in temporary local (my) scalar variables of the given name
3254     that are available in the code snippet. Example:
3255    
3256     my $five = safe_eval '$first + $second', first => 1, second => 4;
3257    
3258     =cut
3259    
3260 root 1.23 sub safe_eval($;@) {
3261     my ($code, %vars) = @_;
3262    
3263     my $qcode = $code;
3264     $qcode =~ s/"/‟/g; # not allowed in #line filenames
3265     $qcode =~ s/\n/\\n/g;
3266    
3267 root 1.466 %vars = (_dummy => 0) unless %vars;
3268    
3269 root 1.499 my @res;
3270 root 1.23 local $_;
3271    
3272 root 1.42 my $eval =
3273 root 1.23 "do {\n"
3274     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3275     . "#line 0 \"{$qcode}\"\n"
3276     . $code
3277     . "\n}"
3278 root 1.25 ;
3279    
3280 root 1.499 if ($CFG{safe_eval}) {
3281     sub_generation_inc;
3282     local @safe::cf::_safe_eval_args = values %vars;
3283     @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3284     sub_generation_inc;
3285     } else {
3286     local @cf::_safe_eval_args = values %vars;
3287     @res = wantarray ? eval eval : scalar eval $eval;
3288     }
3289 root 1.25
3290 root 1.42 if ($@) {
3291     warn "$@";
3292     warn "while executing safe code '$code'\n";
3293     warn "with arguments " . (join " ", %vars) . "\n";
3294     }
3295    
3296 root 1.25 wantarray ? @res : $res[0]
3297 root 1.23 }
3298    
3299 root 1.69 =item cf::register_script_function $function => $cb
3300    
3301     Register a function that can be called from within map/npc scripts. The
3302     function should be reasonably secure and should be put into a package name
3303     like the extension.
3304    
3305     Example: register a function that gets called whenever a map script calls
3306     C<rent::overview>, as used by the C<rent> extension.
3307    
3308     cf::register_script_function "rent::overview" => sub {
3309     ...
3310     };
3311    
3312     =cut
3313    
3314 root 1.23 sub register_script_function {
3315     my ($fun, $cb) = @_;
3316    
3317 root 1.501 $fun = "safe::$fun" if $CFG{safe_eval};
3318     *$fun = $safe_hole->wrap ($cb);
3319 root 1.23 }
3320    
3321 root 1.70 =back
3322    
3323 root 1.71 =cut
3324    
3325 root 1.23 #############################################################################
3326 root 1.203 # the server's init and main functions
3327    
3328 root 1.246 sub load_facedata($) {
3329     my ($path) = @_;
3330 root 1.223
3331 root 1.348 # HACK to clear player env face cache, we need some signal framework
3332     # for this (global event?)
3333     %ext::player_env::MUSIC_FACE_CACHE = ();
3334    
3335 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3336 root 1.334
3337 root 1.229 warn "loading facedata from $path\n";
3338 root 1.223
3339 root 1.236 my $facedata;
3340     0 < aio_load $path, $facedata
3341 root 1.223 or die "$path: $!";
3342    
3343 root 1.237 $facedata = Coro::Storable::thaw $facedata;
3344 root 1.223
3345 root 1.236 $facedata->{version} == 2
3346 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3347    
3348 root 1.334 # patch in the exptable
3349 root 1.500 my $exp_table = $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]);
3350 root 1.334 $facedata->{resource}{"res/exp_table"} = {
3351     type => FT_RSRC,
3352 root 1.500 data => $exp_table,
3353     hash => (Digest::MD5::md5 $exp_table),
3354 root 1.334 };
3355     cf::cede_to_tick;
3356    
3357 root 1.236 {
3358     my $faces = $facedata->{faceinfo};
3359    
3360     while (my ($face, $info) = each %$faces) {
3361     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3362 root 1.405
3363 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3364     cf::face::set_magicmap $idx, $info->{magicmap};
3365 root 1.496 cf::face::set_data $idx, 0, $info->{data32}, $info->{hash32};
3366     cf::face::set_data $idx, 1, $info->{data64}, $info->{hash64};
3367 root 1.302
3368     cf::cede_to_tick;
3369 root 1.236 }
3370    
3371     while (my ($face, $info) = each %$faces) {
3372     next unless $info->{smooth};
3373 root 1.405
3374 root 1.236 my $idx = cf::face::find $face
3375     or next;
3376 root 1.405
3377 root 1.236 if (my $smooth = cf::face::find $info->{smooth}) {
3378 root 1.302 cf::face::set_smooth $idx, $smooth;
3379     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3380 root 1.236 } else {
3381     warn "smooth face '$info->{smooth}' not found for face '$face'";
3382     }
3383 root 1.302
3384     cf::cede_to_tick;
3385 root 1.236 }
3386 root 1.223 }
3387    
3388 root 1.236 {
3389     my $anims = $facedata->{animinfo};
3390    
3391     while (my ($anim, $info) = each %$anims) {
3392     cf::anim::set $anim, $info->{frames}, $info->{facings};
3393 root 1.302 cf::cede_to_tick;
3394 root 1.225 }
3395 root 1.236
3396     cf::anim::invalidate_all; # d'oh
3397 root 1.225 }
3398    
3399 root 1.302 {
3400     my $res = $facedata->{resource};
3401    
3402     while (my ($name, $info) = each %$res) {
3403 root 1.405 if (defined $info->{type}) {
3404     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3405    
3406 root 1.496 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3407 root 1.405 cf::face::set_type $idx, $info->{type};
3408 root 1.337 } else {
3409 root 1.405 $RESOURCE{$name} = $info;
3410 root 1.307 }
3411 root 1.302
3412     cf::cede_to_tick;
3413     }
3414 root 1.406 }
3415    
3416     cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3417 root 1.321
3418 root 1.406 1
3419     }
3420    
3421     cf::global->attach (on_resource_update => sub {
3422     if (my $soundconf = $RESOURCE{"res/sound.conf"}) {
3423     $soundconf = JSON::XS->new->utf8->relaxed->decode ($soundconf->{data});
3424    
3425     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3426     my $sound = $soundconf->{compat}[$_]
3427     or next;
3428 root 1.321
3429 root 1.406 my $face = cf::face::find "sound/$sound->[1]";
3430     cf::sound::set $sound->[0] => $face;
3431     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3432     }
3433 root 1.321
3434 root 1.406 while (my ($k, $v) = each %{$soundconf->{event}}) {
3435     my $face = cf::face::find "sound/$v";
3436     cf::sound::set $k => $face;
3437 root 1.321 }
3438 root 1.302 }
3439 root 1.406 });
3440 root 1.223
3441 root 1.318 register_exticmd fx_want => sub {
3442     my ($ns, $want) = @_;
3443    
3444     while (my ($k, $v) = each %$want) {
3445     $ns->fx_want ($k, $v);
3446     }
3447     };
3448    
3449 root 1.423 sub load_resource_file($) {
3450 root 1.424 my $guard = lock_acquire "load_resource_file";
3451    
3452 root 1.423 my $status = load_resource_file_ $_[0];
3453     get_slot 0.1, 100;
3454     cf::arch::commit_load;
3455 root 1.424
3456 root 1.423 $status
3457     }
3458    
3459 root 1.253 sub reload_regions {
3460 root 1.348 # HACK to clear player env face cache, we need some signal framework
3461     # for this (global event?)
3462     %ext::player_env::MUSIC_FACE_CACHE = ();
3463    
3464 root 1.253 load_resource_file "$MAPDIR/regions"
3465     or die "unable to load regions file\n";
3466 root 1.304
3467     for (cf::region::list) {
3468     $_->{match} = qr/$_->{match}/
3469     if exists $_->{match};
3470     }
3471 root 1.253 }
3472    
3473 root 1.246 sub reload_facedata {
3474 root 1.253 load_facedata "$DATADIR/facedata"
3475 root 1.246 or die "unable to load facedata\n";
3476     }
3477    
3478     sub reload_archetypes {
3479 root 1.253 load_resource_file "$DATADIR/archetypes"
3480 root 1.246 or die "unable to load archetypes\n";
3481 root 1.241 }
3482    
3483 root 1.246 sub reload_treasures {
3484 root 1.253 load_resource_file "$DATADIR/treasures"
3485 root 1.246 or die "unable to load treasurelists\n";
3486 root 1.241 }
3487    
3488 root 1.223 sub reload_resources {
3489 root 1.245 warn "reloading resource files...\n";
3490    
3491 root 1.246 reload_facedata;
3492     reload_archetypes;
3493 root 1.423 reload_regions;
3494 root 1.246 reload_treasures;
3495 root 1.245
3496     warn "finished reloading resource files\n";
3497 root 1.223 }
3498    
3499 root 1.345 sub reload_config {
3500 root 1.485 warn "reloading config file...\n";
3501    
3502 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3503 root 1.72 or return;
3504    
3505     local $/;
3506 root 1.485 *CFG = YAML::XS::Load scalar <$fh>;
3507 root 1.131
3508     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
3509    
3510 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3511     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3512    
3513 root 1.131 if (exists $CFG{mlockall}) {
3514     eval {
3515 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3516 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3517     };
3518     warn $@ if $@;
3519     }
3520 root 1.485
3521     warn "finished reloading resource files\n";
3522 root 1.72 }
3523    
3524 root 1.445 sub pidfile() {
3525     sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3526     or die "$PIDFILE: $!";
3527     flock $fh, &Fcntl::LOCK_EX
3528     or die "$PIDFILE: flock: $!";
3529     $fh
3530     }
3531    
3532     # make sure only one server instance is running at any one time
3533     sub atomic {
3534     my $fh = pidfile;
3535    
3536     my $pid = <$fh>;
3537     kill 9, $pid if $pid > 0;
3538    
3539     seek $fh, 0, 0;
3540     print $fh $$;
3541     }
3542    
3543 root 1.474 sub main_loop {
3544     warn "EV::loop starting\n";
3545     if (1) {
3546     EV::loop;
3547     }
3548     warn "EV::loop returned\n";
3549     goto &main_loop unless $REALLY_UNLOOP;
3550     }
3551    
3552 root 1.39 sub main {
3553 root 1.453 cf::init_globals; # initialise logging
3554    
3555     LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3556     LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3557     LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3558     LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen.";
3559    
3560     $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3561 root 1.445
3562 root 1.108 # we must not ever block the main coroutine
3563     local $Coro::idle = sub {
3564 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3565 root 1.175 (async {
3566 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3567 root 1.396 EV::loop EV::LOOP_ONESHOT;
3568 root 1.175 })->prio (Coro::PRIO_MAX);
3569 root 1.108 };
3570    
3571 root 1.453 evthread_start IO::AIO::poll_fileno;
3572    
3573     cf::sync_job {
3574 root 1.515 cf::init_experience;
3575     cf::init_anim;
3576     cf::init_attackmess;
3577     cf::init_dynamic;
3578    
3579 root 1.495 cf::load_settings;
3580     cf::load_materials;
3581    
3582 root 1.453 reload_resources;
3583 root 1.423 reload_config;
3584     db_init;
3585 root 1.453
3586     cf::init_uuid;
3587     cf::init_signals;
3588     cf::init_skills;
3589    
3590     cf::init_beforeplay;
3591    
3592     atomic;
3593    
3594 root 1.423 load_extensions;
3595    
3596 root 1.453 utime time, time, $RUNTIMEFILE;
3597 root 1.183
3598 root 1.453 # no (long-running) fork's whatsoever before this point(!)
3599 root 1.475 use POSIX ();
3600 root 1.453 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3601 root 1.445
3602 root 1.453 (pop @POST_INIT)->(0) while @POST_INIT;
3603     };
3604 root 1.445
3605 root 1.516 cf::object::thawer::errors_are_fatal 0;
3606 root 1.517 warn "parse errors in files are no longer fatal from this point on.\n";
3607 root 1.516
3608 root 1.474 main_loop;
3609 root 1.34 }
3610    
3611     #############################################################################
3612 root 1.155 # initialisation and cleanup
3613    
3614     # install some emergency cleanup handlers
3615     BEGIN {
3616 root 1.396 our %SIGWATCHER = ();
3617 root 1.155 for my $signal (qw(INT HUP TERM)) {
3618 root 1.512 $SIGWATCHER{$signal} = AE::signal $signal, sub {
3619 root 1.396 cf::cleanup "SIG$signal";
3620     };
3621 root 1.155 }
3622     }
3623    
3624 root 1.417 sub write_runtime_sync {
3625 root 1.512 my $t0 = AE::time;
3626 root 1.506
3627 root 1.281 # first touch the runtime file to show we are still running:
3628     # the fsync below can take a very very long time.
3629    
3630 root 1.445 IO::AIO::aio_utime $RUNTIMEFILE, undef, undef;
3631 root 1.281
3632     my $guard = cf::lock_acquire "write_runtime";
3633    
3634 root 1.505 my $fh = aio_open "$RUNTIMEFILE~", O_WRONLY | O_CREAT | O_TRUNC, 0644
3635 root 1.281 or return;
3636    
3637     my $value = $cf::RUNTIME + 90 + 10;
3638     # 10 is the runtime save interval, for a monotonic clock
3639     # 60 allows for the watchdog to kill the server.
3640    
3641     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3642     and return;
3643    
3644     # always fsync - this file is important
3645     aio_fsync $fh
3646     and return;
3647    
3648     # touch it again to show we are up-to-date
3649     aio_utime $fh, undef, undef;
3650    
3651     close $fh
3652     or return;
3653    
3654 root 1.445 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3655 root 1.281 and return;
3656    
3657 root 1.512 warn sprintf "runtime file written (%gs).\n", AE::time - $t0;
3658 root 1.281
3659     1
3660     }
3661    
3662 root 1.416 our $uuid_lock;
3663     our $uuid_skip;
3664    
3665     sub write_uuid_sync($) {
3666     $uuid_skip ||= $_[0];
3667    
3668     return if $uuid_lock;
3669     local $uuid_lock = 1;
3670    
3671     my $uuid = "$LOCALDIR/uuid";
3672    
3673     my $fh = aio_open "$uuid~", O_WRONLY | O_CREAT, 0644
3674     or return;
3675    
3676 root 1.454 my $value = uuid_seq uuid_cur;
3677    
3678 root 1.455 unless ($value) {
3679     warn "cowardly refusing to write zero uuid value!\n";
3680 root 1.454 return;
3681     }
3682    
3683     my $value = uuid_str $value + $uuid_skip;
3684 root 1.416 $uuid_skip = 0;
3685    
3686     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3687     and return;
3688    
3689     # always fsync - this file is important
3690     aio_fsync $fh
3691     and return;
3692    
3693     close $fh
3694     or return;
3695    
3696     aio_rename "$uuid~", $uuid
3697     and return;
3698    
3699     warn "uuid file written ($value).\n";
3700    
3701     1
3702    
3703     }
3704    
3705     sub write_uuid($$) {
3706     my ($skip, $sync) = @_;
3707    
3708     $sync ? write_uuid_sync $skip
3709     : async { write_uuid_sync $skip };
3710     }
3711    
3712 root 1.156 sub emergency_save() {
3713 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3714    
3715 root 1.457 warn "emergency_perl_save: enter\n";
3716 root 1.155
3717     cf::sync_job {
3718 root 1.457 # this is a trade-off: we want to be very quick here, so
3719     # save all maps without fsync, and later call a global sync
3720     # (which in turn might be very very slow)
3721     local $USE_FSYNC = 0;
3722    
3723 root 1.155 # use a peculiar iteration method to avoid tripping on perl
3724     # refcount bugs in for. also avoids problems with players
3725 root 1.167 # and maps saved/destroyed asynchronously.
3726 root 1.457 warn "emergency_perl_save: begin player save\n";
3727 root 1.155 for my $login (keys %cf::PLAYER) {
3728     my $pl = $cf::PLAYER{$login} or next;
3729     $pl->valid or next;
3730 root 1.382 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3731 root 1.155 $pl->save;
3732     }
3733 root 1.457 warn "emergency_perl_save: end player save\n";
3734 root 1.155
3735 root 1.457 warn "emergency_perl_save: begin map save\n";
3736 root 1.155 for my $path (keys %cf::MAP) {
3737     my $map = $cf::MAP{$path} or next;
3738     $map->valid or next;
3739     $map->save;
3740     }
3741 root 1.457 warn "emergency_perl_save: end map save\n";
3742 root 1.208
3743 root 1.457 warn "emergency_perl_save: begin database checkpoint\n";
3744 root 1.208 BDB::db_env_txn_checkpoint $DB_ENV;
3745 root 1.457 warn "emergency_perl_save: end database checkpoint\n";
3746 root 1.416
3747 root 1.457 warn "emergency_perl_save: begin write uuid\n";
3748 root 1.416 write_uuid_sync 1;
3749 root 1.457 warn "emergency_perl_save: end write uuid\n";
3750 root 1.155 };
3751    
3752 root 1.457 warn "emergency_perl_save: starting sync()\n";
3753     IO::AIO::aio_sync sub {
3754     warn "emergency_perl_save: finished sync()\n";
3755     };
3756    
3757     warn "emergency_perl_save: leave\n";
3758 root 1.155 }
3759 root 1.22
3760 root 1.211 sub post_cleanup {
3761     my ($make_core) = @_;
3762    
3763     warn Carp::longmess "post_cleanup backtrace"
3764     if $make_core;
3765 root 1.445
3766     my $fh = pidfile;
3767     unlink $PIDFILE if <$fh> == $$;
3768 root 1.211 }
3769    
3770 root 1.441 # a safer delete_package, copied from Symbol
3771     sub clear_package($) {
3772     my $pkg = shift;
3773    
3774     # expand to full symbol table name if needed
3775     unless ($pkg =~ /^main::.*::$/) {
3776     $pkg = "main$pkg" if $pkg =~ /^::/;
3777     $pkg = "main::$pkg" unless $pkg =~ /^main::/;
3778     $pkg .= '::' unless $pkg =~ /::$/;
3779     }
3780    
3781     my($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
3782     my $stem_symtab = *{$stem}{HASH};
3783    
3784     defined $stem_symtab and exists $stem_symtab->{$leaf}
3785     or return;
3786    
3787     # clear all symbols
3788     my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3789     for my $name (keys %$leaf_symtab) {
3790     _gv_clear *{"$pkg$name"};
3791     # use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3792     }
3793     }
3794    
3795 root 1.246 sub do_reload_perl() {
3796 root 1.106 # can/must only be called in main
3797     if ($Coro::current != $Coro::main) {
3798 root 1.183 warn "can only reload from main coroutine";
3799 root 1.106 return;
3800     }
3801    
3802 root 1.441 return if $RELOAD++;
3803    
3804 root 1.512 my $t1 = AE::time;
3805 root 1.457
3806 root 1.441 while ($RELOAD) {
3807     warn "reloading...";
3808 root 1.103
3809 root 1.441 warn "entering sync_job";
3810 root 1.212
3811 root 1.441 cf::sync_job {
3812     cf::write_runtime_sync; # external watchdog should not bark
3813     cf::emergency_save;
3814     cf::write_runtime_sync; # external watchdog should not bark
3815 root 1.183
3816 root 1.441 warn "syncing database to disk";
3817     BDB::db_env_txn_checkpoint $DB_ENV;
3818 root 1.106
3819 root 1.441 # if anything goes wrong in here, we should simply crash as we already saved
3820 root 1.65
3821 root 1.441 warn "flushing outstanding aio requests";
3822     while (IO::AIO::nreqs || BDB::nreqs) {
3823     Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3824     }
3825 root 1.183
3826 root 1.441 warn "cancelling all extension coros";
3827     $_->cancel for values %EXT_CORO;
3828     %EXT_CORO = ();
3829 root 1.223
3830 root 1.441 warn "removing commands";
3831     %COMMAND = ();
3832 root 1.103
3833 root 1.441 warn "removing ext/exti commands";
3834     %EXTCMD = ();
3835     %EXTICMD = ();
3836 root 1.159
3837 root 1.441 warn "unloading/nuking all extensions";
3838     for my $pkg (@EXTS) {
3839     warn "... unloading $pkg";
3840 root 1.159
3841 root 1.441 if (my $cb = $pkg->can ("unload")) {
3842     eval {
3843     $cb->($pkg);
3844     1
3845     } or warn "$pkg unloaded, but with errors: $@";
3846     }
3847 root 1.159
3848 root 1.441 warn "... clearing $pkg";
3849     clear_package $pkg;
3850 root 1.159 }
3851    
3852 root 1.441 warn "unloading all perl modules loaded from $LIBDIR";
3853     while (my ($k, $v) = each %INC) {
3854     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3855 root 1.65
3856 root 1.441 warn "... unloading $k";
3857     delete $INC{$k};
3858 root 1.65
3859 root 1.441 $k =~ s/\.pm$//;
3860     $k =~ s/\//::/g;
3861 root 1.65
3862 root 1.441 if (my $cb = $k->can ("unload_module")) {
3863     $cb->();
3864     }
3865 root 1.65
3866 root 1.441 clear_package $k;
3867 root 1.65 }
3868    
3869 root 1.441 warn "getting rid of safe::, as good as possible";
3870     clear_package "safe::$_"
3871     for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3872 root 1.65
3873 root 1.441 warn "unloading cf.pm \"a bit\"";
3874     delete $INC{"cf.pm"};
3875 root 1.466 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES;
3876 root 1.65
3877 root 1.441 # don't, removes xs symbols, too,
3878     # and global variables created in xs
3879     #clear_package __PACKAGE__;
3880 root 1.65
3881 root 1.441 warn "unload completed, starting to reload now";
3882 root 1.65
3883 root 1.441 warn "reloading cf.pm";
3884     require cf;
3885 root 1.483 cf::_connect_to_perl_1;
3886 root 1.183
3887 root 1.441 warn "loading config and database again";
3888     cf::reload_config;
3889 root 1.100
3890 root 1.441 warn "loading extensions";
3891     cf::load_extensions;
3892 root 1.65
3893 root 1.457 if ($REATTACH_ON_RELOAD) {
3894     warn "reattaching attachments to objects/players";
3895     _global_reattach; # objects, sockets
3896     warn "reattaching attachments to maps";
3897     reattach $_ for values %MAP;
3898     warn "reattaching attachments to players";
3899     reattach $_ for values %PLAYER;
3900     }
3901 root 1.65
3902 root 1.457 warn "running post_init jobs";
3903 root 1.453 (pop @POST_INIT)->(1) while @POST_INIT;
3904    
3905 root 1.441 warn "leaving sync_job";
3906 root 1.183
3907 root 1.441 1
3908     } or do {
3909     warn $@;
3910     cf::cleanup "error while reloading, exiting.";
3911     };
3912 root 1.183
3913 root 1.441 warn "reloaded";
3914     --$RELOAD;
3915     }
3916 root 1.457
3917 root 1.512 $t1 = AE::time - $t1;
3918 root 1.457 warn "reload completed in ${t1}s\n";
3919 root 1.65 };
3920    
3921 root 1.175 our $RELOAD_WATCHER; # used only during reload
3922    
3923 root 1.246 sub reload_perl() {
3924     # doing reload synchronously and two reloads happen back-to-back,
3925     # coro crashes during coro_state_free->destroy here.
3926    
3927 root 1.457 $RELOAD_WATCHER ||= cf::async {
3928     Coro::AIO::aio_wait cache_extensions;
3929    
3930 root 1.512 $RELOAD_WATCHER = AE::timer $TICK * 1.5, 0, sub {
3931 root 1.457 do_reload_perl;
3932     undef $RELOAD_WATCHER;
3933     };
3934 root 1.396 };
3935 root 1.246 }
3936    
3937 root 1.111 register_command "reload" => sub {
3938 root 1.65 my ($who, $arg) = @_;
3939    
3940     if ($who->flag (FLAG_WIZ)) {
3941 root 1.175 $who->message ("reloading server.");
3942 root 1.374 async {
3943     $Coro::current->{desc} = "perl_reload";
3944     reload_perl;
3945     };
3946 root 1.65 }
3947     };
3948    
3949 root 1.27 unshift @INC, $LIBDIR;
3950 root 1.17
3951 root 1.183 my $bug_warning = 0;
3952    
3953 root 1.239 our @WAIT_FOR_TICK;
3954     our @WAIT_FOR_TICK_BEGIN;
3955    
3956     sub wait_for_tick {
3957 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
3958 root 1.241
3959 root 1.239 my $signal = new Coro::Signal;
3960     push @WAIT_FOR_TICK, $signal;
3961     $signal->wait;
3962     }
3963    
3964     sub wait_for_tick_begin {
3965 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
3966 root 1.241
3967 root 1.239 my $signal = new Coro::Signal;
3968     push @WAIT_FOR_TICK_BEGIN, $signal;
3969     $signal->wait;
3970     }
3971    
3972 root 1.412 sub tick {
3973 root 1.396 if ($Coro::current != $Coro::main) {
3974     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3975     unless ++$bug_warning > 10;
3976     return;
3977     }
3978    
3979     cf::server_tick; # one server iteration
3980 root 1.245
3981 root 1.512 #for(1..3e6){} AE::now_update; $NOW=AE::now; # generate load #d#
3982 root 1.502
3983 root 1.396 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3984 root 1.402 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
3985 root 1.396 Coro::async_pool {
3986     $Coro::current->{desc} = "runtime saver";
3987 root 1.417 write_runtime_sync
3988 root 1.396 or warn "ERROR: unable to write runtime file: $!";
3989     };
3990     }
3991 root 1.265
3992 root 1.396 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
3993     $sig->send;
3994     }
3995     while (my $sig = shift @WAIT_FOR_TICK) {
3996     $sig->send;
3997     }
3998 root 1.265
3999 root 1.412 $LOAD = ($NOW - $TICK_START) / $TICK;
4000 root 1.396 $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
4001 root 1.265
4002 root 1.412 if (0) {
4003     if ($NEXT_TICK) {
4004     my $jitter = $TICK_START - $NEXT_TICK;
4005     $JITTER = $JITTER * 0.75 + $jitter * 0.25;
4006     warn "jitter $JITTER\n";#d#
4007     }
4008     }
4009     }
4010 root 1.35
4011 root 1.206 {
4012 root 1.401 # configure BDB
4013    
4014 root 1.503 BDB::min_parallel 16;
4015 root 1.400 BDB::max_poll_reqs $TICK * 0.1;
4016 root 1.433 $AnyEvent::BDB::WATCHER->priority (1);
4017 root 1.77
4018 root 1.206 unless ($DB_ENV) {
4019     $DB_ENV = BDB::db_env_create;
4020 root 1.437 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT);
4021     $DB_ENV->set_flags (&BDB::LOG_AUTOREMOVE ) if BDB::VERSION v0, v4.7;
4022     $DB_ENV->log_set_config (&BDB::LOG_AUTO_REMOVE) if BDB::VERSION v4.7;
4023 root 1.371 $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
4024     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
4025 root 1.206
4026     cf::sync_job {
4027 root 1.208 eval {
4028     BDB::db_env_open
4029     $DB_ENV,
4030 root 1.253 $BDBDIR,
4031 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
4032     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
4033     0666;
4034    
4035 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
4036 root 1.208 };
4037    
4038     cf::cleanup "db_env_open(db): $@" if $@;
4039 root 1.206 };
4040     }
4041 root 1.363
4042 root 1.396 $BDB_DEADLOCK_WATCHER = EV::periodic 0, 3, 0, sub {
4043     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
4044     };
4045     $BDB_CHECKPOINT_WATCHER = EV::periodic 0, 60, 0, sub {
4046     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
4047     };
4048     $BDB_TRICKLE_WATCHER = EV::periodic 0, 10, 0, sub {
4049     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
4050     };
4051 root 1.206 }
4052    
4053     {
4054 root 1.401 # configure IO::AIO
4055    
4056 root 1.206 IO::AIO::min_parallel 8;
4057     IO::AIO::max_poll_time $TICK * 0.1;
4058 root 1.435 undef $AnyEvent::AIO::WATCHER;
4059 root 1.206 }
4060 root 1.108
4061 root 1.262 my $_log_backtrace;
4062    
4063 root 1.260 sub _log_backtrace {
4064     my ($msg, @addr) = @_;
4065    
4066 root 1.262 $msg =~ s/\n//;
4067 root 1.260
4068 root 1.262 # limit the # of concurrent backtraces
4069     if ($_log_backtrace < 2) {
4070     ++$_log_backtrace;
4071 root 1.446 my $perl_bt = Carp::longmess $msg;
4072 root 1.262 async {
4073 root 1.374 $Coro::current->{desc} = "abt $msg";
4074    
4075 root 1.262 my @bt = fork_call {
4076     @addr = map { sprintf "%x", $_ } @addr;
4077     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
4078     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
4079     or die "addr2line: $!";
4080    
4081     my @funcs;
4082     my @res = <$fh>;
4083     chomp for @res;
4084     while (@res) {
4085     my ($func, $line) = splice @res, 0, 2, ();
4086     push @funcs, "[$func] $line";
4087     }
4088 root 1.260
4089 root 1.262 @funcs
4090     };
4091 root 1.260
4092 root 1.446 LOG llevInfo, "[ABT] $perl_bt\n";
4093     LOG llevInfo, "[ABT] --- C backtrace follows ---\n";
4094 root 1.262 LOG llevInfo, "[ABT] $_\n" for @bt;
4095     --$_log_backtrace;
4096     };
4097     } else {
4098 root 1.260 LOG llevInfo, "[ABT] $msg\n";
4099 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
4100     }
4101 root 1.260 }
4102    
4103 root 1.249 # load additional modules
4104 root 1.467 require "cf/$_.pm" for @EXTRA_MODULES;
4105 root 1.483 cf::_connect_to_perl_2;
4106 root 1.249
4107 root 1.125 END { cf::emergency_save }
4108    
4109 root 1.1 1
4110