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