ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.542
Committed: Wed May 5 09:05:03 2010 UTC (14 years ago) by root
Branch: MAIN
Changes since 1.541: +0 -1 lines
Log Message:
require 5.10.1

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