ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.539
Committed: Tue May 4 22:49:21 2010 UTC (14 years, 2 months ago) by root
Branch: MAIN
Changes since 1.538: +1 -5 lines
Log Message:
more common sense

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