ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.525
Committed: Wed Apr 21 03:34:26 2010 UTC (14 years, 1 month ago) by root
Branch: MAIN
Changes since 1.524: +2 -1 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.524 $EXT_MAP{"cf::map::wrap"} = [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.524 bless $self, "cf::map::wrap";
2349 root 1.349 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 root 1.525 $self->activate_recursive
2663     unless exists $self->{_link_pos}; # maybe we re in-flight again
2664 root 1.110 }
2665    
2666 root 1.306 =item $player_object->goto ($path, $x, $y[, $check->($map)[, $done->()]])
2667 root 1.268
2668     Moves the player to the given map-path and coordinates by first freezing
2669     her, loading and preparing them map, calling the provided $check callback
2670     that has to return the map if sucecssful, and then unfreezes the player on
2671 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2672     be called at the end of this process.
2673 root 1.110
2674 root 1.436 Note that $check will be called with a potentially non-loaded map, so if
2675     it needs a loaded map it has to call C<< ->load >>.
2676    
2677 root 1.110 =cut
2678    
2679 root 1.270 our $GOTOGEN;
2680    
2681 root 1.136 sub cf::object::player::goto {
2682 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2683 root 1.268
2684 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2685     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2686    
2687 root 1.110 $self->enter_link;
2688    
2689 root 1.140 (async {
2690 root 1.374 $Coro::current->{desc} = "player::goto $path $x $y";
2691    
2692 root 1.365 # *tag paths override both path and x|y
2693     if ($path =~ /^\*(.*)$/) {
2694     if (my @obs = grep $_->map, ext::map_tags::find $1) {
2695     my $ob = $obs[rand @obs];
2696 root 1.366
2697 root 1.367 # see if we actually can go there
2698 root 1.368 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2699     $ob = $obs[rand @obs];
2700 root 1.369 } else {
2701     $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2702 root 1.368 }
2703 root 1.369 # else put us there anyways for now #d#
2704 root 1.366
2705 root 1.365 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2706 root 1.369 } else {
2707     ($path, $x, $y) = (undef, undef, undef);
2708 root 1.365 }
2709     }
2710    
2711 root 1.197 my $map = eval {
2712 root 1.369 my $map = defined $path ? cf::map::find $path : undef;
2713 root 1.268
2714     if ($map) {
2715     $map = $map->customise_for ($self);
2716     $map = $check->($map) if $check && $map;
2717     } else {
2718 root 1.369 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2719 root 1.268 }
2720    
2721 root 1.197 $map
2722 root 1.268 };
2723    
2724     if ($@) {
2725     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2726     LOG llevError | logBacktrace, Carp::longmess $@;
2727     }
2728 root 1.115
2729 root 1.270 if ($gen == $self->{_goto_generation}) {
2730     delete $self->{_goto_generation};
2731     $self->leave_link ($map, $x, $y);
2732     }
2733 root 1.306
2734     $done->() if $done;
2735 root 1.110 })->prio (1);
2736     }
2737    
2738     =item $player_object->enter_exit ($exit_object)
2739    
2740     =cut
2741    
2742     sub parse_random_map_params {
2743     my ($spec) = @_;
2744    
2745     my $rmp = { # defaults
2746 root 1.181 xsize => (cf::rndm 15, 40),
2747     ysize => (cf::rndm 15, 40),
2748     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2749 root 1.182 #layout => string,
2750 root 1.110 };
2751    
2752     for (split /\n/, $spec) {
2753     my ($k, $v) = split /\s+/, $_, 2;
2754    
2755     $rmp->{lc $k} = $v if (length $k) && (length $v);
2756     }
2757    
2758     $rmp
2759     }
2760    
2761     sub prepare_random_map {
2762     my ($exit) = @_;
2763    
2764     # all this does is basically replace the /! path by
2765     # a new random map path (?random/...) with a seed
2766     # that depends on the exit object
2767    
2768     my $rmp = parse_random_map_params $exit->msg;
2769    
2770     if ($exit->map) {
2771 root 1.198 $rmp->{region} = $exit->region->name;
2772 root 1.110 $rmp->{origin_map} = $exit->map->path;
2773     $rmp->{origin_x} = $exit->x;
2774     $rmp->{origin_y} = $exit->y;
2775 root 1.430
2776     $exit->map->touch;
2777 root 1.110 }
2778    
2779     $rmp->{random_seed} ||= $exit->random_seed;
2780    
2781 root 1.422 my $data = JSON::XS->new->utf8->pretty->canonical->encode ($rmp);
2782 root 1.110 my $md5 = Digest::MD5::md5_hex $data;
2783 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2784 root 1.110
2785 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2786 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2787 root 1.177 undef $fh;
2788     aio_rename "$meta~", $meta;
2789 root 1.110
2790 root 1.430 my $slaying = "?random/$md5";
2791    
2792     if ($exit->valid) {
2793     $exit->slaying ("?random/$md5");
2794     $exit->msg (undef);
2795     }
2796 root 1.110 }
2797     }
2798    
2799     sub cf::object::player::enter_exit {
2800     my ($self, $exit) = @_;
2801    
2802     return unless $self->type == cf::PLAYER;
2803    
2804 root 1.430 $self->enter_link;
2805    
2806     (async {
2807     $Coro::current->{desc} = "enter_exit";
2808    
2809     unless (eval {
2810     $self->deactivate_recursive; # just to be sure
2811 root 1.195
2812 root 1.430 # random map handling
2813     {
2814     my $guard = cf::lock_acquire "exit_prepare:$exit";
2815 root 1.195
2816 root 1.430 prepare_random_map $exit
2817     if $exit->slaying eq "/!";
2818     }
2819 root 1.110
2820 root 1.430 my $map = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2821     my $x = $exit->stats->hp;
2822     my $y = $exit->stats->sp;
2823 root 1.296
2824 root 1.430 $self->goto ($map, $x, $y);
2825 root 1.374
2826 root 1.430 # if exit is damned, update players death & WoR home-position
2827     $self->contr->savebed ($map, $x, $y)
2828     if $exit->flag (cf::FLAG_DAMNED);
2829 root 1.110
2830 root 1.430 1
2831 root 1.110 }) {
2832 root 1.447 $self->message ("Something went wrong deep within the deliantra server. "
2833 root 1.233 . "I'll try to bring you back to the map you were before. "
2834     . "Please report this to the dungeon master!",
2835     cf::NDI_UNIQUE | cf::NDI_RED);
2836 root 1.110
2837     warn "ERROR in enter_exit: $@";
2838     $self->leave_link;
2839     }
2840     })->prio (1);
2841     }
2842    
2843 root 1.95 =head3 cf::client
2844    
2845     =over 4
2846    
2847     =item $client->send_drawinfo ($text, $flags)
2848    
2849     Sends a drawinfo packet to the client. Circumvents output buffering so
2850     should not be used under normal circumstances.
2851    
2852 root 1.70 =cut
2853    
2854 root 1.95 sub cf::client::send_drawinfo {
2855     my ($self, $text, $flags) = @_;
2856    
2857     utf8::encode $text;
2858 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2859 root 1.95 }
2860    
2861 root 1.494 =item $client->send_big_packet ($pkt)
2862    
2863     Like C<send_packet>, but tries to compress large packets, and fragments
2864     them as required.
2865    
2866     =cut
2867    
2868     our $MAXFRAGSIZE = cf::MAXSOCKBUF - 64;
2869    
2870     sub cf::client::send_big_packet {
2871     my ($self, $pkt) = @_;
2872    
2873     # try lzf for large packets
2874     $pkt = "lzf " . Compress::LZF::compress $pkt
2875     if 1024 <= length $pkt and $self->{can_lzf};
2876    
2877     # split very large packets
2878     if ($MAXFRAGSIZE < length $pkt and $self->{can_lzf}) {
2879     $self->send_packet ("frag $_") for unpack "(a$MAXFRAGSIZE)*", $pkt;
2880     $pkt = "frag";
2881     }
2882    
2883     $self->send_packet ($pkt);
2884     }
2885    
2886 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2887 root 1.283
2888     Send a drawinfo or msg packet to the client, formatting the msg for the
2889     client if neccessary. C<$type> should be a string identifying the type of
2890     the message, with C<log> being the default. If C<$color> is negative, suppress
2891     the message unless the client supports the msg packet.
2892    
2893     =cut
2894    
2895 root 1.391 # non-persistent channels (usually the info channel)
2896 root 1.350 our %CHANNEL = (
2897 root 1.486 "c/motd" => {
2898     id => "infobox",
2899     title => "MOTD",
2900     reply => undef,
2901     tooltip => "The message of the day",
2902     },
2903 root 1.350 "c/identify" => {
2904 root 1.375 id => "infobox",
2905 root 1.350 title => "Identify",
2906     reply => undef,
2907     tooltip => "Items recently identified",
2908     },
2909 root 1.352 "c/examine" => {
2910 root 1.375 id => "infobox",
2911 root 1.352 title => "Examine",
2912     reply => undef,
2913     tooltip => "Signs and other items you examined",
2914     },
2915 root 1.487 "c/shopinfo" => {
2916     id => "infobox",
2917     title => "Shop Info",
2918     reply => undef,
2919     tooltip => "What your bargaining skill tells you about the shop",
2920     },
2921 root 1.389 "c/book" => {
2922     id => "infobox",
2923     title => "Book",
2924     reply => undef,
2925     tooltip => "The contents of a note or book",
2926     },
2927 root 1.375 "c/lookat" => {
2928     id => "infobox",
2929     title => "Look",
2930     reply => undef,
2931     tooltip => "What you saw there",
2932     },
2933 root 1.390 "c/who" => {
2934     id => "infobox",
2935     title => "Players",
2936     reply => undef,
2937     tooltip => "Shows players who are currently online",
2938     },
2939     "c/body" => {
2940     id => "infobox",
2941     title => "Body Parts",
2942     reply => undef,
2943     tooltip => "Shows which body parts you posess and are available",
2944     },
2945 root 1.465 "c/statistics" => {
2946     id => "infobox",
2947     title => "Statistics",
2948     reply => undef,
2949     tooltip => "Shows your primary statistics",
2950     },
2951 root 1.450 "c/skills" => {
2952     id => "infobox",
2953     title => "Skills",
2954     reply => undef,
2955     tooltip => "Shows your experience per skill and item power",
2956     },
2957 root 1.470 "c/shopitems" => {
2958     id => "infobox",
2959     title => "Shop Items",
2960     reply => undef,
2961     tooltip => "Shows the items currently for sale in this shop",
2962     },
2963 root 1.465 "c/resistances" => {
2964     id => "infobox",
2965     title => "Resistances",
2966     reply => undef,
2967     tooltip => "Shows your resistances",
2968     },
2969     "c/pets" => {
2970     id => "infobox",
2971     title => "Pets",
2972     reply => undef,
2973     tooltip => "Shows information abotu your pets/a specific pet",
2974     },
2975 root 1.471 "c/perceiveself" => {
2976     id => "infobox",
2977     title => "Perceive Self",
2978     reply => undef,
2979     tooltip => "You gained detailed knowledge about yourself",
2980     },
2981 root 1.390 "c/uptime" => {
2982     id => "infobox",
2983     title => "Uptime",
2984     reply => undef,
2985 root 1.391 tooltip => "How long the server has been running since last restart",
2986 root 1.390 },
2987     "c/mapinfo" => {
2988     id => "infobox",
2989     title => "Map Info",
2990     reply => undef,
2991     tooltip => "Information related to the maps",
2992     },
2993 root 1.426 "c/party" => {
2994     id => "party",
2995     title => "Party",
2996     reply => "gsay ",
2997     tooltip => "Messages and chat related to your party",
2998     },
2999 root 1.464 "c/death" => {
3000     id => "death",
3001     title => "Death",
3002     reply => undef,
3003     tooltip => "Reason for and more info about your most recent death",
3004     },
3005 root 1.462 "c/say" => $SAY_CHANNEL,
3006     "c/chat" => $CHAT_CHANNEL,
3007 root 1.350 );
3008    
3009 root 1.283 sub cf::client::send_msg {
3010 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
3011 root 1.283
3012 root 1.447 $msg = $self->pl->expand_cfpod ($msg)
3013     unless $color & cf::NDI_VERBATIM;
3014 root 1.283
3015 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
3016 root 1.311
3017 root 1.350 # check predefined channels, for the benefit of C
3018 root 1.375 if ($CHANNEL{$channel}) {
3019     $channel = $CHANNEL{$channel};
3020    
3021 root 1.463 $self->ext_msg (channel_info => $channel);
3022 root 1.375 $channel = $channel->{id};
3023 root 1.350
3024 root 1.375 } elsif (ref $channel) {
3025 root 1.311 # send meta info to client, if not yet sent
3026     unless (exists $self->{channel}{$channel->{id}}) {
3027     $self->{channel}{$channel->{id}} = $channel;
3028 root 1.463 $self->ext_msg (channel_info => $channel);
3029 root 1.311 }
3030    
3031     $channel = $channel->{id};
3032     }
3033    
3034 root 1.313 return unless @extra || length $msg;
3035    
3036 root 1.463 # default colour, mask it out
3037     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
3038     if $color & cf::NDI_DEF;
3039    
3040     my $pkt = "msg "
3041     . $self->{json_coder}->encode (
3042     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]
3043     );
3044    
3045 root 1.494 $self->send_big_packet ($pkt);
3046 root 1.283 }
3047    
3048 root 1.316 =item $client->ext_msg ($type, @msg)
3049 root 1.232
3050 root 1.287 Sends an ext event to the client.
3051 root 1.232
3052     =cut
3053    
3054 root 1.316 sub cf::client::ext_msg($$@) {
3055     my ($self, $type, @msg) = @_;
3056 root 1.232
3057 root 1.343 if ($self->extcmd == 2) {
3058 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
3059 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
3060 root 1.316 push @msg, msgtype => "event_$type";
3061 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3062 root 1.316 }
3063 root 1.232 }
3064 root 1.95
3065 root 1.336 =item $client->ext_reply ($msgid, @msg)
3066    
3067     Sends an ext reply to the client.
3068    
3069     =cut
3070    
3071     sub cf::client::ext_reply($$@) {
3072     my ($self, $id, @msg) = @_;
3073    
3074     if ($self->extcmd == 2) {
3075 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
3076 root 1.343 } elsif ($self->extcmd == 1) {
3077 root 1.336 #TODO: version 1, remove
3078     unshift @msg, msgtype => "reply", msgid => $id;
3079 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3080 root 1.336 }
3081     }
3082    
3083 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
3084    
3085     Queues a query to the client, calling the given callback with
3086     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
3087     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
3088    
3089 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
3090     become reliable at some point in the future.
3091 root 1.95
3092     =cut
3093    
3094     sub cf::client::query {
3095     my ($self, $flags, $text, $cb) = @_;
3096    
3097     return unless $self->state == ST_PLAYING
3098     || $self->state == ST_SETUP
3099     || $self->state == ST_CUSTOM;
3100    
3101     $self->state (ST_CUSTOM);
3102    
3103     utf8::encode $text;
3104     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
3105    
3106     $self->send_packet ($self->{query_queue}[0][0])
3107     if @{ $self->{query_queue} } == 1;
3108 root 1.287
3109     1
3110 root 1.95 }
3111    
3112     cf::client->attach (
3113 root 1.290 on_connect => sub {
3114     my ($ns) = @_;
3115    
3116     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
3117     },
3118 root 1.95 on_reply => sub {
3119     my ($ns, $msg) = @_;
3120    
3121     # this weird shuffling is so that direct followup queries
3122     # get handled first
3123 root 1.128 my $queue = delete $ns->{query_queue}
3124 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
3125 root 1.95
3126     (shift @$queue)->[1]->($msg);
3127 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
3128 root 1.95
3129     push @{ $ns->{query_queue} }, @$queue;
3130    
3131     if (@{ $ns->{query_queue} } == @$queue) {
3132     if (@$queue) {
3133     $ns->send_packet ($ns->{query_queue}[0][0]);
3134     } else {
3135 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
3136 root 1.95 }
3137     }
3138     },
3139 root 1.287 on_exticmd => sub {
3140     my ($ns, $buf) = @_;
3141    
3142 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
3143 root 1.287
3144     if (ref $msg) {
3145 root 1.316 my ($type, $reply, @payload) =
3146     "ARRAY" eq ref $msg
3147     ? @$msg
3148     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
3149    
3150 root 1.338 my @reply;
3151    
3152 root 1.316 if (my $cb = $EXTICMD{$type}) {
3153 root 1.338 @reply = $cb->($ns, @payload);
3154     }
3155    
3156     $ns->ext_reply ($reply, @reply)
3157     if $reply;
3158 root 1.316
3159 root 1.287 } else {
3160     warn "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
3161     }
3162    
3163     cf::override;
3164     },
3165 root 1.95 );
3166    
3167 root 1.140 =item $client->async (\&cb)
3168 root 1.96
3169     Create a new coroutine, running the specified callback. The coroutine will
3170     be automatically cancelled when the client gets destroyed (e.g. on logout,
3171     or loss of connection).
3172    
3173     =cut
3174    
3175 root 1.140 sub cf::client::async {
3176 root 1.96 my ($self, $cb) = @_;
3177    
3178 root 1.140 my $coro = &Coro::async ($cb);
3179 root 1.103
3180     $coro->on_destroy (sub {
3181 root 1.96 delete $self->{_coro}{$coro+0};
3182 root 1.103 });
3183 root 1.96
3184     $self->{_coro}{$coro+0} = $coro;
3185 root 1.103
3186     $coro
3187 root 1.96 }
3188    
3189     cf::client->attach (
3190 root 1.509 on_client_destroy => sub {
3191 root 1.96 my ($ns) = @_;
3192    
3193 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
3194 root 1.96 },
3195     );
3196    
3197 root 1.95 =back
3198    
3199 root 1.70
3200     =head2 SAFE SCRIPTING
3201    
3202     Functions that provide a safe environment to compile and execute
3203     snippets of perl code without them endangering the safety of the server
3204     itself. Looping constructs, I/O operators and other built-in functionality
3205     is not available in the safe scripting environment, and the number of
3206 root 1.79 functions and methods that can be called is greatly reduced.
3207 root 1.70
3208     =cut
3209 root 1.23
3210 root 1.42 our $safe = new Safe "safe";
3211 root 1.23 our $safe_hole = new Safe::Hole;
3212    
3213     $SIG{FPE} = 'IGNORE';
3214    
3215 root 1.328 $safe->permit_only (Opcode::opset qw(
3216 elmex 1.498 :base_core :base_mem :base_orig :base_math :base_loop
3217 root 1.328 grepstart grepwhile mapstart mapwhile
3218     sort time
3219     ));
3220 root 1.23
3221 root 1.25 # here we export the classes and methods available to script code
3222    
3223 root 1.70 =pod
3224    
3225 root 1.228 The following functions and methods are available within a safe environment:
3226 root 1.70
3227 root 1.297 cf::object
3228 root 1.383 contr pay_amount pay_player map x y force_find force_add destroy
3229 root 1.425 insert remove name archname title slaying race decrease split
3230 root 1.466 value
3231 root 1.297
3232     cf::object::player
3233     player
3234    
3235     cf::player
3236     peaceful
3237    
3238     cf::map
3239     trigger
3240 root 1.70
3241     =cut
3242    
3243 root 1.25 for (
3244 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
3245 elmex 1.431 insert remove inv nrof name archname title slaying race
3246 root 1.466 decrease split destroy change_exp value msg lore send_msg)],
3247 root 1.25 ["cf::object::player" => qw(player)],
3248 root 1.466 ["cf::player" => qw(peaceful send_msg)],
3249 elmex 1.91 ["cf::map" => qw(trigger)],
3250 root 1.25 ) {
3251     no strict 'refs';
3252     my ($pkg, @funs) = @$_;
3253 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3254 root 1.25 for @funs;
3255     }
3256 root 1.23
3257 root 1.70 =over 4
3258    
3259     =item @retval = safe_eval $code, [var => value, ...]
3260    
3261     Compiled and executes the given perl code snippet. additional var/value
3262     pairs result in temporary local (my) scalar variables of the given name
3263     that are available in the code snippet. Example:
3264    
3265     my $five = safe_eval '$first + $second', first => 1, second => 4;
3266    
3267     =cut
3268    
3269 root 1.23 sub safe_eval($;@) {
3270     my ($code, %vars) = @_;
3271    
3272     my $qcode = $code;
3273     $qcode =~ s/"/‟/g; # not allowed in #line filenames
3274     $qcode =~ s/\n/\\n/g;
3275    
3276 root 1.466 %vars = (_dummy => 0) unless %vars;
3277    
3278 root 1.499 my @res;
3279 root 1.23 local $_;
3280    
3281 root 1.42 my $eval =
3282 root 1.23 "do {\n"
3283     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3284     . "#line 0 \"{$qcode}\"\n"
3285     . $code
3286     . "\n}"
3287 root 1.25 ;
3288    
3289 root 1.499 if ($CFG{safe_eval}) {
3290     sub_generation_inc;
3291     local @safe::cf::_safe_eval_args = values %vars;
3292     @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3293     sub_generation_inc;
3294     } else {
3295     local @cf::_safe_eval_args = values %vars;
3296     @res = wantarray ? eval eval : scalar eval $eval;
3297     }
3298 root 1.25
3299 root 1.42 if ($@) {
3300     warn "$@";
3301     warn "while executing safe code '$code'\n";
3302     warn "with arguments " . (join " ", %vars) . "\n";
3303     }
3304    
3305 root 1.25 wantarray ? @res : $res[0]
3306 root 1.23 }
3307    
3308 root 1.69 =item cf::register_script_function $function => $cb
3309    
3310     Register a function that can be called from within map/npc scripts. The
3311     function should be reasonably secure and should be put into a package name
3312     like the extension.
3313    
3314     Example: register a function that gets called whenever a map script calls
3315     C<rent::overview>, as used by the C<rent> extension.
3316    
3317     cf::register_script_function "rent::overview" => sub {
3318     ...
3319     };
3320    
3321     =cut
3322    
3323 root 1.23 sub register_script_function {
3324     my ($fun, $cb) = @_;
3325    
3326 root 1.501 $fun = "safe::$fun" if $CFG{safe_eval};
3327     *$fun = $safe_hole->wrap ($cb);
3328 root 1.23 }
3329    
3330 root 1.70 =back
3331    
3332 root 1.71 =cut
3333    
3334 root 1.23 #############################################################################
3335 root 1.203 # the server's init and main functions
3336    
3337 root 1.246 sub load_facedata($) {
3338     my ($path) = @_;
3339 root 1.223
3340 root 1.348 # HACK to clear player env face cache, we need some signal framework
3341     # for this (global event?)
3342     %ext::player_env::MUSIC_FACE_CACHE = ();
3343    
3344 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3345 root 1.334
3346 root 1.229 warn "loading facedata from $path\n";
3347 root 1.223
3348 root 1.236 my $facedata;
3349     0 < aio_load $path, $facedata
3350 root 1.223 or die "$path: $!";
3351    
3352 root 1.237 $facedata = Coro::Storable::thaw $facedata;
3353 root 1.223
3354 root 1.236 $facedata->{version} == 2
3355 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3356    
3357 root 1.334 # patch in the exptable
3358 root 1.500 my $exp_table = $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]);
3359 root 1.334 $facedata->{resource}{"res/exp_table"} = {
3360     type => FT_RSRC,
3361 root 1.500 data => $exp_table,
3362     hash => (Digest::MD5::md5 $exp_table),
3363 root 1.334 };
3364     cf::cede_to_tick;
3365    
3366 root 1.236 {
3367     my $faces = $facedata->{faceinfo};
3368    
3369     while (my ($face, $info) = each %$faces) {
3370     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3371 root 1.405
3372 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3373     cf::face::set_magicmap $idx, $info->{magicmap};
3374 root 1.496 cf::face::set_data $idx, 0, $info->{data32}, $info->{hash32};
3375     cf::face::set_data $idx, 1, $info->{data64}, $info->{hash64};
3376 root 1.302
3377     cf::cede_to_tick;
3378 root 1.236 }
3379    
3380     while (my ($face, $info) = each %$faces) {
3381     next unless $info->{smooth};
3382 root 1.405
3383 root 1.236 my $idx = cf::face::find $face
3384     or next;
3385 root 1.405
3386 root 1.236 if (my $smooth = cf::face::find $info->{smooth}) {
3387 root 1.302 cf::face::set_smooth $idx, $smooth;
3388     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3389 root 1.236 } else {
3390     warn "smooth face '$info->{smooth}' not found for face '$face'";
3391     }
3392 root 1.302
3393     cf::cede_to_tick;
3394 root 1.236 }
3395 root 1.223 }
3396    
3397 root 1.236 {
3398     my $anims = $facedata->{animinfo};
3399    
3400     while (my ($anim, $info) = each %$anims) {
3401     cf::anim::set $anim, $info->{frames}, $info->{facings};
3402 root 1.302 cf::cede_to_tick;
3403 root 1.225 }
3404 root 1.236
3405     cf::anim::invalidate_all; # d'oh
3406 root 1.225 }
3407    
3408 root 1.302 {
3409     my $res = $facedata->{resource};
3410    
3411     while (my ($name, $info) = each %$res) {
3412 root 1.405 if (defined $info->{type}) {
3413     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3414    
3415 root 1.496 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3416 root 1.405 cf::face::set_type $idx, $info->{type};
3417 root 1.337 } else {
3418 root 1.405 $RESOURCE{$name} = $info;
3419 root 1.307 }
3420 root 1.302
3421     cf::cede_to_tick;
3422     }
3423 root 1.406 }
3424    
3425     cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3426 root 1.321
3427 root 1.406 1
3428     }
3429    
3430     cf::global->attach (on_resource_update => sub {
3431     if (my $soundconf = $RESOURCE{"res/sound.conf"}) {
3432     $soundconf = JSON::XS->new->utf8->relaxed->decode ($soundconf->{data});
3433    
3434     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3435     my $sound = $soundconf->{compat}[$_]
3436     or next;
3437 root 1.321
3438 root 1.406 my $face = cf::face::find "sound/$sound->[1]";
3439     cf::sound::set $sound->[0] => $face;
3440     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3441     }
3442 root 1.321
3443 root 1.406 while (my ($k, $v) = each %{$soundconf->{event}}) {
3444     my $face = cf::face::find "sound/$v";
3445     cf::sound::set $k => $face;
3446 root 1.321 }
3447 root 1.302 }
3448 root 1.406 });
3449 root 1.223
3450 root 1.318 register_exticmd fx_want => sub {
3451     my ($ns, $want) = @_;
3452    
3453     while (my ($k, $v) = each %$want) {
3454     $ns->fx_want ($k, $v);
3455     }
3456     };
3457    
3458 root 1.423 sub load_resource_file($) {
3459 root 1.424 my $guard = lock_acquire "load_resource_file";
3460    
3461 root 1.423 my $status = load_resource_file_ $_[0];
3462     get_slot 0.1, 100;
3463     cf::arch::commit_load;
3464 root 1.424
3465 root 1.423 $status
3466     }
3467    
3468 root 1.253 sub reload_regions {
3469 root 1.348 # HACK to clear player env face cache, we need some signal framework
3470     # for this (global event?)
3471     %ext::player_env::MUSIC_FACE_CACHE = ();
3472    
3473 root 1.253 load_resource_file "$MAPDIR/regions"
3474     or die "unable to load regions file\n";
3475 root 1.304
3476     for (cf::region::list) {
3477     $_->{match} = qr/$_->{match}/
3478     if exists $_->{match};
3479     }
3480 root 1.253 }
3481    
3482 root 1.246 sub reload_facedata {
3483 root 1.253 load_facedata "$DATADIR/facedata"
3484 root 1.246 or die "unable to load facedata\n";
3485     }
3486    
3487     sub reload_archetypes {
3488 root 1.253 load_resource_file "$DATADIR/archetypes"
3489 root 1.246 or die "unable to load archetypes\n";
3490 root 1.241 }
3491    
3492 root 1.246 sub reload_treasures {
3493 root 1.253 load_resource_file "$DATADIR/treasures"
3494 root 1.246 or die "unable to load treasurelists\n";
3495 root 1.241 }
3496    
3497 root 1.223 sub reload_resources {
3498 root 1.245 warn "reloading resource files...\n";
3499    
3500 root 1.246 reload_facedata;
3501     reload_archetypes;
3502 root 1.423 reload_regions;
3503 root 1.246 reload_treasures;
3504 root 1.245
3505     warn "finished reloading resource files\n";
3506 root 1.223 }
3507    
3508 root 1.345 sub reload_config {
3509 root 1.485 warn "reloading config file...\n";
3510    
3511 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3512 root 1.72 or return;
3513    
3514     local $/;
3515 root 1.485 *CFG = YAML::XS::Load scalar <$fh>;
3516 root 1.131
3517     $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_105_115", 5, 37];
3518    
3519 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3520     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3521    
3522 root 1.131 if (exists $CFG{mlockall}) {
3523     eval {
3524 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3525 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3526     };
3527     warn $@ if $@;
3528     }
3529 root 1.485
3530     warn "finished reloading resource files\n";
3531 root 1.72 }
3532    
3533 root 1.445 sub pidfile() {
3534     sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3535     or die "$PIDFILE: $!";
3536     flock $fh, &Fcntl::LOCK_EX
3537     or die "$PIDFILE: flock: $!";
3538     $fh
3539     }
3540    
3541     # make sure only one server instance is running at any one time
3542     sub atomic {
3543     my $fh = pidfile;
3544    
3545     my $pid = <$fh>;
3546     kill 9, $pid if $pid > 0;
3547    
3548     seek $fh, 0, 0;
3549     print $fh $$;
3550     }
3551    
3552 root 1.474 sub main_loop {
3553     warn "EV::loop starting\n";
3554     if (1) {
3555     EV::loop;
3556     }
3557     warn "EV::loop returned\n";
3558     goto &main_loop unless $REALLY_UNLOOP;
3559     }
3560    
3561 root 1.39 sub main {
3562 root 1.453 cf::init_globals; # initialise logging
3563    
3564     LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3565     LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3566     LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3567     LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen.";
3568    
3569     $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3570 root 1.445
3571 root 1.108 # we must not ever block the main coroutine
3572     local $Coro::idle = sub {
3573 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3574 root 1.175 (async {
3575 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3576 root 1.396 EV::loop EV::LOOP_ONESHOT;
3577 root 1.175 })->prio (Coro::PRIO_MAX);
3578 root 1.108 };
3579    
3580 root 1.453 evthread_start IO::AIO::poll_fileno;
3581    
3582     cf::sync_job {
3583 root 1.515 cf::init_experience;
3584     cf::init_anim;
3585     cf::init_attackmess;
3586     cf::init_dynamic;
3587    
3588 root 1.495 cf::load_settings;
3589     cf::load_materials;
3590    
3591 root 1.453 reload_resources;
3592 root 1.423 reload_config;
3593     db_init;
3594 root 1.453
3595     cf::init_uuid;
3596     cf::init_signals;
3597     cf::init_skills;
3598    
3599     cf::init_beforeplay;
3600    
3601     atomic;
3602    
3603 root 1.423 load_extensions;
3604    
3605 root 1.453 utime time, time, $RUNTIMEFILE;
3606 root 1.183
3607 root 1.453 # no (long-running) fork's whatsoever before this point(!)
3608 root 1.475 use POSIX ();
3609 root 1.453 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3610 root 1.445
3611 root 1.453 (pop @POST_INIT)->(0) while @POST_INIT;
3612     };
3613 root 1.445
3614 root 1.516 cf::object::thawer::errors_are_fatal 0;
3615 root 1.517 warn "parse errors in files are no longer fatal from this point on.\n";
3616 root 1.516
3617 root 1.474 main_loop;
3618 root 1.34 }
3619    
3620     #############################################################################
3621 root 1.155 # initialisation and cleanup
3622    
3623     # install some emergency cleanup handlers
3624     BEGIN {
3625 root 1.396 our %SIGWATCHER = ();
3626 root 1.155 for my $signal (qw(INT HUP TERM)) {
3627 root 1.512 $SIGWATCHER{$signal} = AE::signal $signal, sub {
3628 root 1.396 cf::cleanup "SIG$signal";
3629     };
3630 root 1.155 }
3631     }
3632    
3633 root 1.417 sub write_runtime_sync {
3634 root 1.512 my $t0 = AE::time;
3635 root 1.506
3636 root 1.281 # first touch the runtime file to show we are still running:
3637     # the fsync below can take a very very long time.
3638    
3639 root 1.445 IO::AIO::aio_utime $RUNTIMEFILE, undef, undef;
3640 root 1.281
3641     my $guard = cf::lock_acquire "write_runtime";
3642    
3643 root 1.505 my $fh = aio_open "$RUNTIMEFILE~", O_WRONLY | O_CREAT | O_TRUNC, 0644
3644 root 1.281 or return;
3645    
3646     my $value = $cf::RUNTIME + 90 + 10;
3647     # 10 is the runtime save interval, for a monotonic clock
3648     # 60 allows for the watchdog to kill the server.
3649    
3650     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3651     and return;
3652    
3653     # always fsync - this file is important
3654     aio_fsync $fh
3655     and return;
3656    
3657     # touch it again to show we are up-to-date
3658     aio_utime $fh, undef, undef;
3659    
3660     close $fh
3661     or return;
3662    
3663 root 1.445 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3664 root 1.281 and return;
3665    
3666 root 1.512 warn sprintf "runtime file written (%gs).\n", AE::time - $t0;
3667 root 1.281
3668     1
3669     }
3670    
3671 root 1.416 our $uuid_lock;
3672     our $uuid_skip;
3673    
3674     sub write_uuid_sync($) {
3675     $uuid_skip ||= $_[0];
3676    
3677     return if $uuid_lock;
3678     local $uuid_lock = 1;
3679    
3680     my $uuid = "$LOCALDIR/uuid";
3681    
3682     my $fh = aio_open "$uuid~", O_WRONLY | O_CREAT, 0644
3683     or return;
3684    
3685 root 1.454 my $value = uuid_seq uuid_cur;
3686    
3687 root 1.455 unless ($value) {
3688     warn "cowardly refusing to write zero uuid value!\n";
3689 root 1.454 return;
3690     }
3691    
3692     my $value = uuid_str $value + $uuid_skip;
3693 root 1.416 $uuid_skip = 0;
3694    
3695     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3696     and return;
3697    
3698     # always fsync - this file is important
3699     aio_fsync $fh
3700     and return;
3701    
3702     close $fh
3703     or return;
3704    
3705     aio_rename "$uuid~", $uuid
3706     and return;
3707    
3708     warn "uuid file written ($value).\n";
3709    
3710     1
3711    
3712     }
3713    
3714     sub write_uuid($$) {
3715     my ($skip, $sync) = @_;
3716    
3717     $sync ? write_uuid_sync $skip
3718     : async { write_uuid_sync $skip };
3719     }
3720    
3721 root 1.156 sub emergency_save() {
3722 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3723    
3724 root 1.457 warn "emergency_perl_save: enter\n";
3725 root 1.155
3726     cf::sync_job {
3727 root 1.457 # this is a trade-off: we want to be very quick here, so
3728     # save all maps without fsync, and later call a global sync
3729     # (which in turn might be very very slow)
3730     local $USE_FSYNC = 0;
3731    
3732 root 1.155 # use a peculiar iteration method to avoid tripping on perl
3733     # refcount bugs in for. also avoids problems with players
3734 root 1.167 # and maps saved/destroyed asynchronously.
3735 root 1.457 warn "emergency_perl_save: begin player save\n";
3736 root 1.155 for my $login (keys %cf::PLAYER) {
3737     my $pl = $cf::PLAYER{$login} or next;
3738     $pl->valid or next;
3739 root 1.382 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3740 root 1.155 $pl->save;
3741     }
3742 root 1.457 warn "emergency_perl_save: end player save\n";
3743 root 1.155
3744 root 1.457 warn "emergency_perl_save: begin map save\n";
3745 root 1.155 for my $path (keys %cf::MAP) {
3746     my $map = $cf::MAP{$path} or next;
3747     $map->valid or next;
3748     $map->save;
3749     }
3750 root 1.457 warn "emergency_perl_save: end map save\n";
3751 root 1.208
3752 root 1.457 warn "emergency_perl_save: begin database checkpoint\n";
3753 root 1.208 BDB::db_env_txn_checkpoint $DB_ENV;
3754 root 1.457 warn "emergency_perl_save: end database checkpoint\n";
3755 root 1.416
3756 root 1.457 warn "emergency_perl_save: begin write uuid\n";
3757 root 1.416 write_uuid_sync 1;
3758 root 1.457 warn "emergency_perl_save: end write uuid\n";
3759 root 1.155 };
3760    
3761 root 1.457 warn "emergency_perl_save: starting sync()\n";
3762     IO::AIO::aio_sync sub {
3763     warn "emergency_perl_save: finished sync()\n";
3764     };
3765    
3766     warn "emergency_perl_save: leave\n";
3767 root 1.155 }
3768 root 1.22
3769 root 1.211 sub post_cleanup {
3770     my ($make_core) = @_;
3771    
3772     warn Carp::longmess "post_cleanup backtrace"
3773     if $make_core;
3774 root 1.445
3775     my $fh = pidfile;
3776     unlink $PIDFILE if <$fh> == $$;
3777 root 1.211 }
3778    
3779 root 1.441 # a safer delete_package, copied from Symbol
3780     sub clear_package($) {
3781     my $pkg = shift;
3782    
3783     # expand to full symbol table name if needed
3784     unless ($pkg =~ /^main::.*::$/) {
3785     $pkg = "main$pkg" if $pkg =~ /^::/;
3786     $pkg = "main::$pkg" unless $pkg =~ /^main::/;
3787     $pkg .= '::' unless $pkg =~ /::$/;
3788     }
3789    
3790     my($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
3791     my $stem_symtab = *{$stem}{HASH};
3792    
3793     defined $stem_symtab and exists $stem_symtab->{$leaf}
3794     or return;
3795    
3796     # clear all symbols
3797     my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3798     for my $name (keys %$leaf_symtab) {
3799     _gv_clear *{"$pkg$name"};
3800     # use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3801     }
3802     }
3803    
3804 root 1.246 sub do_reload_perl() {
3805 root 1.106 # can/must only be called in main
3806 root 1.522 if (in_main) {
3807 root 1.183 warn "can only reload from main coroutine";
3808 root 1.106 return;
3809     }
3810    
3811 root 1.441 return if $RELOAD++;
3812    
3813 root 1.512 my $t1 = AE::time;
3814 root 1.457
3815 root 1.441 while ($RELOAD) {
3816     warn "reloading...";
3817 root 1.103
3818 root 1.441 warn "entering sync_job";
3819 root 1.212
3820 root 1.441 cf::sync_job {
3821     cf::write_runtime_sync; # external watchdog should not bark
3822     cf::emergency_save;
3823     cf::write_runtime_sync; # external watchdog should not bark
3824 root 1.183
3825 root 1.441 warn "syncing database to disk";
3826     BDB::db_env_txn_checkpoint $DB_ENV;
3827 root 1.106
3828 root 1.441 # if anything goes wrong in here, we should simply crash as we already saved
3829 root 1.65
3830 root 1.441 warn "flushing outstanding aio requests";
3831     while (IO::AIO::nreqs || BDB::nreqs) {
3832     Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3833     }
3834 root 1.183
3835 root 1.441 warn "cancelling all extension coros";
3836     $_->cancel for values %EXT_CORO;
3837     %EXT_CORO = ();
3838 root 1.223
3839 root 1.441 warn "removing commands";
3840     %COMMAND = ();
3841 root 1.103
3842 root 1.441 warn "removing ext/exti commands";
3843     %EXTCMD = ();
3844     %EXTICMD = ();
3845 root 1.159
3846 root 1.441 warn "unloading/nuking all extensions";
3847     for my $pkg (@EXTS) {
3848     warn "... unloading $pkg";
3849 root 1.159
3850 root 1.441 if (my $cb = $pkg->can ("unload")) {
3851     eval {
3852     $cb->($pkg);
3853     1
3854     } or warn "$pkg unloaded, but with errors: $@";
3855     }
3856 root 1.159
3857 root 1.441 warn "... clearing $pkg";
3858     clear_package $pkg;
3859 root 1.159 }
3860    
3861 root 1.441 warn "unloading all perl modules loaded from $LIBDIR";
3862     while (my ($k, $v) = each %INC) {
3863     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3864 root 1.65
3865 root 1.441 warn "... unloading $k";
3866     delete $INC{$k};
3867 root 1.65
3868 root 1.441 $k =~ s/\.pm$//;
3869     $k =~ s/\//::/g;
3870 root 1.65
3871 root 1.441 if (my $cb = $k->can ("unload_module")) {
3872     $cb->();
3873     }
3874 root 1.65
3875 root 1.441 clear_package $k;
3876 root 1.65 }
3877    
3878 root 1.441 warn "getting rid of safe::, as good as possible";
3879     clear_package "safe::$_"
3880     for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3881 root 1.65
3882 root 1.441 warn "unloading cf.pm \"a bit\"";
3883     delete $INC{"cf.pm"};
3884 root 1.466 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES;
3885 root 1.65
3886 root 1.441 # don't, removes xs symbols, too,
3887     # and global variables created in xs
3888     #clear_package __PACKAGE__;
3889 root 1.65
3890 root 1.441 warn "unload completed, starting to reload now";
3891 root 1.65
3892 root 1.441 warn "reloading cf.pm";
3893     require cf;
3894 root 1.483 cf::_connect_to_perl_1;
3895 root 1.183
3896 root 1.441 warn "loading config and database again";
3897     cf::reload_config;
3898 root 1.100
3899 root 1.441 warn "loading extensions";
3900     cf::load_extensions;
3901 root 1.65
3902 root 1.457 if ($REATTACH_ON_RELOAD) {
3903     warn "reattaching attachments to objects/players";
3904     _global_reattach; # objects, sockets
3905     warn "reattaching attachments to maps";
3906     reattach $_ for values %MAP;
3907     warn "reattaching attachments to players";
3908     reattach $_ for values %PLAYER;
3909     }
3910 root 1.65
3911 root 1.457 warn "running post_init jobs";
3912 root 1.453 (pop @POST_INIT)->(1) while @POST_INIT;
3913    
3914 root 1.441 warn "leaving sync_job";
3915 root 1.183
3916 root 1.441 1
3917     } or do {
3918     warn $@;
3919     cf::cleanup "error while reloading, exiting.";
3920     };
3921 root 1.183
3922 root 1.441 warn "reloaded";
3923     --$RELOAD;
3924     }
3925 root 1.457
3926 root 1.512 $t1 = AE::time - $t1;
3927 root 1.457 warn "reload completed in ${t1}s\n";
3928 root 1.65 };
3929    
3930 root 1.175 our $RELOAD_WATCHER; # used only during reload
3931    
3932 root 1.246 sub reload_perl() {
3933     # doing reload synchronously and two reloads happen back-to-back,
3934     # coro crashes during coro_state_free->destroy here.
3935    
3936 root 1.457 $RELOAD_WATCHER ||= cf::async {
3937     Coro::AIO::aio_wait cache_extensions;
3938    
3939 root 1.512 $RELOAD_WATCHER = AE::timer $TICK * 1.5, 0, sub {
3940 root 1.457 do_reload_perl;
3941     undef $RELOAD_WATCHER;
3942     };
3943 root 1.396 };
3944 root 1.246 }
3945    
3946 root 1.111 register_command "reload" => sub {
3947 root 1.65 my ($who, $arg) = @_;
3948    
3949     if ($who->flag (FLAG_WIZ)) {
3950 root 1.175 $who->message ("reloading server.");
3951 root 1.374 async {
3952     $Coro::current->{desc} = "perl_reload";
3953     reload_perl;
3954     };
3955 root 1.65 }
3956     };
3957    
3958 root 1.27 unshift @INC, $LIBDIR;
3959 root 1.17
3960 root 1.183 my $bug_warning = 0;
3961    
3962 root 1.239 our @WAIT_FOR_TICK;
3963     our @WAIT_FOR_TICK_BEGIN;
3964    
3965     sub wait_for_tick {
3966 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
3967 root 1.241
3968 root 1.239 my $signal = new Coro::Signal;
3969     push @WAIT_FOR_TICK, $signal;
3970     $signal->wait;
3971     }
3972    
3973     sub wait_for_tick_begin {
3974 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
3975 root 1.241
3976 root 1.239 my $signal = new Coro::Signal;
3977     push @WAIT_FOR_TICK_BEGIN, $signal;
3978     $signal->wait;
3979     }
3980    
3981 root 1.412 sub tick {
3982 root 1.396 if ($Coro::current != $Coro::main) {
3983     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
3984     unless ++$bug_warning > 10;
3985     return;
3986     }
3987    
3988     cf::server_tick; # one server iteration
3989 root 1.245
3990 root 1.512 #for(1..3e6){} AE::now_update; $NOW=AE::now; # generate load #d#
3991 root 1.502
3992 root 1.396 if ($NOW >= $NEXT_RUNTIME_WRITE) {
3993 root 1.402 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
3994 root 1.396 Coro::async_pool {
3995     $Coro::current->{desc} = "runtime saver";
3996 root 1.417 write_runtime_sync
3997 root 1.396 or warn "ERROR: unable to write runtime file: $!";
3998     };
3999     }
4000 root 1.265
4001 root 1.396 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
4002     $sig->send;
4003     }
4004     while (my $sig = shift @WAIT_FOR_TICK) {
4005     $sig->send;
4006     }
4007 root 1.265
4008 root 1.412 $LOAD = ($NOW - $TICK_START) / $TICK;
4009 root 1.396 $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
4010 root 1.265
4011 root 1.412 if (0) {
4012     if ($NEXT_TICK) {
4013     my $jitter = $TICK_START - $NEXT_TICK;
4014     $JITTER = $JITTER * 0.75 + $jitter * 0.25;
4015     warn "jitter $JITTER\n";#d#
4016     }
4017     }
4018     }
4019 root 1.35
4020 root 1.206 {
4021 root 1.401 # configure BDB
4022    
4023 root 1.503 BDB::min_parallel 16;
4024 root 1.400 BDB::max_poll_reqs $TICK * 0.1;
4025 root 1.433 $AnyEvent::BDB::WATCHER->priority (1);
4026 root 1.77
4027 root 1.206 unless ($DB_ENV) {
4028     $DB_ENV = BDB::db_env_create;
4029 root 1.437 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT);
4030     $DB_ENV->set_flags (&BDB::LOG_AUTOREMOVE ) if BDB::VERSION v0, v4.7;
4031     $DB_ENV->log_set_config (&BDB::LOG_AUTO_REMOVE) if BDB::VERSION v4.7;
4032 root 1.371 $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
4033     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
4034 root 1.206
4035     cf::sync_job {
4036 root 1.208 eval {
4037     BDB::db_env_open
4038     $DB_ENV,
4039 root 1.253 $BDBDIR,
4040 root 1.208 BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
4041     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
4042     0666;
4043    
4044 root 1.253 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
4045 root 1.208 };
4046    
4047     cf::cleanup "db_env_open(db): $@" if $@;
4048 root 1.206 };
4049     }
4050 root 1.363
4051 root 1.396 $BDB_DEADLOCK_WATCHER = EV::periodic 0, 3, 0, sub {
4052     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
4053     };
4054     $BDB_CHECKPOINT_WATCHER = EV::periodic 0, 60, 0, sub {
4055     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
4056     };
4057     $BDB_TRICKLE_WATCHER = EV::periodic 0, 10, 0, sub {
4058     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
4059     };
4060 root 1.206 }
4061    
4062     {
4063 root 1.401 # configure IO::AIO
4064    
4065 root 1.206 IO::AIO::min_parallel 8;
4066     IO::AIO::max_poll_time $TICK * 0.1;
4067 root 1.435 undef $AnyEvent::AIO::WATCHER;
4068 root 1.206 }
4069 root 1.108
4070 root 1.262 my $_log_backtrace;
4071    
4072 root 1.260 sub _log_backtrace {
4073     my ($msg, @addr) = @_;
4074    
4075 root 1.262 $msg =~ s/\n//;
4076 root 1.260
4077 root 1.262 # limit the # of concurrent backtraces
4078     if ($_log_backtrace < 2) {
4079     ++$_log_backtrace;
4080 root 1.446 my $perl_bt = Carp::longmess $msg;
4081 root 1.262 async {
4082 root 1.374 $Coro::current->{desc} = "abt $msg";
4083    
4084 root 1.262 my @bt = fork_call {
4085     @addr = map { sprintf "%x", $_ } @addr;
4086     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
4087     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
4088     or die "addr2line: $!";
4089    
4090     my @funcs;
4091     my @res = <$fh>;
4092     chomp for @res;
4093     while (@res) {
4094     my ($func, $line) = splice @res, 0, 2, ();
4095     push @funcs, "[$func] $line";
4096     }
4097 root 1.260
4098 root 1.262 @funcs
4099     };
4100 root 1.260
4101 root 1.446 LOG llevInfo, "[ABT] $perl_bt\n";
4102     LOG llevInfo, "[ABT] --- C backtrace follows ---\n";
4103 root 1.262 LOG llevInfo, "[ABT] $_\n" for @bt;
4104     --$_log_backtrace;
4105     };
4106     } else {
4107 root 1.260 LOG llevInfo, "[ABT] $msg\n";
4108 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
4109     }
4110 root 1.260 }
4111    
4112 root 1.249 # load additional modules
4113 root 1.467 require "cf/$_.pm" for @EXTRA_MODULES;
4114 root 1.483 cf::_connect_to_perl_2;
4115 root 1.249
4116 root 1.125 END { cf::emergency_save }
4117    
4118 root 1.1 1
4119