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