ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.538
Committed: Tue May 4 21:45:43 2010 UTC (14 years ago) by root
Branch: MAIN
Changes since 1.537: +2 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.484 #
2 root 1.412 # This file is part of Deliantra, the Roguelike Realtime MMORPG.
3     #
4 root 1.504 # Copyright (©) 2006,2007,2008,2009,2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 root 1.412 #
6 root 1.484 # Deliantra is free software: you can redistribute it and/or modify it under
7     # the terms of the Affero GNU General Public License as published by the
8     # Free Software Foundation, either version 3 of the License, or (at your
9     # option) any later version.
10 root 1.412 #
11     # This program is distributed in the hope that it will be useful,
12     # but WITHOUT ANY WARRANTY; without even the implied warranty of
13     # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     # GNU General Public License for more details.
15     #
16 root 1.484 # You should have received a copy of the Affero GNU General Public License
17     # and the GNU General Public License along with this program. If not, see
18     # <http://www.gnu.org/licenses/>.
19 root 1.412 #
20     # The authors can be reached via e-mail to <support@deliantra.net>
21 root 1.484 #
22 root 1.412
23 root 1.1 package cf;
24    
25 root 1.441 use 5.10.0;
26 root 1.96 use utf8;
27 root 1.466 use strict qw(vars subs);
28 root 1.96
29 root 1.1 use Symbol;
30     use List::Util;
31 root 1.250 use Socket;
32 root 1.433 use EV;
33 root 1.23 use Opcode;
34     use Safe;
35     use Safe::Hole;
36 root 1.385 use Storable ();
37 root 1.493 use Carp ();
38 root 1.19
39 root 1.458 use Guard ();
40 root 1.433 use Coro ();
41 root 1.224 use Coro::State;
42 root 1.250 use Coro::Handle;
43 root 1.441 use Coro::EV;
44 root 1.434 use Coro::AnyEvent;
45 root 1.96 use Coro::Timer;
46     use Coro::Signal;
47     use Coro::Semaphore;
48 root 1.459 use Coro::SemaphoreSet;
49 root 1.433 use Coro::AnyEvent;
50 root 1.105 use Coro::AIO;
51 root 1.437 use Coro::BDB 1.6;
52 root 1.237 use Coro::Storable;
53 root 1.332 use Coro::Util ();
54 root 1.96
55 root 1.398 use JSON::XS 2.01 ();
56 root 1.206 use BDB ();
57 root 1.154 use Data::Dumper;
58 root 1.105 use Fcntl;
59 root 1.484 use YAML::XS ();
60 root 1.433 use IO::AIO ();
61 root 1.32 use Time::HiRes;
62 root 1.208 use Compress::LZF;
63 root 1.302 use Digest::MD5 ();
64 root 1.208
65 root 1.433 AnyEvent::detect;
66    
67 root 1.227 # configure various modules to our taste
68     #
69 root 1.237 $Storable::canonical = 1; # reduce rsync transfers
70 root 1.224 Coro::State::cctx_stacksize 256000; # 1-2MB stack, for deep recursions in maze generator
71 root 1.227
72 root 1.139 $Coro::main->prio (Coro::PRIO_MAX); # run main coroutine ("the server") with very high priority
73 root 1.1
74 root 1.449 # make sure c-lzf reinitialises itself
75     Compress::LZF::set_serializer "Storable", "Storable::net_mstore", "Storable::mretrieve";
76     Compress::LZF::sfreeze_cr { }; # prime Compress::LZF so it does not use require later
77 root 1.448
78 root 1.474 # strictly for debugging
79     $SIG{QUIT} = sub { Carp::cluck "SIGQUIT" };
80    
81 root 1.227 sub WF_AUTOCANCEL () { 1 } # automatically cancel this watcher on reload
82    
83 root 1.85 our %COMMAND = ();
84     our %COMMAND_TIME = ();
85 root 1.159
86     our @EXTS = (); # list of extension package names
87 root 1.85 our %EXTCMD = ();
88 root 1.287 our %EXTICMD = ();
89 root 1.159 our %EXT_CORO = (); # coroutines bound to extensions
90 root 1.161 our %EXT_MAP = (); # pluggable maps
91 root 1.85
92 root 1.451 our $RELOAD; # number of reloads so far, non-zero while in reload
93 root 1.1 our @EVENT;
94 root 1.479 our @REFLECT; # set by XS
95     our %REFLECT; # set by us
96 root 1.253
97 root 1.445 our $CONFDIR = confdir;
98     our $DATADIR = datadir;
99     our $LIBDIR = "$DATADIR/ext";
100     our $PODDIR = "$DATADIR/pod";
101     our $MAPDIR = "$DATADIR/" . mapdir;
102     our $LOCALDIR = localdir;
103     our $TMPDIR = "$LOCALDIR/" . tmpdir;
104     our $UNIQUEDIR = "$LOCALDIR/" . uniquedir;
105     our $PLAYERDIR = "$LOCALDIR/" . playerdir;
106     our $RANDOMDIR = "$LOCALDIR/random";
107     our $BDBDIR = "$LOCALDIR/db";
108     our $PIDFILE = "$LOCALDIR/pid";
109     our $RUNTIMEFILE = "$LOCALDIR/runtime";
110    
111 root 1.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.538 undef $@; # work around perl 5.10.0 utf-8 caching bug
1457 root 1.505
1458     cf::cleanup "mandatory extension '$k' failed to load, exiting."
1459     if exists $v->{meta}{mandatory};
1460 root 1.521
1461     warn "$v->{base}: optional extension cannot be loaded, skipping.\n";
1462     delete $todo{$k};
1463 root 1.505 } else {
1464     $done{$k} = delete $todo{$k};
1465     push @EXTS, $v->{pkg};
1466     $progress = 1;
1467 root 1.278
1468 root 1.532 info "$v->{base}: extension inactive.\n"
1469 root 1.505 unless $active;
1470 root 1.278 }
1471 root 1.505 }
1472    
1473     unless ($progress) {
1474     warn "cannot load " . (join ", ", keys %todo) . ": unable to resolve dependencies\n";
1475 root 1.278
1476 root 1.505 while (my ($k, $v) = each %todo) {
1477     cf::cleanup "mandatory extension '$k' has unresolved dependencies, exiting."
1478     if exists $v->{meta}{mandatory};
1479     }
1480 root 1.278 }
1481     }
1482     };
1483 root 1.1 }
1484    
1485 root 1.8 #############################################################################
1486 root 1.70
1487 root 1.281 =back
1488    
1489 root 1.70 =head2 CORE EXTENSIONS
1490    
1491 root 1.447 Functions and methods that extend core deliantra objects.
1492 root 1.70
1493 root 1.143 =cut
1494    
1495     package cf::player;
1496    
1497 root 1.154 use Coro::AIO;
1498    
1499 root 1.95 =head3 cf::player
1500    
1501 root 1.70 =over 4
1502 root 1.22
1503 root 1.361 =item cf::player::num_playing
1504    
1505     Returns the official number of playing players, as per the Crossfire metaserver rules.
1506    
1507     =cut
1508    
1509     sub num_playing {
1510     scalar grep
1511     $_->ob->map
1512     && !$_->hidden
1513     && !$_->ob->flag (cf::FLAG_WIZ),
1514     cf::player::list
1515     }
1516    
1517 root 1.143 =item cf::player::find $login
1518 root 1.23
1519 root 1.143 Returns the given player object, loading it if necessary (might block).
1520 root 1.23
1521     =cut
1522    
1523 root 1.145 sub playerdir($) {
1524 root 1.253 "$PLAYERDIR/"
1525 root 1.145 . (ref $_[0] ? $_[0]->ob->name : $_[0])
1526     }
1527    
1528 root 1.143 sub path($) {
1529 root 1.145 my $login = ref $_[0] ? $_[0]->ob->name : $_[0];
1530    
1531 root 1.234 (playerdir $login) . "/playerdata"
1532 root 1.143 }
1533    
1534     sub find_active($) {
1535     $cf::PLAYER{$_[0]}
1536     and $cf::PLAYER{$_[0]}->active
1537     and $cf::PLAYER{$_[0]}
1538     }
1539    
1540     sub exists($) {
1541     my ($login) = @_;
1542    
1543     $cf::PLAYER{$login}
1544 root 1.452 or !aio_stat path $login
1545 root 1.143 }
1546    
1547     sub find($) {
1548     return $cf::PLAYER{$_[0]} || do {
1549     my $login = $_[0];
1550    
1551     my $guard = cf::lock_acquire "user_find:$login";
1552    
1553 root 1.151 $cf::PLAYER{$_[0]} || do {
1554 root 1.234 # rename old playerfiles to new ones
1555     #TODO: remove when no longer required
1556     aio_link +(playerdir $login) . "/$login.pl.pst", (playerdir $login) . "/playerdata.pst";
1557     aio_link +(playerdir $login) . "/$login.pl" , (playerdir $login) . "/playerdata";
1558     aio_unlink +(playerdir $login) . "/$login.pl.pst";
1559     aio_unlink +(playerdir $login) . "/$login.pl";
1560    
1561 root 1.356 my $f = new_from_file cf::object::thawer path $login
1562 root 1.151 or return;
1563 root 1.356
1564     my $pl = cf::player::load_pl $f
1565     or return;
1566 root 1.427
1567 root 1.356 local $cf::PLAYER_LOADING{$login} = $pl;
1568     $f->resolve_delayed_derefs;
1569 root 1.151 $cf::PLAYER{$login} = $pl
1570     }
1571     }
1572 root 1.143 }
1573    
1574 root 1.511 cf::player->attach (
1575     on_load => sub {
1576     my ($pl, $path) = @_;
1577    
1578     # restore slots saved in save, below
1579     my $slots = delete $pl->{_slots};
1580    
1581     $pl->ob->current_weapon ($slots->[0]);
1582     $pl->combat_ob ($slots->[1]);
1583     $pl->ranged_ob ($slots->[2]);
1584     },
1585     );
1586    
1587 root 1.143 sub save($) {
1588     my ($pl) = @_;
1589    
1590     return if $pl->{deny_save};
1591    
1592     my $path = path $pl;
1593     my $guard = cf::lock_acquire "user_save:$path";
1594    
1595     return if $pl->{deny_save};
1596 root 1.146
1597 root 1.154 aio_mkdir playerdir $pl, 0770;
1598 root 1.143 $pl->{last_save} = $cf::RUNTIME;
1599    
1600 root 1.420 cf::get_slot 0.01;
1601    
1602 root 1.511 # save slots, to be restored later
1603     local $pl->{_slots} = [$pl->ob->current_weapon, $pl->combat_ob, $pl->ranged_ob];
1604    
1605 root 1.143 $pl->save_pl ($path);
1606 root 1.346 cf::cede_to_tick;
1607 root 1.143 }
1608    
1609     sub new($) {
1610     my ($login) = @_;
1611    
1612     my $self = create;
1613    
1614     $self->ob->name ($login);
1615     $self->{deny_save} = 1;
1616    
1617     $cf::PLAYER{$login} = $self;
1618    
1619     $self
1620 root 1.23 }
1621    
1622 root 1.329 =item $player->send_msg ($channel, $msg, $color, [extra...])
1623    
1624     =cut
1625    
1626     sub send_msg {
1627     my $ns = shift->ns
1628     or return;
1629     $ns->send_msg (@_);
1630     }
1631    
1632 root 1.154 =item $pl->quit_character
1633    
1634     Nukes the player without looking back. If logged in, the connection will
1635     be destroyed. May block for a long time.
1636    
1637     =cut
1638    
1639 root 1.145 sub quit_character {
1640     my ($pl) = @_;
1641    
1642 root 1.220 my $name = $pl->ob->name;
1643    
1644 root 1.145 $pl->{deny_save} = 1;
1645 root 1.443 $pl->password ("*"); # this should lock out the player until we have nuked the dir
1646 root 1.145
1647     $pl->invoke (cf::EVENT_PLAYER_LOGOUT, 1) if $pl->active;
1648     $pl->deactivate;
1649 root 1.432 my $killer = cf::arch::get "killer_quit"; $pl->killer ($killer); $killer->destroy;
1650 root 1.145 $pl->invoke (cf::EVENT_PLAYER_QUIT);
1651     $pl->ns->destroy if $pl->ns;
1652    
1653     my $path = playerdir $pl;
1654     my $temp = "$path~$cf::RUNTIME~deleting~";
1655 root 1.154 aio_rename $path, $temp;
1656 root 1.150 delete $cf::PLAYER{$pl->ob->name};
1657     $pl->destroy;
1658 root 1.220
1659     my $prefix = qr<^~\Q$name\E/>;
1660    
1661     # nuke player maps
1662     $cf::MAP{$_}->nuke for grep /$prefix/, keys %cf::MAP;
1663    
1664 root 1.150 IO::AIO::aio_rmtree $temp;
1665 root 1.145 }
1666    
1667 pippijn 1.221 =item $pl->kick
1668    
1669     Kicks a player out of the game. This destroys the connection.
1670    
1671     =cut
1672    
1673     sub kick {
1674     my ($pl, $kicker) = @_;
1675    
1676     $pl->invoke (cf::EVENT_PLAYER_KICK, $kicker);
1677     $pl->killer ("kicked");
1678     $pl->ns->destroy;
1679     }
1680    
1681 root 1.154 =item cf::player::list_logins
1682    
1683     Returns am arrayref of all valid playernames in the system, can take a
1684     while and may block, so not sync_job-capable, ever.
1685    
1686     =cut
1687    
1688     sub list_logins {
1689 root 1.253 my $dirs = aio_readdir $PLAYERDIR
1690 root 1.154 or return [];
1691    
1692     my @logins;
1693    
1694     for my $login (@$dirs) {
1695 root 1.354 my $path = path $login;
1696    
1697     # a .pst is a dead give-away for a valid player
1698 root 1.427 # if no pst file found, open and chekc for blocked users
1699     if (aio_stat "$path.pst") {
1700 root 1.354 my $fh = aio_open $path, Fcntl::O_RDONLY, 0 or next;
1701     aio_read $fh, 0, 512, my $buf, 0 or next;
1702     $buf !~ /^password -------------$/m or next; # official not-valid tag
1703     }
1704 root 1.154
1705     utf8::decode $login;
1706     push @logins, $login;
1707     }
1708    
1709     \@logins
1710     }
1711    
1712     =item $player->maps
1713    
1714 root 1.523 =item cf::player::maps $login
1715    
1716 root 1.166 Returns an arrayref of map paths that are private for this
1717 root 1.154 player. May block.
1718    
1719     =cut
1720    
1721     sub maps($) {
1722     my ($pl) = @_;
1723    
1724 root 1.201 $pl = ref $pl ? $pl->ob->name : $pl;
1725    
1726 root 1.154 my $files = aio_readdir playerdir $pl
1727     or return;
1728    
1729     my @paths;
1730    
1731     for (@$files) {
1732     utf8::decode $_;
1733     next if /\.(?:pl|pst)$/;
1734 root 1.158 next unless /^$PATH_SEP/o;
1735 root 1.154
1736 root 1.201 push @paths, cf::map::normalise "~$pl/$_";
1737 root 1.154 }
1738    
1739     \@paths
1740     }
1741    
1742 root 1.447 =item $protocol_xml = $player->expand_cfpod ($cfpod)
1743 root 1.283
1744 root 1.447 Expand deliantra pod fragments into protocol xml.
1745 root 1.283
1746 root 1.316 =item $player->ext_reply ($msgid, @msg)
1747 root 1.95
1748     Sends an ext reply to the player.
1749    
1750     =cut
1751    
1752 root 1.316 sub ext_reply($$@) {
1753     my ($self, $id, @msg) = @_;
1754 root 1.95
1755 root 1.336 $self->ns->ext_reply ($id, @msg)
1756 root 1.95 }
1757    
1758 root 1.316 =item $player->ext_msg ($type, @msg)
1759 root 1.231
1760     Sends an ext event to the client.
1761    
1762     =cut
1763    
1764 root 1.316 sub ext_msg($$@) {
1765     my ($self, $type, @msg) = @_;
1766 root 1.231
1767 root 1.316 $self->ns->ext_msg ($type, @msg);
1768 root 1.231 }
1769    
1770 root 1.238 =head3 cf::region
1771    
1772     =over 4
1773    
1774     =cut
1775    
1776     package cf::region;
1777    
1778     =item cf::region::find_by_path $path
1779    
1780 root 1.281 Tries to decuce the likely region for a map knowing only its path.
1781 root 1.238
1782     =cut
1783    
1784     sub find_by_path($) {
1785     my ($path) = @_;
1786    
1787 root 1.523 $path =~ s/^~[^\/]*//; # skip ~login
1788    
1789 root 1.238 my ($match, $specificity);
1790    
1791     for my $region (list) {
1792 root 1.304 if ($region->{match} && $path =~ $region->{match}) {
1793 root 1.238 ($match, $specificity) = ($region, $region->specificity)
1794     if $region->specificity > $specificity;
1795     }
1796     }
1797    
1798     $match
1799     }
1800 root 1.143
1801 root 1.95 =back
1802    
1803 root 1.110 =head3 cf::map
1804    
1805     =over 4
1806    
1807     =cut
1808    
1809     package cf::map;
1810    
1811     use Fcntl;
1812     use Coro::AIO;
1813    
1814 root 1.166 use overload
1815 root 1.173 '""' => \&as_string,
1816     fallback => 1;
1817 root 1.166
1818 root 1.133 our $MAX_RESET = 3600;
1819     our $DEFAULT_RESET = 3000;
1820 root 1.110
1821     sub generate_random_map {
1822 root 1.166 my ($self, $rmp) = @_;
1823 root 1.418
1824     my $lock = cf::lock_acquire "generate_random_map"; # the random map generator is NOT reentrant ATM
1825    
1826 root 1.110 # mit "rum" bekleckern, nicht
1827 root 1.166 $self->_create_random_map (
1828 root 1.110 $rmp->{wallstyle}, $rmp->{wall_name}, $rmp->{floorstyle}, $rmp->{monsterstyle},
1829 root 1.508 $rmp->{treasurestyle}, $rmp->{layoutstyle}, $rmp->{doorstyle}, $rmp->{decorstyle}, $rmp->{miningstyle},
1830 root 1.110 $rmp->{origin_map}, $rmp->{final_map}, $rmp->{exitstyle}, $rmp->{this_map},
1831     $rmp->{exit_on_final_map},
1832     $rmp->{xsize}, $rmp->{ysize},
1833     $rmp->{expand2x}, $rmp->{layoutoptions1}, $rmp->{layoutoptions2}, $rmp->{layoutoptions3},
1834     $rmp->{symmetry}, $rmp->{difficulty}, $rmp->{difficulty_given}, $rmp->{difficulty_increase},
1835     $rmp->{dungeon_level}, $rmp->{dungeon_depth}, $rmp->{decoroptions}, $rmp->{orientation},
1836     $rmp->{origin_y}, $rmp->{origin_x}, $rmp->{random_seed}, $rmp->{total_map_hp},
1837     $rmp->{map_layout_style}, $rmp->{treasureoptions}, $rmp->{symmetry_used},
1838 root 1.162 (cf::region::find $rmp->{region}), $rmp->{custom}
1839     )
1840 root 1.110 }
1841    
1842 root 1.187 =item cf::map->register ($regex, $prio)
1843    
1844     Register a handler for the map path matching the given regex at the
1845     givne priority (higher is better, built-in handlers have priority 0, the
1846     default).
1847    
1848     =cut
1849    
1850 root 1.166 sub register {
1851 root 1.187 my (undef, $regex, $prio) = @_;
1852 root 1.166 my $pkg = caller;
1853    
1854     no strict;
1855     push @{"$pkg\::ISA"}, __PACKAGE__;
1856    
1857 root 1.187 $EXT_MAP{$pkg} = [$prio, qr<$regex>];
1858 root 1.166 }
1859    
1860     # also paths starting with '/'
1861 root 1.524 $EXT_MAP{"cf::map::wrap"} = [0, qr{^(?=/)}];
1862 root 1.166
1863 root 1.170 sub thawer_merge {
1864 root 1.172 my ($self, $merge) = @_;
1865    
1866 root 1.170 # we have to keep some variables in memory intact
1867 root 1.172 local $self->{path};
1868     local $self->{load_path};
1869 root 1.170
1870 root 1.172 $self->SUPER::thawer_merge ($merge);
1871 root 1.170 }
1872    
1873 root 1.166 sub normalise {
1874     my ($path, $base) = @_;
1875    
1876 root 1.192 $path = "$path"; # make sure its a string
1877    
1878 root 1.199 $path =~ s/\.map$//;
1879    
1880 root 1.166 # map plan:
1881     #
1882     # /! non-realised random map exit (special hack!)
1883     # {... are special paths that are not being touched
1884     # ?xxx/... are special absolute paths
1885     # ?random/... random maps
1886     # /... normal maps
1887     # ~user/... per-player map of a specific user
1888    
1889     $path =~ s/$PATH_SEP/\//go;
1890    
1891     # treat it as relative path if it starts with
1892     # something that looks reasonable
1893     if ($path =~ m{^(?:\./|\.\./|\w)}) {
1894     $base or Carp::carp "normalise called with relative path and no base: '$path'";
1895    
1896     $base =~ s{[^/]+/?$}{};
1897     $path = "$base/$path";
1898     }
1899    
1900     for ($path) {
1901     redo if s{//}{/};
1902     redo if s{/\.?/}{/};
1903     redo if s{/[^/]+/\.\./}{/};
1904     }
1905    
1906     $path
1907     }
1908    
1909     sub new_from_path {
1910     my (undef, $path, $base) = @_;
1911    
1912     return $path if UNIVERSAL::isa $path, "cf::map"; # already a map object
1913    
1914     $path = normalise $path, $base;
1915    
1916 root 1.187 for my $pkg (sort { $EXT_MAP{$b}[0] <=> $EXT_MAP{$a}[0] } keys %EXT_MAP) {
1917     if ($path =~ $EXT_MAP{$pkg}[1]) {
1918 root 1.166 my $self = bless cf::map::new, $pkg;
1919     $self->{path} = $path; $self->path ($path);
1920     $self->init; # pass $1 etc.
1921     return $self;
1922     }
1923     }
1924    
1925 root 1.308 Carp::cluck "unable to resolve path '$path' (base '$base').";
1926 root 1.166 ()
1927     }
1928    
1929     sub init {
1930     my ($self) = @_;
1931    
1932     $self
1933     }
1934    
1935     sub as_string {
1936     my ($self) = @_;
1937    
1938     "$self->{path}"
1939     }
1940    
1941     # the displayed name, this is a one way mapping
1942     sub visible_name {
1943     &as_string
1944     }
1945    
1946     # the original (read-only) location
1947     sub load_path {
1948     my ($self) = @_;
1949    
1950 root 1.254 "$MAPDIR/$self->{path}.map"
1951 root 1.166 }
1952    
1953     # the temporary/swap location
1954     sub save_path {
1955     my ($self) = @_;
1956    
1957 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
1958 root 1.254 "$TMPDIR/$path.map"
1959 root 1.166 }
1960    
1961     # the unique path, undef == no special unique path
1962     sub uniq_path {
1963     my ($self) = @_;
1964    
1965 root 1.419 (my $path = $_[0]{path}) =~ s/\//$PATH_SEP/go;
1966 root 1.253 "$UNIQUEDIR/$path"
1967 root 1.166 }
1968    
1969 root 1.275 sub decay_objects {
1970     my ($self) = @_;
1971    
1972     return if $self->{deny_reset};
1973    
1974     $self->do_decay_objects;
1975     }
1976    
1977 root 1.166 sub unlink_save {
1978     my ($self) = @_;
1979    
1980     utf8::encode (my $save = $self->save_path);
1981 root 1.170 IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink $save;
1982     IO::AIO::aioreq_pri 4; Coro::AIO::aio_unlink "$save.pst";
1983 root 1.166 }
1984    
1985     sub load_header_from($) {
1986     my ($self, $path) = @_;
1987 root 1.110
1988     utf8::encode $path;
1989 root 1.356 my $f = new_from_file cf::object::thawer $path
1990     or return;
1991 root 1.110
1992 root 1.356 $self->_load_header ($f)
1993 root 1.110 or return;
1994    
1995 root 1.356 local $MAP_LOADING{$self->{path}} = $self;
1996     $f->resolve_delayed_derefs;
1997    
1998 root 1.166 $self->{load_path} = $path;
1999 root 1.135
2000 root 1.166 1
2001     }
2002 root 1.110
2003 root 1.188 sub load_header_orig {
2004 root 1.166 my ($self) = @_;
2005 root 1.110
2006 root 1.166 $self->load_header_from ($self->load_path)
2007 root 1.110 }
2008    
2009 root 1.188 sub load_header_temp {
2010 root 1.166 my ($self) = @_;
2011 root 1.110
2012 root 1.166 $self->load_header_from ($self->save_path)
2013     }
2014 root 1.110
2015 root 1.188 sub prepare_temp {
2016     my ($self) = @_;
2017    
2018     $self->last_access ((delete $self->{last_access})
2019     || $cf::RUNTIME); #d#
2020     # safety
2021     $self->{instantiate_time} = $cf::RUNTIME
2022     if $self->{instantiate_time} > $cf::RUNTIME;
2023     }
2024    
2025     sub prepare_orig {
2026     my ($self) = @_;
2027    
2028     $self->{load_original} = 1;
2029     $self->{instantiate_time} = $cf::RUNTIME;
2030     $self->last_access ($cf::RUNTIME);
2031     $self->instantiate;
2032     }
2033    
2034 root 1.166 sub load_header {
2035     my ($self) = @_;
2036 root 1.110
2037 root 1.188 if ($self->load_header_temp) {
2038     $self->prepare_temp;
2039 root 1.166 } else {
2040 root 1.188 $self->load_header_orig
2041 root 1.166 or return;
2042 root 1.188 $self->prepare_orig;
2043 root 1.166 }
2044 root 1.120
2045 root 1.275 $self->{deny_reset} = 1
2046     if $self->no_reset;
2047    
2048 root 1.238 $self->default_region (cf::region::find_by_path $self->{path})
2049     unless $self->default_region;
2050    
2051 root 1.166 1
2052     }
2053 root 1.110
2054 root 1.166 sub find;
2055     sub find {
2056     my ($path, $origin) = @_;
2057 root 1.134
2058 root 1.166 $path = normalise $path, $origin && $origin->path;
2059 root 1.110
2060 root 1.459 my $guard1 = cf::lock_acquire "map_data:$path";#d#remove
2061     my $guard2 = cf::lock_acquire "map_find:$path";
2062 root 1.110
2063 root 1.166 $cf::MAP{$path} || do {
2064     my $map = new_from_path cf::map $path
2065     or return;
2066 root 1.110
2067 root 1.116 $map->{last_save} = $cf::RUNTIME;
2068 root 1.110
2069 root 1.166 $map->load_header
2070     or return;
2071 root 1.134
2072 root 1.275 if ($map->should_reset) {#d#TODO# disabled, crashy (locking issue?)
2073 root 1.185 # doing this can freeze the server in a sync job, obviously
2074     #$cf::WAIT_FOR_TICK->wait;
2075 root 1.429 undef $guard2;
2076 root 1.358 undef $guard1;
2077 root 1.112 $map->reset;
2078 root 1.192 return find $path;
2079 root 1.112 }
2080 root 1.110
2081 root 1.166 $cf::MAP{$path} = $map
2082 root 1.110 }
2083     }
2084    
2085 root 1.500 sub pre_load { }
2086     #sub post_load { } # XS
2087 root 1.188
2088 root 1.110 sub load {
2089     my ($self) = @_;
2090    
2091 root 1.196 local $self->{deny_reset} = 1; # loading can take a long time
2092    
2093 root 1.120 my $path = $self->{path};
2094    
2095 root 1.256 {
2096 root 1.357 my $guard = cf::lock_acquire "map_data:$path";
2097 root 1.256
2098 root 1.357 return unless $self->valid;
2099 root 1.360 return unless $self->in_memory == cf::MAP_SWAPPED;
2100 root 1.110
2101 root 1.256 $self->in_memory (cf::MAP_LOADING);
2102 root 1.110
2103 root 1.256 $self->alloc;
2104 root 1.188
2105 root 1.256 $self->pre_load;
2106 root 1.346 cf::cede_to_tick;
2107 root 1.188
2108 root 1.356 my $f = new_from_file cf::object::thawer $self->{load_path};
2109     $f->skip_block;
2110     $self->_load_objects ($f)
2111 root 1.256 or return;
2112 root 1.110
2113 root 1.439 $self->post_load_original
2114 root 1.256 if delete $self->{load_original};
2115 root 1.111
2116 root 1.256 if (my $uniq = $self->uniq_path) {
2117     utf8::encode $uniq;
2118 root 1.356 unless (aio_stat $uniq) {
2119     if (my $f = new_from_file cf::object::thawer $uniq) {
2120     $self->clear_unique_items;
2121     $self->_load_objects ($f);
2122     $f->resolve_delayed_derefs;
2123     }
2124 root 1.256 }
2125 root 1.110 }
2126    
2127 root 1.356 $f->resolve_delayed_derefs;
2128    
2129 root 1.346 cf::cede_to_tick;
2130 root 1.256 # now do the right thing for maps
2131     $self->link_multipart_objects;
2132 root 1.110 $self->difficulty ($self->estimate_difficulty)
2133     unless $self->difficulty;
2134 root 1.346 cf::cede_to_tick;
2135 root 1.256
2136     unless ($self->{deny_activate}) {
2137     $self->decay_objects;
2138     $self->fix_auto_apply;
2139     $self->update_buttons;
2140 root 1.346 cf::cede_to_tick;
2141 root 1.256 $self->activate;
2142     }
2143    
2144 root 1.325 $self->{last_save} = $cf::RUNTIME;
2145     $self->last_access ($cf::RUNTIME);
2146 root 1.324
2147 root 1.420 $self->in_memory (cf::MAP_ACTIVE);
2148 root 1.110 }
2149    
2150 root 1.188 $self->post_load;
2151 root 1.166 }
2152    
2153 root 1.507 # customize the map for a given player, i.e.
2154     # return the _real_ map. used by e.g. per-player
2155     # maps to change the path to ~playername/mappath
2156 root 1.166 sub customise_for {
2157     my ($self, $ob) = @_;
2158    
2159     return find "~" . $ob->name . "/" . $self->{path}
2160     if $self->per_player;
2161 root 1.134
2162 root 1.275 # return find "?party/" . $ob->name . "/" . $self->{path}
2163     # if $self->per_party;
2164    
2165 root 1.166 $self
2166 root 1.110 }
2167    
2168 root 1.157 # find and load all maps in the 3x3 area around a map
2169 root 1.333 sub load_neighbours {
2170 root 1.157 my ($map) = @_;
2171    
2172 root 1.333 my @neigh; # diagonal neighbours
2173 root 1.157
2174     for (0 .. 3) {
2175     my $neigh = $map->tile_path ($_)
2176     or next;
2177     $neigh = find $neigh, $map
2178     or next;
2179     $neigh->load;
2180    
2181 root 1.527 # now find the diagonal neighbours
2182 root 1.333 push @neigh,
2183     [$neigh->tile_path (($_ + 3) % 4), $neigh],
2184     [$neigh->tile_path (($_ + 1) % 4), $neigh];
2185 root 1.157 }
2186    
2187 root 1.333 for (grep defined $_->[0], @neigh) {
2188     my ($path, $origin) = @$_;
2189     my $neigh = find $path, $origin
2190 root 1.157 or next;
2191     $neigh->load;
2192     }
2193     }
2194    
2195 root 1.133 sub find_sync {
2196 root 1.110 my ($path, $origin) = @_;
2197    
2198 root 1.534 return cf::LOG cf::llevError | cf::logBacktrace, "do_find_sync"
2199     if $Coro::current == $Coro::main;
2200    
2201     find $path, $origin
2202 root 1.133 }
2203    
2204     sub do_load_sync {
2205     my ($map) = @_;
2206 root 1.110
2207 root 1.534 return cf::LOG cf::llevError | cf::logBacktrace, "do_load_sync"
2208 root 1.342 if $Coro::current == $Coro::main;
2209 root 1.339
2210 root 1.534 $map->load;
2211 root 1.110 }
2212    
2213 root 1.157 our %MAP_PREFETCH;
2214 root 1.183 our $MAP_PREFETCHER = undef;
2215 root 1.157
2216     sub find_async {
2217 root 1.339 my ($path, $origin, $load) = @_;
2218 root 1.157
2219 root 1.166 $path = normalise $path, $origin && $origin->{path};
2220 root 1.157
2221 root 1.166 if (my $map = $cf::MAP{$path}) {
2222 root 1.420 return $map if !$load || $map->in_memory == cf::MAP_ACTIVE;
2223 root 1.157 }
2224    
2225 root 1.339 $MAP_PREFETCH{$path} |= $load;
2226    
2227 root 1.183 $MAP_PREFETCHER ||= cf::async {
2228 root 1.374 $Coro::current->{desc} = "map prefetcher";
2229    
2230 root 1.183 while (%MAP_PREFETCH) {
2231 root 1.339 while (my ($k, $v) = each %MAP_PREFETCH) {
2232     if (my $map = find $k) {
2233     $map->load if $v;
2234 root 1.308 }
2235 root 1.183
2236 root 1.339 delete $MAP_PREFETCH{$k};
2237 root 1.183 }
2238     }
2239     undef $MAP_PREFETCHER;
2240     };
2241 root 1.189 $MAP_PREFETCHER->prio (6);
2242 root 1.157
2243     ()
2244     }
2245    
2246 root 1.518 # common code, used by both ->save and ->swapout
2247     sub _save {
2248 root 1.110 my ($self) = @_;
2249    
2250     $self->{last_save} = $cf::RUNTIME;
2251    
2252     return unless $self->dirty;
2253    
2254 root 1.166 my $save = $self->save_path; utf8::encode $save;
2255     my $uniq = $self->uniq_path; utf8::encode $uniq;
2256 root 1.117
2257 root 1.110 $self->{load_path} = $save;
2258    
2259     return if $self->{deny_save};
2260    
2261 root 1.132 local $self->{last_access} = $self->last_access;#d#
2262    
2263 root 1.143 cf::async {
2264 root 1.374 $Coro::current->{desc} = "map player save";
2265 root 1.143 $_->contr->save for $self->players;
2266     };
2267    
2268 root 1.420 cf::get_slot 0.02;
2269    
2270 root 1.110 if ($uniq) {
2271 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS);
2272     $self->_save_objects ($uniq, cf::IO_UNIQUES);
2273 root 1.110 } else {
2274 root 1.166 $self->_save_objects ($save, cf::IO_HEADER | cf::IO_OBJECTS | cf::IO_UNIQUES);
2275 root 1.110 }
2276     }
2277    
2278 root 1.518 sub save {
2279     my ($self) = @_;
2280    
2281     my $lock = cf::lock_acquire "map_data:$self->{path}";
2282    
2283     $self->_save;
2284     }
2285    
2286 root 1.110 sub swap_out {
2287     my ($self) = @_;
2288    
2289 root 1.345 my $lock = cf::lock_acquire "map_data:$self->{path}";
2290 root 1.137
2291 root 1.420 return if $self->in_memory != cf::MAP_ACTIVE;
2292 root 1.110 return if $self->{deny_save};
2293 root 1.518 return if $self->players;
2294 root 1.110
2295 root 1.518 # first deactivate the map and "unlink" it from the core
2296     $self->deactivate;
2297     $_->clear_links_to ($self) for values %cf::MAP;
2298 root 1.359 $self->in_memory (cf::MAP_SWAPPED);
2299    
2300 root 1.518 # then atomically save
2301     $self->_save;
2302    
2303     # then free the map
2304 root 1.110 $self->clear;
2305     }
2306    
2307 root 1.112 sub reset_at {
2308     my ($self) = @_;
2309 root 1.110
2310     # TODO: safety, remove and allow resettable per-player maps
2311 root 1.114 return 1e99 if $self->{deny_reset};
2312 root 1.110
2313 root 1.112 my $time = $self->fixed_resettime ? $self->{instantiate_time} : $self->last_access;
2314 root 1.114 my $to = List::Util::min $MAX_RESET, $self->reset_timeout || $DEFAULT_RESET;
2315 root 1.110
2316 root 1.112 $time + $to
2317     }
2318    
2319     sub should_reset {
2320     my ($self) = @_;
2321    
2322     $self->reset_at <= $cf::RUNTIME
2323 root 1.111 }
2324    
2325 root 1.110 sub reset {
2326     my ($self) = @_;
2327    
2328 root 1.167 my $lock = cf::lock_acquire "map_data:$self->{path}";
2329 root 1.137
2330 root 1.110 return if $self->players;
2331    
2332 root 1.532 cf::trace "resetting map ", $self->path, "\n";
2333 root 1.110
2334 root 1.210 $self->in_memory (cf::MAP_SWAPPED);
2335    
2336     # need to save uniques path
2337     unless ($self->{deny_save}) {
2338     my $uniq = $self->uniq_path; utf8::encode $uniq;
2339    
2340     $self->_save_objects ($uniq, cf::IO_UNIQUES)
2341     if $uniq;
2342     }
2343    
2344 root 1.111 delete $cf::MAP{$self->path};
2345 root 1.110
2346 root 1.358 $self->deactivate;
2347 root 1.359 $_->clear_links_to ($self) for values %cf::MAP;
2348 root 1.167 $self->clear;
2349    
2350 root 1.166 $self->unlink_save;
2351 root 1.111 $self->destroy;
2352 root 1.110 }
2353    
2354 root 1.114 my $nuke_counter = "aaaa";
2355    
2356     sub nuke {
2357     my ($self) = @_;
2358    
2359 root 1.349 {
2360     my $lock = cf::lock_acquire "map_data:$self->{path}";
2361    
2362     delete $cf::MAP{$self->path};
2363 root 1.174
2364 root 1.351 $self->unlink_save;
2365    
2366 root 1.524 bless $self, "cf::map::wrap";
2367 root 1.349 delete $self->{deny_reset};
2368     $self->{deny_save} = 1;
2369     $self->reset_timeout (1);
2370     $self->path ($self->{path} = "{nuke}/" . ($nuke_counter++));
2371 root 1.174
2372 root 1.349 $cf::MAP{$self->path} = $self;
2373     }
2374 root 1.174
2375 root 1.114 $self->reset; # polite request, might not happen
2376     }
2377    
2378 root 1.276 =item $maps = cf::map::tmp_maps
2379    
2380     Returns an arrayref with all map paths of currently instantiated and saved
2381 root 1.277 maps. May block.
2382 root 1.276
2383     =cut
2384    
2385     sub tmp_maps() {
2386     [
2387     map {
2388     utf8::decode $_;
2389 root 1.277 /\.map$/
2390 root 1.276 ? normalise $_
2391     : ()
2392     } @{ aio_readdir $TMPDIR or [] }
2393     ]
2394     }
2395    
2396 root 1.277 =item $maps = cf::map::random_maps
2397    
2398     Returns an arrayref with all map paths of currently instantiated and saved
2399     random maps. May block.
2400    
2401     =cut
2402    
2403     sub random_maps() {
2404     [
2405     map {
2406     utf8::decode $_;
2407     /\.map$/
2408     ? normalise "?random/$_"
2409     : ()
2410     } @{ aio_readdir $RANDOMDIR or [] }
2411     ]
2412     }
2413    
2414 root 1.158 =item cf::map::unique_maps
2415    
2416 root 1.166 Returns an arrayref of paths of all shared maps that have
2417 root 1.158 instantiated unique items. May block.
2418    
2419     =cut
2420    
2421     sub unique_maps() {
2422 root 1.276 [
2423     map {
2424     utf8::decode $_;
2425 root 1.419 s/\.map$//; # TODO future compatibility hack
2426     /\.pst$/ || !/^$PATH_SEP/o # TODO unique maps apparebntly lack the .map suffix :/
2427     ? ()
2428     : normalise $_
2429 root 1.276 } @{ aio_readdir $UNIQUEDIR or [] }
2430     ]
2431 root 1.158 }
2432    
2433 root 1.489 =item cf::map::static_maps
2434    
2435     Returns an arrayref if paths of all static maps (all preinstalled F<.map>
2436 root 1.491 file in the shared directory excluding F</styles> and F</editor>). May
2437     block.
2438 root 1.489
2439     =cut
2440    
2441     sub static_maps() {
2442     my @dirs = "";
2443     my @maps;
2444    
2445     while (@dirs) {
2446     my $dir = shift @dirs;
2447    
2448 root 1.491 next if $dir eq "/styles" || $dir eq "/editor";
2449 root 1.490
2450 root 1.489 my ($dirs, $files) = Coro::AIO::aio_scandir "$MAPDIR$dir", 2
2451     or return;
2452    
2453     for (@$files) {
2454     s/\.map$// or next;
2455     utf8::decode $_;
2456     push @maps, "$dir/$_";
2457     }
2458    
2459     push @dirs, map "$dir/$_", @$dirs;
2460     }
2461    
2462     \@maps
2463     }
2464    
2465 root 1.155 =back
2466    
2467     =head3 cf::object
2468    
2469     =cut
2470    
2471     package cf::object;
2472    
2473     =over 4
2474    
2475     =item $ob->inv_recursive
2476 root 1.110
2477 root 1.419 Returns the inventory of the object I<and> their inventories, recursively,
2478     but I<not> the object itself.
2479 root 1.110
2480 root 1.155 =cut
2481 root 1.144
2482 root 1.155 sub inv_recursive_;
2483     sub inv_recursive_ {
2484     map { $_, inv_recursive_ $_->inv } @_
2485     }
2486 root 1.110
2487 root 1.155 sub inv_recursive {
2488     inv_recursive_ inv $_[0]
2489 root 1.110 }
2490    
2491 root 1.356 =item $ref = $ob->ref
2492    
2493 root 1.419 Creates and returns a persistent reference to an object that can be stored as a string.
2494 root 1.356
2495     =item $ob = cf::object::deref ($refstring)
2496    
2497     returns the objetc referenced by refstring. may return undef when it cnanot find the object,
2498     even if the object actually exists. May block.
2499    
2500     =cut
2501    
2502     sub deref {
2503     my ($ref) = @_;
2504    
2505 root 1.377 if ($ref =~ m{^player\/(<1\.[0-9a-f]+>)/(.*)$}) {
2506 root 1.356 my ($uuid, $name) = ($1, $2);
2507     my $pl = $cf::PLAYER_LOADING{$name} || cf::player::find $name
2508     or return;
2509     $pl->ob->uuid eq $uuid
2510     or return;
2511    
2512     $pl->ob
2513     } else {
2514     warn "$ref: cannot resolve object reference\n";
2515     undef
2516     }
2517     }
2518    
2519 root 1.110 package cf;
2520    
2521     =back
2522    
2523 root 1.95 =head3 cf::object::player
2524    
2525     =over 4
2526    
2527 root 1.79 =item $player_object->reply ($npc, $msg[, $flags])
2528 root 1.28
2529     Sends a message to the player, as if the npc C<$npc> replied. C<$npc>
2530     can be C<undef>. Does the right thing when the player is currently in a
2531     dialogue with the given NPC character.
2532    
2533     =cut
2534    
2535 root 1.428 our $SAY_CHANNEL = {
2536     id => "say",
2537     title => "Map",
2538     reply => "say ",
2539 root 1.468 tooltip => "Things said to and replied from NPCs near you and other players on the same map only.",
2540 root 1.428 };
2541    
2542     our $CHAT_CHANNEL = {
2543     id => "chat",
2544     title => "Chat",
2545     reply => "chat ",
2546     tooltip => "Player chat and shouts, global to the server.",
2547     };
2548    
2549 root 1.22 # rough implementation of a future "reply" method that works
2550     # with dialog boxes.
2551 root 1.95 #TODO: the first argument must go, split into a $npc->reply_to ( method
2552 root 1.23 sub cf::object::player::reply($$$;$) {
2553     my ($self, $npc, $msg, $flags) = @_;
2554    
2555     $flags = cf::NDI_BROWN | cf::NDI_UNIQUE unless @_ >= 4;
2556 root 1.22
2557 root 1.24 if ($self->{record_replies}) {
2558     push @{ $self->{record_replies} }, [$npc, $msg, $flags];
2559 elmex 1.282
2560 root 1.24 } else {
2561 elmex 1.282 my $pl = $self->contr;
2562    
2563     if ($pl->{npc_dialog} && $pl->{npc_dialog}->{id}) {
2564 root 1.316 my $dialog = $pl->{npc_dialog};
2565     $dialog->{pl}->ext_msg ($dialog->{id}, update => msg => $dialog->{pl}->expand_cfpod ($msg));
2566 elmex 1.282
2567     } else {
2568     $msg = $npc->name . " says: $msg" if $npc;
2569 root 1.428 $self->send_msg ($SAY_CHANNEL => $msg, $flags);
2570 elmex 1.282 }
2571 root 1.24 }
2572 root 1.22 }
2573    
2574 root 1.329 =item $object->send_msg ($channel, $msg, $color, [extra...])
2575    
2576     =cut
2577    
2578     sub cf::object::send_msg {
2579     my $pl = shift->contr
2580     or return;
2581     $pl->send_msg (@_);
2582     }
2583    
2584 root 1.79 =item $player_object->may ("access")
2585    
2586     Returns wether the given player is authorized to access resource "access"
2587     (e.g. "command_wizcast").
2588    
2589     =cut
2590    
2591     sub cf::object::player::may {
2592     my ($self, $access) = @_;
2593    
2594     $self->flag (cf::FLAG_WIZ) ||
2595     (ref $cf::CFG{"may_$access"}
2596     ? scalar grep $self->name eq $_, @{$cf::CFG{"may_$access"}}
2597     : $cf::CFG{"may_$access"})
2598     }
2599 root 1.70
2600 root 1.115 =item $player_object->enter_link
2601    
2602     Freezes the player and moves him/her to a special map (C<{link}>).
2603    
2604 root 1.446 The player should be reasonably safe there for short amounts of time (e.g.
2605     for loading a map). You I<MUST> call C<leave_link> as soon as possible,
2606 root 1.527 though, as the player cannot control the character while it is on the link
2607 root 1.446 map.
2608 root 1.115
2609 root 1.166 Will never block.
2610    
2611 root 1.115 =item $player_object->leave_link ($map, $x, $y)
2612    
2613 root 1.166 Moves the player out of the special C<{link}> map onto the specified
2614     map. If the map is not valid (or omitted), the player will be moved back
2615     to the location he/she was before the call to C<enter_link>, or, if that
2616     fails, to the emergency map position.
2617 root 1.115
2618     Might block.
2619    
2620     =cut
2621    
2622 root 1.166 sub link_map {
2623     unless ($LINK_MAP) {
2624     $LINK_MAP = cf::map::find "{link}"
2625 root 1.196 or cf::cleanup "FATAL: unable to provide {link} map, exiting.";
2626 root 1.166 $LINK_MAP->load;
2627     }
2628    
2629     $LINK_MAP
2630     }
2631    
2632 root 1.110 sub cf::object::player::enter_link {
2633     my ($self) = @_;
2634    
2635 root 1.259 $self->deactivate_recursive;
2636 root 1.258
2637 root 1.527 ++$self->{_link_recursion};
2638    
2639 root 1.166 return if UNIVERSAL::isa $self->map, "ext::map_link";
2640 root 1.110
2641 root 1.120 $self->{_link_pos} ||= [$self->map->{path}, $self->x, $self->y]
2642 root 1.270 if $self->map && $self->map->{path} ne "{link}";
2643 root 1.110
2644 root 1.519 $self->enter_map ($LINK_MAP || link_map, 3, 3);
2645 root 1.110 }
2646    
2647     sub cf::object::player::leave_link {
2648     my ($self, $map, $x, $y) = @_;
2649    
2650 root 1.270 return unless $self->contr->active;
2651    
2652 root 1.110 my $link_pos = delete $self->{_link_pos};
2653    
2654     unless ($map) {
2655     # restore original map position
2656     ($map, $x, $y) = @{ $link_pos || [] };
2657 root 1.133 $map = cf::map::find $map;
2658 root 1.110
2659     unless ($map) {
2660     ($map, $x, $y) = @$EMERGENCY_POSITION;
2661 root 1.133 $map = cf::map::find $map
2662 root 1.110 or die "FATAL: cannot load emergency map\n";
2663     }
2664     }
2665    
2666     ($x, $y) = (-1, -1)
2667     unless (defined $x) && (defined $y);
2668    
2669     # use -1 or undef as default coordinates, not 0, 0
2670     ($x, $y) = ($map->enter_x, $map->enter_y)
2671 root 1.492 if $x <= 0 && $y <= 0;
2672 root 1.110
2673     $map->load;
2674 root 1.333 $map->load_neighbours;
2675 root 1.110
2676 root 1.143 return unless $self->contr->active;
2677 root 1.215
2678     local $self->{_prev_pos} = $link_pos; # ugly hack for rent.ext
2679 root 1.527 if ($self->enter_map ($map, $x, $y)) {
2680     # entering was successful
2681     delete $self->{_link_recursion};
2682     # only activate afterwards, to support waiting in hooks
2683     $self->activate_recursive;
2684     }
2685 root 1.476
2686 root 1.110 }
2687    
2688 root 1.527 =item $player_object->goto ($path, $x, $y[, $check->($map, $x, $y, $player)[, $done->($player)]])
2689 root 1.268
2690     Moves the player to the given map-path and coordinates by first freezing
2691     her, loading and preparing them map, calling the provided $check callback
2692     that has to return the map if sucecssful, and then unfreezes the player on
2693 root 1.306 the new (success) or old (failed) map position. In either case, $done will
2694     be called at the end of this process.
2695 root 1.110
2696 root 1.436 Note that $check will be called with a potentially non-loaded map, so if
2697     it needs a loaded map it has to call C<< ->load >>.
2698    
2699 root 1.110 =cut
2700    
2701 root 1.270 our $GOTOGEN;
2702    
2703 root 1.136 sub cf::object::player::goto {
2704 root 1.306 my ($self, $path, $x, $y, $check, $done) = @_;
2705 root 1.268
2706 root 1.527 if ($self->{_link_recursion} >= $MAX_LINKS) {
2707 root 1.532 error "FATAL: link recursion exceeded, ", $self->name, " goto $path $x $y, redirecting.";
2708 root 1.527 $self->failmsg ("Something went wrong inside the server - please contact an administrator!");
2709     ($path, $x, $y) = @$EMERGENCY_POSITION;
2710     }
2711    
2712 root 1.270 # do generation counting so two concurrent goto's will be executed in-order
2713     my $gen = $self->{_goto_generation} = ++$GOTOGEN;
2714    
2715 root 1.110 $self->enter_link;
2716    
2717 root 1.140 (async {
2718 root 1.374 $Coro::current->{desc} = "player::goto $path $x $y";
2719    
2720 root 1.365 # *tag paths override both path and x|y
2721     if ($path =~ /^\*(.*)$/) {
2722     if (my @obs = grep $_->map, ext::map_tags::find $1) {
2723     my $ob = $obs[rand @obs];
2724 root 1.366
2725 root 1.367 # see if we actually can go there
2726 root 1.368 if (@obs = grep !$self->blocked ($_->map, $_->x, $_->y), $ob, $ob->tail) {
2727     $ob = $obs[rand @obs];
2728 root 1.369 } else {
2729     $self->message ("Wow, it's pretty crowded in there.", cf::NDI_UNIQUE | cf::NDI_RED);
2730 root 1.368 }
2731 root 1.369 # else put us there anyways for now #d#
2732 root 1.366
2733 root 1.365 ($path, $x, $y) = ($ob->map, $ob->x, $ob->y);
2734 root 1.369 } else {
2735     ($path, $x, $y) = (undef, undef, undef);
2736 root 1.365 }
2737     }
2738    
2739 root 1.197 my $map = eval {
2740 root 1.369 my $map = defined $path ? cf::map::find $path : undef;
2741 root 1.268
2742     if ($map) {
2743     $map = $map->customise_for ($self);
2744 root 1.527 $map = $check->($map, $x, $y, $self) if $check && $map;
2745 root 1.268 } else {
2746 root 1.369 $self->message ("The exit to '$path' is closed.", cf::NDI_UNIQUE | cf::NDI_RED);
2747 root 1.268 }
2748    
2749 root 1.197 $map
2750 root 1.268 };
2751    
2752     if ($@) {
2753     $self->message ("Something went wrong within the server, please report this incident!", cf::NDI_UNIQUE | cf::NDI_RED);
2754     LOG llevError | logBacktrace, Carp::longmess $@;
2755     }
2756 root 1.115
2757 root 1.270 if ($gen == $self->{_goto_generation}) {
2758     delete $self->{_goto_generation};
2759     $self->leave_link ($map, $x, $y);
2760     }
2761 root 1.306
2762 root 1.527 $done->($self) if $done;
2763 root 1.110 })->prio (1);
2764     }
2765    
2766     =item $player_object->enter_exit ($exit_object)
2767    
2768     =cut
2769    
2770     sub parse_random_map_params {
2771     my ($spec) = @_;
2772    
2773     my $rmp = { # defaults
2774 root 1.181 xsize => (cf::rndm 15, 40),
2775     ysize => (cf::rndm 15, 40),
2776     symmetry => (cf::rndm 1, cf::SYMMETRY_XY),
2777 root 1.182 #layout => string,
2778 root 1.110 };
2779    
2780     for (split /\n/, $spec) {
2781     my ($k, $v) = split /\s+/, $_, 2;
2782    
2783     $rmp->{lc $k} = $v if (length $k) && (length $v);
2784     }
2785    
2786     $rmp
2787     }
2788    
2789     sub prepare_random_map {
2790     my ($exit) = @_;
2791    
2792     # all this does is basically replace the /! path by
2793     # a new random map path (?random/...) with a seed
2794     # that depends on the exit object
2795    
2796     my $rmp = parse_random_map_params $exit->msg;
2797    
2798     if ($exit->map) {
2799 root 1.198 $rmp->{region} = $exit->region->name;
2800 root 1.110 $rmp->{origin_map} = $exit->map->path;
2801     $rmp->{origin_x} = $exit->x;
2802     $rmp->{origin_y} = $exit->y;
2803 root 1.430
2804     $exit->map->touch;
2805 root 1.110 }
2806    
2807     $rmp->{random_seed} ||= $exit->random_seed;
2808    
2809 root 1.422 my $data = JSON::XS->new->utf8->pretty->canonical->encode ($rmp);
2810 root 1.110 my $md5 = Digest::MD5::md5_hex $data;
2811 root 1.253 my $meta = "$RANDOMDIR/$md5.meta";
2812 root 1.110
2813 root 1.177 if (my $fh = aio_open "$meta~", O_WRONLY | O_CREAT, 0666) {
2814 root 1.110 aio_write $fh, 0, (length $data), $data, 0;
2815 root 1.177 undef $fh;
2816     aio_rename "$meta~", $meta;
2817 root 1.110
2818 root 1.430 my $slaying = "?random/$md5";
2819    
2820     if ($exit->valid) {
2821     $exit->slaying ("?random/$md5");
2822     $exit->msg (undef);
2823     }
2824 root 1.110 }
2825     }
2826    
2827     sub cf::object::player::enter_exit {
2828     my ($self, $exit) = @_;
2829    
2830     return unless $self->type == cf::PLAYER;
2831    
2832 root 1.430 $self->enter_link;
2833    
2834     (async {
2835     $Coro::current->{desc} = "enter_exit";
2836    
2837     unless (eval {
2838     $self->deactivate_recursive; # just to be sure
2839 root 1.195
2840 root 1.430 # random map handling
2841     {
2842     my $guard = cf::lock_acquire "exit_prepare:$exit";
2843 root 1.195
2844 root 1.430 prepare_random_map $exit
2845     if $exit->slaying eq "/!";
2846     }
2847 root 1.110
2848 root 1.430 my $map = cf::map::normalise $exit->slaying, $exit->map && $exit->map->path;
2849     my $x = $exit->stats->hp;
2850     my $y = $exit->stats->sp;
2851 root 1.296
2852 root 1.430 $self->goto ($map, $x, $y);
2853 root 1.374
2854 root 1.430 # if exit is damned, update players death & WoR home-position
2855     $self->contr->savebed ($map, $x, $y)
2856     if $exit->flag (cf::FLAG_DAMNED);
2857 root 1.110
2858 root 1.430 1
2859 root 1.110 }) {
2860 root 1.447 $self->message ("Something went wrong deep within the deliantra server. "
2861 root 1.233 . "I'll try to bring you back to the map you were before. "
2862     . "Please report this to the dungeon master!",
2863     cf::NDI_UNIQUE | cf::NDI_RED);
2864 root 1.110
2865 root 1.532 error "ERROR in enter_exit: $@";
2866 root 1.110 $self->leave_link;
2867     }
2868     })->prio (1);
2869     }
2870    
2871 root 1.95 =head3 cf::client
2872    
2873     =over 4
2874    
2875     =item $client->send_drawinfo ($text, $flags)
2876    
2877     Sends a drawinfo packet to the client. Circumvents output buffering so
2878     should not be used under normal circumstances.
2879    
2880 root 1.70 =cut
2881    
2882 root 1.95 sub cf::client::send_drawinfo {
2883     my ($self, $text, $flags) = @_;
2884    
2885     utf8::encode $text;
2886 root 1.225 $self->send_packet (sprintf "drawinfo %d %s", $flags || cf::NDI_BLACK, $text);
2887 root 1.95 }
2888    
2889 root 1.494 =item $client->send_big_packet ($pkt)
2890    
2891     Like C<send_packet>, but tries to compress large packets, and fragments
2892     them as required.
2893    
2894     =cut
2895    
2896     our $MAXFRAGSIZE = cf::MAXSOCKBUF - 64;
2897    
2898     sub cf::client::send_big_packet {
2899     my ($self, $pkt) = @_;
2900    
2901     # try lzf for large packets
2902     $pkt = "lzf " . Compress::LZF::compress $pkt
2903     if 1024 <= length $pkt and $self->{can_lzf};
2904    
2905     # split very large packets
2906     if ($MAXFRAGSIZE < length $pkt and $self->{can_lzf}) {
2907     $self->send_packet ("frag $_") for unpack "(a$MAXFRAGSIZE)*", $pkt;
2908     $pkt = "frag";
2909     }
2910    
2911     $self->send_packet ($pkt);
2912     }
2913    
2914 root 1.311 =item $client->send_msg ($channel, $msg, $color, [extra...])
2915 root 1.283
2916     Send a drawinfo or msg packet to the client, formatting the msg for the
2917     client if neccessary. C<$type> should be a string identifying the type of
2918     the message, with C<log> being the default. If C<$color> is negative, suppress
2919     the message unless the client supports the msg packet.
2920    
2921     =cut
2922    
2923 root 1.391 # non-persistent channels (usually the info channel)
2924 root 1.350 our %CHANNEL = (
2925 root 1.486 "c/motd" => {
2926     id => "infobox",
2927     title => "MOTD",
2928     reply => undef,
2929     tooltip => "The message of the day",
2930     },
2931 root 1.350 "c/identify" => {
2932 root 1.375 id => "infobox",
2933 root 1.350 title => "Identify",
2934     reply => undef,
2935     tooltip => "Items recently identified",
2936     },
2937 root 1.352 "c/examine" => {
2938 root 1.375 id => "infobox",
2939 root 1.352 title => "Examine",
2940     reply => undef,
2941     tooltip => "Signs and other items you examined",
2942     },
2943 root 1.487 "c/shopinfo" => {
2944     id => "infobox",
2945     title => "Shop Info",
2946     reply => undef,
2947     tooltip => "What your bargaining skill tells you about the shop",
2948     },
2949 root 1.389 "c/book" => {
2950     id => "infobox",
2951     title => "Book",
2952     reply => undef,
2953     tooltip => "The contents of a note or book",
2954     },
2955 root 1.375 "c/lookat" => {
2956     id => "infobox",
2957     title => "Look",
2958     reply => undef,
2959     tooltip => "What you saw there",
2960     },
2961 root 1.390 "c/who" => {
2962     id => "infobox",
2963     title => "Players",
2964     reply => undef,
2965     tooltip => "Shows players who are currently online",
2966     },
2967     "c/body" => {
2968     id => "infobox",
2969     title => "Body Parts",
2970     reply => undef,
2971     tooltip => "Shows which body parts you posess and are available",
2972     },
2973 root 1.465 "c/statistics" => {
2974     id => "infobox",
2975     title => "Statistics",
2976     reply => undef,
2977     tooltip => "Shows your primary statistics",
2978     },
2979 root 1.450 "c/skills" => {
2980     id => "infobox",
2981     title => "Skills",
2982     reply => undef,
2983     tooltip => "Shows your experience per skill and item power",
2984     },
2985 root 1.470 "c/shopitems" => {
2986     id => "infobox",
2987     title => "Shop Items",
2988     reply => undef,
2989     tooltip => "Shows the items currently for sale in this shop",
2990     },
2991 root 1.465 "c/resistances" => {
2992     id => "infobox",
2993     title => "Resistances",
2994     reply => undef,
2995     tooltip => "Shows your resistances",
2996     },
2997     "c/pets" => {
2998     id => "infobox",
2999     title => "Pets",
3000     reply => undef,
3001     tooltip => "Shows information abotu your pets/a specific pet",
3002     },
3003 root 1.471 "c/perceiveself" => {
3004     id => "infobox",
3005     title => "Perceive Self",
3006     reply => undef,
3007     tooltip => "You gained detailed knowledge about yourself",
3008     },
3009 root 1.390 "c/uptime" => {
3010     id => "infobox",
3011     title => "Uptime",
3012     reply => undef,
3013 root 1.391 tooltip => "How long the server has been running since last restart",
3014 root 1.390 },
3015     "c/mapinfo" => {
3016     id => "infobox",
3017     title => "Map Info",
3018     reply => undef,
3019     tooltip => "Information related to the maps",
3020     },
3021 root 1.426 "c/party" => {
3022     id => "party",
3023     title => "Party",
3024     reply => "gsay ",
3025     tooltip => "Messages and chat related to your party",
3026     },
3027 root 1.464 "c/death" => {
3028     id => "death",
3029     title => "Death",
3030     reply => undef,
3031     tooltip => "Reason for and more info about your most recent death",
3032     },
3033 root 1.462 "c/say" => $SAY_CHANNEL,
3034     "c/chat" => $CHAT_CHANNEL,
3035 root 1.350 );
3036    
3037 root 1.283 sub cf::client::send_msg {
3038 root 1.311 my ($self, $channel, $msg, $color, @extra) = @_;
3039 root 1.283
3040 root 1.447 $msg = $self->pl->expand_cfpod ($msg)
3041     unless $color & cf::NDI_VERBATIM;
3042 root 1.283
3043 root 1.323 $color &= cf::NDI_CLIENT_MASK; # just in case...
3044 root 1.311
3045 root 1.350 # check predefined channels, for the benefit of C
3046 root 1.375 if ($CHANNEL{$channel}) {
3047     $channel = $CHANNEL{$channel};
3048    
3049 root 1.463 $self->ext_msg (channel_info => $channel);
3050 root 1.375 $channel = $channel->{id};
3051 root 1.350
3052 root 1.375 } elsif (ref $channel) {
3053 root 1.311 # send meta info to client, if not yet sent
3054     unless (exists $self->{channel}{$channel->{id}}) {
3055     $self->{channel}{$channel->{id}} = $channel;
3056 root 1.463 $self->ext_msg (channel_info => $channel);
3057 root 1.311 }
3058    
3059     $channel = $channel->{id};
3060     }
3061    
3062 root 1.313 return unless @extra || length $msg;
3063    
3064 root 1.463 # default colour, mask it out
3065     $color &= ~(cf::NDI_COLOR_MASK | cf::NDI_DEF)
3066     if $color & cf::NDI_DEF;
3067    
3068     my $pkt = "msg "
3069     . $self->{json_coder}->encode (
3070     [$color & cf::NDI_CLIENT_MASK, $channel, $msg, @extra]
3071     );
3072    
3073 root 1.494 $self->send_big_packet ($pkt);
3074 root 1.283 }
3075    
3076 root 1.316 =item $client->ext_msg ($type, @msg)
3077 root 1.232
3078 root 1.287 Sends an ext event to the client.
3079 root 1.232
3080     =cut
3081    
3082 root 1.316 sub cf::client::ext_msg($$@) {
3083     my ($self, $type, @msg) = @_;
3084 root 1.232
3085 root 1.343 if ($self->extcmd == 2) {
3086 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ([$type, @msg]));
3087 root 1.343 } elsif ($self->extcmd == 1) { # TODO: remove
3088 root 1.316 push @msg, msgtype => "event_$type";
3089 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3090 root 1.316 }
3091 root 1.232 }
3092 root 1.95
3093 root 1.336 =item $client->ext_reply ($msgid, @msg)
3094    
3095     Sends an ext reply to the client.
3096    
3097     =cut
3098    
3099     sub cf::client::ext_reply($$@) {
3100     my ($self, $id, @msg) = @_;
3101    
3102     if ($self->extcmd == 2) {
3103 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode (["reply-$id", @msg]));
3104 root 1.343 } elsif ($self->extcmd == 1) {
3105 root 1.336 #TODO: version 1, remove
3106     unshift @msg, msgtype => "reply", msgid => $id;
3107 root 1.494 $self->send_big_packet ("ext " . $self->{json_coder}->encode ({@msg}));
3108 root 1.336 }
3109     }
3110    
3111 root 1.95 =item $success = $client->query ($flags, "text", \&cb)
3112    
3113     Queues a query to the client, calling the given callback with
3114     the reply text on a reply. flags can be C<cf::CS_QUERY_YESNO>,
3115     C<cf::CS_QUERY_SINGLECHAR> or C<cf::CS_QUERY_HIDEINPUT> or C<0>.
3116    
3117 root 1.287 Queries can fail, so check the return code. Or don't, as queries will
3118     become reliable at some point in the future.
3119 root 1.95
3120     =cut
3121    
3122     sub cf::client::query {
3123     my ($self, $flags, $text, $cb) = @_;
3124    
3125     return unless $self->state == ST_PLAYING
3126     || $self->state == ST_SETUP
3127     || $self->state == ST_CUSTOM;
3128    
3129     $self->state (ST_CUSTOM);
3130    
3131     utf8::encode $text;
3132     push @{ $self->{query_queue} }, [(sprintf "query %d %s", $flags, $text), $cb];
3133    
3134     $self->send_packet ($self->{query_queue}[0][0])
3135     if @{ $self->{query_queue} } == 1;
3136 root 1.287
3137     1
3138 root 1.95 }
3139    
3140     cf::client->attach (
3141 root 1.290 on_connect => sub {
3142     my ($ns) = @_;
3143    
3144     $ns->{json_coder} = JSON::XS->new->utf8->max_size (1e6)->convert_blessed;
3145     },
3146 root 1.95 on_reply => sub {
3147     my ($ns, $msg) = @_;
3148    
3149     # this weird shuffling is so that direct followup queries
3150     # get handled first
3151 root 1.128 my $queue = delete $ns->{query_queue}
3152 root 1.129 or return; # be conservative, not sure how that can happen, but we saw a crash here
3153 root 1.95
3154     (shift @$queue)->[1]->($msg);
3155 root 1.202 return unless $ns->valid; # temporary(?) workaround for callback destroying socket
3156 root 1.95
3157     push @{ $ns->{query_queue} }, @$queue;
3158    
3159     if (@{ $ns->{query_queue} } == @$queue) {
3160     if (@$queue) {
3161     $ns->send_packet ($ns->{query_queue}[0][0]);
3162     } else {
3163 root 1.98 $ns->state (ST_PLAYING) if $ns->state == ST_CUSTOM;
3164 root 1.95 }
3165     }
3166     },
3167 root 1.287 on_exticmd => sub {
3168     my ($ns, $buf) = @_;
3169    
3170 root 1.290 my $msg = eval { $ns->{json_coder}->decode ($buf) };
3171 root 1.287
3172     if (ref $msg) {
3173 root 1.316 my ($type, $reply, @payload) =
3174     "ARRAY" eq ref $msg
3175     ? @$msg
3176     : ($msg->{msgtype}, $msg->{msgid}, %$msg); # TODO: version 1, remove
3177    
3178 root 1.338 my @reply;
3179    
3180 root 1.316 if (my $cb = $EXTICMD{$type}) {
3181 root 1.338 @reply = $cb->($ns, @payload);
3182     }
3183    
3184     $ns->ext_reply ($reply, @reply)
3185     if $reply;
3186 root 1.316
3187 root 1.287 } else {
3188 root 1.532 error "client " . ($ns->pl ? $ns->pl->ob->name : $ns->host) . " sent unparseable exti message: <$buf>\n";
3189 root 1.287 }
3190    
3191     cf::override;
3192     },
3193 root 1.95 );
3194    
3195 root 1.140 =item $client->async (\&cb)
3196 root 1.96
3197     Create a new coroutine, running the specified callback. The coroutine will
3198     be automatically cancelled when the client gets destroyed (e.g. on logout,
3199     or loss of connection).
3200    
3201     =cut
3202    
3203 root 1.140 sub cf::client::async {
3204 root 1.96 my ($self, $cb) = @_;
3205    
3206 root 1.140 my $coro = &Coro::async ($cb);
3207 root 1.103
3208     $coro->on_destroy (sub {
3209 root 1.96 delete $self->{_coro}{$coro+0};
3210 root 1.103 });
3211 root 1.96
3212     $self->{_coro}{$coro+0} = $coro;
3213 root 1.103
3214     $coro
3215 root 1.96 }
3216    
3217     cf::client->attach (
3218 root 1.509 on_client_destroy => sub {
3219 root 1.96 my ($ns) = @_;
3220    
3221 root 1.97 $_->cancel for values %{ (delete $ns->{_coro}) || {} };
3222 root 1.96 },
3223     );
3224    
3225 root 1.95 =back
3226    
3227 root 1.70
3228     =head2 SAFE SCRIPTING
3229    
3230     Functions that provide a safe environment to compile and execute
3231     snippets of perl code without them endangering the safety of the server
3232     itself. Looping constructs, I/O operators and other built-in functionality
3233     is not available in the safe scripting environment, and the number of
3234 root 1.79 functions and methods that can be called is greatly reduced.
3235 root 1.70
3236     =cut
3237 root 1.23
3238 root 1.42 our $safe = new Safe "safe";
3239 root 1.23 our $safe_hole = new Safe::Hole;
3240    
3241     $SIG{FPE} = 'IGNORE';
3242    
3243 root 1.328 $safe->permit_only (Opcode::opset qw(
3244 elmex 1.498 :base_core :base_mem :base_orig :base_math :base_loop
3245 root 1.328 grepstart grepwhile mapstart mapwhile
3246     sort time
3247     ));
3248 root 1.23
3249 root 1.25 # here we export the classes and methods available to script code
3250    
3251 root 1.70 =pod
3252    
3253 root 1.228 The following functions and methods are available within a safe environment:
3254 root 1.70
3255 root 1.297 cf::object
3256 root 1.383 contr pay_amount pay_player map x y force_find force_add destroy
3257 root 1.425 insert remove name archname title slaying race decrease split
3258 root 1.466 value
3259 root 1.297
3260     cf::object::player
3261     player
3262    
3263     cf::player
3264     peaceful
3265    
3266     cf::map
3267     trigger
3268 root 1.70
3269     =cut
3270    
3271 root 1.25 for (
3272 root 1.297 ["cf::object" => qw(contr pay_amount pay_player map force_find force_add x y
3273 elmex 1.431 insert remove inv nrof name archname title slaying race
3274 root 1.466 decrease split destroy change_exp value msg lore send_msg)],
3275 root 1.25 ["cf::object::player" => qw(player)],
3276 root 1.466 ["cf::player" => qw(peaceful send_msg)],
3277 elmex 1.91 ["cf::map" => qw(trigger)],
3278 root 1.25 ) {
3279     no strict 'refs';
3280     my ($pkg, @funs) = @$_;
3281 root 1.41 *{"safe::$pkg\::$_"} = $safe_hole->wrap (\&{"$pkg\::$_"})
3282 root 1.25 for @funs;
3283     }
3284 root 1.23
3285 root 1.70 =over 4
3286    
3287     =item @retval = safe_eval $code, [var => value, ...]
3288    
3289     Compiled and executes the given perl code snippet. additional var/value
3290     pairs result in temporary local (my) scalar variables of the given name
3291     that are available in the code snippet. Example:
3292    
3293     my $five = safe_eval '$first + $second', first => 1, second => 4;
3294    
3295     =cut
3296    
3297 root 1.23 sub safe_eval($;@) {
3298     my ($code, %vars) = @_;
3299    
3300     my $qcode = $code;
3301     $qcode =~ s/"/‟/g; # not allowed in #line filenames
3302     $qcode =~ s/\n/\\n/g;
3303    
3304 root 1.466 %vars = (_dummy => 0) unless %vars;
3305    
3306 root 1.499 my @res;
3307 root 1.23 local $_;
3308    
3309 root 1.42 my $eval =
3310 root 1.23 "do {\n"
3311     . "my (" . (join ",", map "\$$_", keys %vars) . ") = \@cf::_safe_eval_args;\n"
3312     . "#line 0 \"{$qcode}\"\n"
3313     . $code
3314     . "\n}"
3315 root 1.25 ;
3316    
3317 root 1.499 if ($CFG{safe_eval}) {
3318     sub_generation_inc;
3319     local @safe::cf::_safe_eval_args = values %vars;
3320     @res = wantarray ? $safe->reval ($eval) : scalar $safe->reval ($eval);
3321     sub_generation_inc;
3322     } else {
3323     local @cf::_safe_eval_args = values %vars;
3324     @res = wantarray ? eval eval : scalar eval $eval;
3325     }
3326 root 1.25
3327 root 1.42 if ($@) {
3328 root 1.532 warn "$@",
3329     "while executing safe code '$code'\n",
3330     "with arguments " . (join " ", %vars) . "\n";
3331 root 1.42 }
3332    
3333 root 1.25 wantarray ? @res : $res[0]
3334 root 1.23 }
3335    
3336 root 1.69 =item cf::register_script_function $function => $cb
3337    
3338     Register a function that can be called from within map/npc scripts. The
3339     function should be reasonably secure and should be put into a package name
3340     like the extension.
3341    
3342     Example: register a function that gets called whenever a map script calls
3343     C<rent::overview>, as used by the C<rent> extension.
3344    
3345     cf::register_script_function "rent::overview" => sub {
3346     ...
3347     };
3348    
3349     =cut
3350    
3351 root 1.23 sub register_script_function {
3352     my ($fun, $cb) = @_;
3353    
3354 root 1.501 $fun = "safe::$fun" if $CFG{safe_eval};
3355     *$fun = $safe_hole->wrap ($cb);
3356 root 1.23 }
3357    
3358 root 1.70 =back
3359    
3360 root 1.71 =cut
3361    
3362 root 1.23 #############################################################################
3363 root 1.203 # the server's init and main functions
3364    
3365 root 1.246 sub load_facedata($) {
3366     my ($path) = @_;
3367 root 1.223
3368 root 1.348 # HACK to clear player env face cache, we need some signal framework
3369     # for this (global event?)
3370     %ext::player_env::MUSIC_FACE_CACHE = ();
3371    
3372 root 1.344 my $enc = JSON::XS->new->utf8->canonical->relaxed;
3373 root 1.334
3374 root 1.532 trace "loading facedata from $path\n";
3375 root 1.223
3376 root 1.236 my $facedata;
3377     0 < aio_load $path, $facedata
3378 root 1.223 or die "$path: $!";
3379    
3380 root 1.237 $facedata = Coro::Storable::thaw $facedata;
3381 root 1.223
3382 root 1.236 $facedata->{version} == 2
3383 root 1.226 or cf::cleanup "$path: version mismatch, cannot proceed.";
3384    
3385 root 1.334 # patch in the exptable
3386 root 1.500 my $exp_table = $enc->encode ([map cf::level_to_min_exp $_, 1 .. cf::settings->max_level]);
3387 root 1.334 $facedata->{resource}{"res/exp_table"} = {
3388     type => FT_RSRC,
3389 root 1.500 data => $exp_table,
3390     hash => (Digest::MD5::md5 $exp_table),
3391 root 1.334 };
3392     cf::cede_to_tick;
3393    
3394 root 1.236 {
3395     my $faces = $facedata->{faceinfo};
3396    
3397     while (my ($face, $info) = each %$faces) {
3398     my $idx = (cf::face::find $face) || cf::face::alloc $face;
3399 root 1.405
3400 root 1.302 cf::face::set_visibility $idx, $info->{visibility};
3401     cf::face::set_magicmap $idx, $info->{magicmap};
3402 root 1.496 cf::face::set_data $idx, 0, $info->{data32}, $info->{hash32};
3403     cf::face::set_data $idx, 1, $info->{data64}, $info->{hash64};
3404 root 1.302
3405     cf::cede_to_tick;
3406 root 1.236 }
3407    
3408     while (my ($face, $info) = each %$faces) {
3409     next unless $info->{smooth};
3410 root 1.405
3411 root 1.236 my $idx = cf::face::find $face
3412     or next;
3413 root 1.405
3414 root 1.236 if (my $smooth = cf::face::find $info->{smooth}) {
3415 root 1.302 cf::face::set_smooth $idx, $smooth;
3416     cf::face::set_smoothlevel $idx, $info->{smoothlevel};
3417 root 1.236 } else {
3418 root 1.532 error "smooth face '$info->{smooth}' not found for face '$face'";
3419 root 1.236 }
3420 root 1.302
3421     cf::cede_to_tick;
3422 root 1.236 }
3423 root 1.223 }
3424    
3425 root 1.236 {
3426     my $anims = $facedata->{animinfo};
3427    
3428     while (my ($anim, $info) = each %$anims) {
3429     cf::anim::set $anim, $info->{frames}, $info->{facings};
3430 root 1.302 cf::cede_to_tick;
3431 root 1.225 }
3432 root 1.236
3433     cf::anim::invalidate_all; # d'oh
3434 root 1.225 }
3435    
3436 root 1.302 {
3437     my $res = $facedata->{resource};
3438    
3439     while (my ($name, $info) = each %$res) {
3440 root 1.405 if (defined $info->{type}) {
3441     my $idx = (cf::face::find $name) || cf::face::alloc $name;
3442    
3443 root 1.496 cf::face::set_data $idx, 0, $info->{data}, $info->{hash};
3444 root 1.405 cf::face::set_type $idx, $info->{type};
3445 root 1.337 } else {
3446 root 1.530 $RESOURCE{$name} = $info; # unused
3447 root 1.307 }
3448 root 1.302
3449     cf::cede_to_tick;
3450     }
3451 root 1.406 }
3452    
3453     cf::global->invoke (EVENT_GLOBAL_RESOURCE_UPDATE);
3454 root 1.321
3455 root 1.406 1
3456     }
3457    
3458 root 1.318 register_exticmd fx_want => sub {
3459     my ($ns, $want) = @_;
3460    
3461     while (my ($k, $v) = each %$want) {
3462     $ns->fx_want ($k, $v);
3463     }
3464     };
3465    
3466 root 1.423 sub load_resource_file($) {
3467 root 1.424 my $guard = lock_acquire "load_resource_file";
3468    
3469 root 1.423 my $status = load_resource_file_ $_[0];
3470     get_slot 0.1, 100;
3471     cf::arch::commit_load;
3472 root 1.424
3473 root 1.423 $status
3474     }
3475    
3476 root 1.253 sub reload_regions {
3477 root 1.348 # HACK to clear player env face cache, we need some signal framework
3478     # for this (global event?)
3479     %ext::player_env::MUSIC_FACE_CACHE = ();
3480    
3481 root 1.253 load_resource_file "$MAPDIR/regions"
3482     or die "unable to load regions file\n";
3483 root 1.304
3484     for (cf::region::list) {
3485     $_->{match} = qr/$_->{match}/
3486     if exists $_->{match};
3487     }
3488 root 1.253 }
3489    
3490 root 1.246 sub reload_facedata {
3491 root 1.253 load_facedata "$DATADIR/facedata"
3492 root 1.246 or die "unable to load facedata\n";
3493     }
3494    
3495     sub reload_archetypes {
3496 root 1.253 load_resource_file "$DATADIR/archetypes"
3497 root 1.246 or die "unable to load archetypes\n";
3498 root 1.241 }
3499    
3500 root 1.246 sub reload_treasures {
3501 root 1.253 load_resource_file "$DATADIR/treasures"
3502 root 1.246 or die "unable to load treasurelists\n";
3503 root 1.241 }
3504    
3505 root 1.530 sub reload_sound {
3506 root 1.532 trace "loading sound config from $DATADIR/sound\n";
3507 root 1.531
3508 root 1.530 0 < Coro::AIO::aio_load "$DATADIR/sound", my $data
3509     or die "$DATADIR/sound $!";
3510    
3511     my $soundconf = JSON::XS->new->utf8->relaxed->decode ($data);
3512    
3513     for (0 .. SOUND_CAST_SPELL_0 - 1) {
3514     my $sound = $soundconf->{compat}[$_]
3515     or next;
3516    
3517     my $face = cf::face::find "sound/$sound->[1]";
3518     cf::sound::set $sound->[0] => $face;
3519     cf::sound::old_sound_index $_, $face; # gcfclient-compat
3520     }
3521    
3522     while (my ($k, $v) = each %{$soundconf->{event}}) {
3523     my $face = cf::face::find "sound/$v";
3524     cf::sound::set $k => $face;
3525     }
3526     }
3527    
3528 root 1.223 sub reload_resources {
3529 root 1.532 trace "reloading resource files...\n";
3530 root 1.245
3531 root 1.246 reload_facedata;
3532 root 1.530 reload_sound;
3533 root 1.246 reload_archetypes;
3534 root 1.423 reload_regions;
3535 root 1.246 reload_treasures;
3536 root 1.245
3537 root 1.532 trace "finished reloading resource files\n";
3538 root 1.223 }
3539    
3540 root 1.345 sub reload_config {
3541 root 1.532 trace "reloading config file...\n";
3542 root 1.485
3543 root 1.253 open my $fh, "<:utf8", "$CONFDIR/config"
3544 root 1.72 or return;
3545    
3546     local $/;
3547 root 1.485 *CFG = YAML::XS::Load scalar <$fh>;
3548 root 1.131
3549 root 1.527 $EMERGENCY_POSITION = $CFG{emergency_position} || ["/world/world_104_115", 49, 38];
3550 root 1.131
3551 root 1.139 $cf::map::MAX_RESET = $CFG{map_max_reset} if exists $CFG{map_max_reset};
3552     $cf::map::DEFAULT_RESET = $CFG{map_default_reset} if exists $CFG{map_default_reset};
3553    
3554 root 1.131 if (exists $CFG{mlockall}) {
3555     eval {
3556 root 1.147 $CFG{mlockall} ? eval "mlockall()" : eval "munlockall()"
3557 root 1.131 and die "WARNING: m(un)lockall failed: $!\n";
3558     };
3559     warn $@ if $@;
3560     }
3561 root 1.485
3562 root 1.532 trace "finished reloading resource files\n";
3563 root 1.72 }
3564    
3565 root 1.445 sub pidfile() {
3566     sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT
3567     or die "$PIDFILE: $!";
3568     flock $fh, &Fcntl::LOCK_EX
3569     or die "$PIDFILE: flock: $!";
3570     $fh
3571     }
3572    
3573     # make sure only one server instance is running at any one time
3574     sub atomic {
3575     my $fh = pidfile;
3576    
3577     my $pid = <$fh>;
3578     kill 9, $pid if $pid > 0;
3579    
3580     seek $fh, 0, 0;
3581     print $fh $$;
3582     }
3583    
3584 root 1.474 sub main_loop {
3585 root 1.532 trace "EV::loop starting\n";
3586 root 1.474 if (1) {
3587     EV::loop;
3588     }
3589 root 1.532 trace "EV::loop returned\n";
3590 root 1.474 goto &main_loop unless $REALLY_UNLOOP;
3591     }
3592    
3593 root 1.39 sub main {
3594 root 1.453 cf::init_globals; # initialise logging
3595    
3596     LOG llevInfo, "Welcome to Deliantra, v" . VERSION;
3597     LOG llevInfo, "Copyright (C) 2005-2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team.";
3598     LOG llevInfo, "Copyright (C) 1994 Mark Wedel.";
3599     LOG llevInfo, "Copyright (C) 1992 Frank Tore Johansen.";
3600    
3601     $Coro::current->prio (Coro::PRIO_MAX); # give the main loop max. priority
3602 root 1.445
3603 root 1.108 # we must not ever block the main coroutine
3604     local $Coro::idle = sub {
3605 root 1.115 Carp::cluck "FATAL: Coro::idle was called, major BUG, use cf::sync_job!\n";#d#
3606 root 1.175 (async {
3607 root 1.374 $Coro::current->{desc} = "IDLE BUG HANDLER";
3608 root 1.396 EV::loop EV::LOOP_ONESHOT;
3609 root 1.175 })->prio (Coro::PRIO_MAX);
3610 root 1.108 };
3611    
3612 root 1.453 evthread_start IO::AIO::poll_fileno;
3613    
3614     cf::sync_job {
3615 root 1.515 cf::init_experience;
3616     cf::init_anim;
3617     cf::init_attackmess;
3618     cf::init_dynamic;
3619    
3620 root 1.495 cf::load_settings;
3621     cf::load_materials;
3622    
3623 root 1.453 reload_resources;
3624 root 1.423 reload_config;
3625     db_init;
3626 root 1.453
3627     cf::init_uuid;
3628     cf::init_signals;
3629     cf::init_skills;
3630    
3631     cf::init_beforeplay;
3632    
3633     atomic;
3634    
3635 root 1.423 load_extensions;
3636    
3637 root 1.453 utime time, time, $RUNTIMEFILE;
3638 root 1.183
3639 root 1.453 # no (long-running) fork's whatsoever before this point(!)
3640 root 1.475 use POSIX ();
3641 root 1.453 POSIX::close delete $ENV{LOCKUTIL_LOCK_FD} if exists $ENV{LOCKUTIL_LOCK_FD};
3642 root 1.445
3643 root 1.453 (pop @POST_INIT)->(0) while @POST_INIT;
3644     };
3645 root 1.445
3646 root 1.516 cf::object::thawer::errors_are_fatal 0;
3647 root 1.532 info "parse errors in files are no longer fatal from this point on.\n";
3648 root 1.516
3649 root 1.474 main_loop;
3650 root 1.34 }
3651    
3652     #############################################################################
3653 root 1.155 # initialisation and cleanup
3654    
3655     # install some emergency cleanup handlers
3656     BEGIN {
3657 root 1.396 our %SIGWATCHER = ();
3658 root 1.155 for my $signal (qw(INT HUP TERM)) {
3659 root 1.512 $SIGWATCHER{$signal} = AE::signal $signal, sub {
3660 root 1.396 cf::cleanup "SIG$signal";
3661     };
3662 root 1.155 }
3663     }
3664    
3665 root 1.417 sub write_runtime_sync {
3666 root 1.512 my $t0 = AE::time;
3667 root 1.506
3668 root 1.281 # first touch the runtime file to show we are still running:
3669     # the fsync below can take a very very long time.
3670    
3671 root 1.445 IO::AIO::aio_utime $RUNTIMEFILE, undef, undef;
3672 root 1.281
3673     my $guard = cf::lock_acquire "write_runtime";
3674    
3675 root 1.505 my $fh = aio_open "$RUNTIMEFILE~", O_WRONLY | O_CREAT | O_TRUNC, 0644
3676 root 1.281 or return;
3677    
3678     my $value = $cf::RUNTIME + 90 + 10;
3679     # 10 is the runtime save interval, for a monotonic clock
3680     # 60 allows for the watchdog to kill the server.
3681    
3682     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3683     and return;
3684    
3685     # always fsync - this file is important
3686     aio_fsync $fh
3687     and return;
3688    
3689     # touch it again to show we are up-to-date
3690     aio_utime $fh, undef, undef;
3691    
3692     close $fh
3693     or return;
3694    
3695 root 1.445 aio_rename "$RUNTIMEFILE~", $RUNTIMEFILE
3696 root 1.281 and return;
3697    
3698 root 1.532 trace sprintf "runtime file written (%gs).\n", AE::time - $t0;
3699 root 1.281
3700     1
3701     }
3702    
3703 root 1.416 our $uuid_lock;
3704     our $uuid_skip;
3705    
3706     sub write_uuid_sync($) {
3707     $uuid_skip ||= $_[0];
3708    
3709     return if $uuid_lock;
3710     local $uuid_lock = 1;
3711    
3712     my $uuid = "$LOCALDIR/uuid";
3713    
3714     my $fh = aio_open "$uuid~", O_WRONLY | O_CREAT, 0644
3715     or return;
3716    
3717 root 1.454 my $value = uuid_seq uuid_cur;
3718    
3719 root 1.455 unless ($value) {
3720 root 1.532 info "cowardly refusing to write zero uuid value!\n";
3721 root 1.454 return;
3722     }
3723    
3724     my $value = uuid_str $value + $uuid_skip;
3725 root 1.416 $uuid_skip = 0;
3726    
3727     (aio_write $fh, 0, (length $value), $value, 0) <= 0
3728     and return;
3729    
3730     # always fsync - this file is important
3731     aio_fsync $fh
3732     and return;
3733    
3734     close $fh
3735     or return;
3736    
3737     aio_rename "$uuid~", $uuid
3738     and return;
3739    
3740 root 1.532 trace "uuid file written ($value).\n";
3741 root 1.416
3742     1
3743    
3744     }
3745    
3746     sub write_uuid($$) {
3747     my ($skip, $sync) = @_;
3748    
3749     $sync ? write_uuid_sync $skip
3750     : async { write_uuid_sync $skip };
3751     }
3752    
3753 root 1.156 sub emergency_save() {
3754 root 1.155 my $freeze_guard = cf::freeze_mainloop;
3755    
3756 root 1.532 info "emergency_perl_save: enter\n";
3757 root 1.155
3758 root 1.534 # this is a trade-off: we want to be very quick here, so
3759     # save all maps without fsync, and later call a global sync
3760     # (which in turn might be very very slow)
3761     local $USE_FSYNC = 0;
3762    
3763 root 1.155 cf::sync_job {
3764 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3765    
3766 root 1.155 # use a peculiar iteration method to avoid tripping on perl
3767     # refcount bugs in for. also avoids problems with players
3768 root 1.167 # and maps saved/destroyed asynchronously.
3769 root 1.532 info "emergency_perl_save: begin player save\n";
3770 root 1.155 for my $login (keys %cf::PLAYER) {
3771     my $pl = $cf::PLAYER{$login} or next;
3772     $pl->valid or next;
3773 root 1.382 delete $pl->{unclean_save}; # not strictly necessary, but cannot hurt
3774 root 1.155 $pl->save;
3775     }
3776 root 1.532 info "emergency_perl_save: end player save\n";
3777 root 1.155
3778 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3779    
3780 root 1.532 info "emergency_perl_save: begin map save\n";
3781 root 1.155 for my $path (keys %cf::MAP) {
3782     my $map = $cf::MAP{$path} or next;
3783     $map->valid or next;
3784     $map->save;
3785     }
3786 root 1.532 info "emergency_perl_save: end map save\n";
3787 root 1.208
3788 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3789    
3790 root 1.532 info "emergency_perl_save: begin database checkpoint\n";
3791 root 1.208 BDB::db_env_txn_checkpoint $DB_ENV;
3792 root 1.532 info "emergency_perl_save: end database checkpoint\n";
3793 root 1.416
3794 root 1.532 info "emergency_perl_save: begin write uuid\n";
3795 root 1.416 write_uuid_sync 1;
3796 root 1.532 info "emergency_perl_save: end write uuid\n";
3797 root 1.155
3798 root 1.535 cf::write_runtime_sync; # external watchdog should not bark
3799    
3800     trace "emergency_perl_save: syncing database to disk";
3801     BDB::db_env_txn_checkpoint $DB_ENV;
3802    
3803 root 1.536 info "emergency_perl_save: starting sync\n";
3804 root 1.535 IO::AIO::aio_sync sub {
3805 root 1.536 info "emergency_perl_save: finished sync\n";
3806 root 1.535 };
3807    
3808     cf::write_runtime_sync; # external watchdog should not bark
3809    
3810     trace "emergency_perl_save: flushing outstanding aio requests";
3811     while (IO::AIO::nreqs || BDB::nreqs) {
3812     Coro::EV::timer_once 0.01; # let the sync_job do it's thing
3813     }
3814    
3815     cf::write_runtime_sync; # external watchdog should not bark
3816 root 1.457 };
3817    
3818 root 1.532 info "emergency_perl_save: leave\n";
3819 root 1.155 }
3820 root 1.22
3821 root 1.211 sub post_cleanup {
3822     my ($make_core) = @_;
3823    
3824 root 1.535 IO::AIO::flush;
3825    
3826 root 1.532 error Carp::longmess "post_cleanup backtrace"
3827 root 1.211 if $make_core;
3828 root 1.445
3829     my $fh = pidfile;
3830     unlink $PIDFILE if <$fh> == $$;
3831 root 1.211 }
3832    
3833 root 1.441 # a safer delete_package, copied from Symbol
3834     sub clear_package($) {
3835     my $pkg = shift;
3836    
3837     # expand to full symbol table name if needed
3838     unless ($pkg =~ /^main::.*::$/) {
3839     $pkg = "main$pkg" if $pkg =~ /^::/;
3840     $pkg = "main::$pkg" unless $pkg =~ /^main::/;
3841     $pkg .= '::' unless $pkg =~ /::$/;
3842     }
3843    
3844     my($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
3845     my $stem_symtab = *{$stem}{HASH};
3846    
3847     defined $stem_symtab and exists $stem_symtab->{$leaf}
3848     or return;
3849    
3850     # clear all symbols
3851     my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
3852     for my $name (keys %$leaf_symtab) {
3853     _gv_clear *{"$pkg$name"};
3854     # use PApp::Util; PApp::Util::sv_dump *{"$pkg$name"};
3855     }
3856     }
3857    
3858 root 1.246 sub do_reload_perl() {
3859 root 1.106 # can/must only be called in main
3860 root 1.522 if (in_main) {
3861 root 1.532 error "can only reload from main coroutine";
3862 root 1.106 return;
3863     }
3864    
3865 root 1.441 return if $RELOAD++;
3866    
3867 root 1.512 my $t1 = AE::time;
3868 root 1.457
3869 root 1.441 while ($RELOAD) {
3870 root 1.532 info "reloading...";
3871 root 1.103
3872 root 1.532 trace "entering sync_job";
3873 root 1.212
3874 root 1.441 cf::sync_job {
3875     cf::emergency_save;
3876 root 1.183
3877 root 1.532 trace "cancelling all extension coros";
3878 root 1.441 $_->cancel for values %EXT_CORO;
3879     %EXT_CORO = ();
3880 root 1.223
3881 root 1.532 trace "removing commands";
3882 root 1.441 %COMMAND = ();
3883 root 1.103
3884 root 1.532 trace "removing ext/exti commands";
3885 root 1.441 %EXTCMD = ();
3886     %EXTICMD = ();
3887 root 1.159
3888 root 1.532 trace "unloading/nuking all extensions";
3889 root 1.441 for my $pkg (@EXTS) {
3890 root 1.532 trace "... unloading $pkg";
3891 root 1.159
3892 root 1.441 if (my $cb = $pkg->can ("unload")) {
3893     eval {
3894     $cb->($pkg);
3895     1
3896 root 1.532 } or error "$pkg unloaded, but with errors: $@";
3897 root 1.441 }
3898 root 1.159
3899 root 1.532 trace "... clearing $pkg";
3900 root 1.441 clear_package $pkg;
3901 root 1.159 }
3902    
3903 root 1.532 trace "unloading all perl modules loaded from $LIBDIR";
3904 root 1.441 while (my ($k, $v) = each %INC) {
3905     next unless $v =~ /^\Q$LIBDIR\E\/.*\.pm$/;
3906 root 1.65
3907 root 1.532 trace "... unloading $k";
3908 root 1.441 delete $INC{$k};
3909 root 1.65
3910 root 1.441 $k =~ s/\.pm$//;
3911     $k =~ s/\//::/g;
3912 root 1.65
3913 root 1.441 if (my $cb = $k->can ("unload_module")) {
3914     $cb->();
3915     }
3916 root 1.65
3917 root 1.441 clear_package $k;
3918 root 1.65 }
3919    
3920 root 1.532 trace "getting rid of safe::, as good as possible";
3921 root 1.441 clear_package "safe::$_"
3922     for qw(cf::attachable cf::object cf::object::player cf::client cf::player cf::map cf::party cf::region);
3923 root 1.65
3924 root 1.532 trace "unloading cf.pm \"a bit\"";
3925 root 1.441 delete $INC{"cf.pm"};
3926 root 1.466 delete $INC{"cf/$_.pm"} for @EXTRA_MODULES;
3927 root 1.65
3928 root 1.441 # don't, removes xs symbols, too,
3929     # and global variables created in xs
3930     #clear_package __PACKAGE__;
3931 root 1.65
3932 root 1.532 info "unload completed, starting to reload now";
3933 root 1.65
3934 root 1.532 trace "reloading cf.pm";
3935 root 1.441 require cf;
3936 root 1.483 cf::_connect_to_perl_1;
3937 root 1.183
3938 root 1.532 trace "loading config and database again";
3939 root 1.441 cf::reload_config;
3940 root 1.100
3941 root 1.532 trace "loading extensions";
3942 root 1.441 cf::load_extensions;
3943 root 1.65
3944 root 1.457 if ($REATTACH_ON_RELOAD) {
3945 root 1.532 trace "reattaching attachments to objects/players";
3946 root 1.457 _global_reattach; # objects, sockets
3947 root 1.532 trace "reattaching attachments to maps";
3948 root 1.457 reattach $_ for values %MAP;
3949 root 1.532 trace "reattaching attachments to players";
3950 root 1.457 reattach $_ for values %PLAYER;
3951     }
3952 root 1.65
3953 root 1.532 trace "running post_init jobs";
3954 root 1.453 (pop @POST_INIT)->(1) while @POST_INIT;
3955    
3956 root 1.532 trace "leaving sync_job";
3957 root 1.183
3958 root 1.441 1
3959     } or do {
3960 root 1.532 error $@;
3961 root 1.441 cf::cleanup "error while reloading, exiting.";
3962     };
3963 root 1.183
3964 root 1.532 info "reloaded";
3965 root 1.441 --$RELOAD;
3966     }
3967 root 1.457
3968 root 1.512 $t1 = AE::time - $t1;
3969 root 1.532 info "reload completed in ${t1}s\n";
3970 root 1.65 };
3971    
3972 root 1.175 our $RELOAD_WATCHER; # used only during reload
3973    
3974 root 1.246 sub reload_perl() {
3975     # doing reload synchronously and two reloads happen back-to-back,
3976     # coro crashes during coro_state_free->destroy here.
3977    
3978 root 1.457 $RELOAD_WATCHER ||= cf::async {
3979     Coro::AIO::aio_wait cache_extensions;
3980    
3981 root 1.512 $RELOAD_WATCHER = AE::timer $TICK * 1.5, 0, sub {
3982 root 1.457 do_reload_perl;
3983     undef $RELOAD_WATCHER;
3984     };
3985 root 1.396 };
3986 root 1.246 }
3987    
3988 root 1.111 register_command "reload" => sub {
3989 root 1.65 my ($who, $arg) = @_;
3990    
3991     if ($who->flag (FLAG_WIZ)) {
3992 root 1.175 $who->message ("reloading server.");
3993 root 1.374 async {
3994     $Coro::current->{desc} = "perl_reload";
3995     reload_perl;
3996     };
3997 root 1.65 }
3998     };
3999    
4000 root 1.27 unshift @INC, $LIBDIR;
4001 root 1.17
4002 root 1.183 my $bug_warning = 0;
4003    
4004 root 1.239 our @WAIT_FOR_TICK;
4005     our @WAIT_FOR_TICK_BEGIN;
4006    
4007     sub wait_for_tick {
4008 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
4009 root 1.241
4010 root 1.239 my $signal = new Coro::Signal;
4011     push @WAIT_FOR_TICK, $signal;
4012     $signal->wait;
4013     }
4014    
4015     sub wait_for_tick_begin {
4016 root 1.497 return Coro::cede if tick_inhibit || $Coro::current == $Coro::main;
4017 root 1.241
4018 root 1.239 my $signal = new Coro::Signal;
4019     push @WAIT_FOR_TICK_BEGIN, $signal;
4020     $signal->wait;
4021     }
4022    
4023 root 1.412 sub tick {
4024 root 1.396 if ($Coro::current != $Coro::main) {
4025     Carp::cluck "major BUG: server tick called outside of main coro, skipping it"
4026     unless ++$bug_warning > 10;
4027     return;
4028     }
4029    
4030     cf::server_tick; # one server iteration
4031 root 1.245
4032 root 1.512 #for(1..3e6){} AE::now_update; $NOW=AE::now; # generate load #d#
4033 root 1.502
4034 root 1.396 if ($NOW >= $NEXT_RUNTIME_WRITE) {
4035 root 1.402 $NEXT_RUNTIME_WRITE = List::Util::max $NEXT_RUNTIME_WRITE + 10, $NOW + 5.;
4036 root 1.396 Coro::async_pool {
4037     $Coro::current->{desc} = "runtime saver";
4038 root 1.417 write_runtime_sync
4039 root 1.532 or error "ERROR: unable to write runtime file: $!";
4040 root 1.396 };
4041     }
4042 root 1.265
4043 root 1.396 if (my $sig = shift @WAIT_FOR_TICK_BEGIN) {
4044     $sig->send;
4045     }
4046     while (my $sig = shift @WAIT_FOR_TICK) {
4047     $sig->send;
4048     }
4049 root 1.265
4050 root 1.412 $LOAD = ($NOW - $TICK_START) / $TICK;
4051 root 1.396 $LOADAVG = $LOADAVG * 0.75 + $LOAD * 0.25;
4052 root 1.265
4053 root 1.412 if (0) {
4054     if ($NEXT_TICK) {
4055     my $jitter = $TICK_START - $NEXT_TICK;
4056     $JITTER = $JITTER * 0.75 + $jitter * 0.25;
4057 root 1.532 debug "jitter $JITTER\n";#d#
4058 root 1.412 }
4059     }
4060     }
4061 root 1.35
4062 root 1.206 {
4063 root 1.401 # configure BDB
4064    
4065 root 1.503 BDB::min_parallel 16;
4066 root 1.400 BDB::max_poll_reqs $TICK * 0.1;
4067 root 1.433 $AnyEvent::BDB::WATCHER->priority (1);
4068 root 1.77
4069 root 1.206 unless ($DB_ENV) {
4070     $DB_ENV = BDB::db_env_create;
4071 root 1.437 $DB_ENV->set_flags (BDB::AUTO_COMMIT | BDB::REGION_INIT);
4072     $DB_ENV->set_flags (&BDB::LOG_AUTOREMOVE ) if BDB::VERSION v0, v4.7;
4073     $DB_ENV->log_set_config (&BDB::LOG_AUTO_REMOVE) if BDB::VERSION v4.7;
4074 root 1.371 $DB_ENV->set_timeout (30, BDB::SET_TXN_TIMEOUT);
4075     $DB_ENV->set_timeout (30, BDB::SET_LOCK_TIMEOUT);
4076 root 1.206
4077 root 1.534 cf::sync_job {
4078     eval {
4079     BDB::db_env_open
4080     $DB_ENV,
4081     $BDBDIR,
4082     BDB::INIT_LOCK | BDB::INIT_LOG | BDB::INIT_MPOOL | BDB::INIT_TXN
4083     | BDB::RECOVER | BDB::REGISTER | BDB::USE_ENVIRON | BDB::CREATE,
4084     0666;
4085 root 1.208
4086 root 1.534 cf::cleanup "db_env_open($BDBDIR): $!" if $!;
4087     };
4088 root 1.533
4089 root 1.534 cf::cleanup "db_env_open(db): $@" if $@;
4090     };
4091 root 1.206 }
4092 root 1.363
4093 root 1.396 $BDB_DEADLOCK_WATCHER = EV::periodic 0, 3, 0, sub {
4094     BDB::db_env_lock_detect $DB_ENV, 0, BDB::LOCK_DEFAULT, 0, sub { };
4095     };
4096     $BDB_CHECKPOINT_WATCHER = EV::periodic 0, 60, 0, sub {
4097     BDB::db_env_txn_checkpoint $DB_ENV, 0, 0, 0, sub { };
4098     };
4099     $BDB_TRICKLE_WATCHER = EV::periodic 0, 10, 0, sub {
4100     BDB::db_env_memp_trickle $DB_ENV, 20, 0, sub { };
4101     };
4102 root 1.206 }
4103    
4104     {
4105 root 1.401 # configure IO::AIO
4106    
4107 root 1.206 IO::AIO::min_parallel 8;
4108     IO::AIO::max_poll_time $TICK * 0.1;
4109 root 1.435 undef $AnyEvent::AIO::WATCHER;
4110 root 1.206 }
4111 root 1.108
4112 root 1.262 my $_log_backtrace;
4113    
4114 root 1.260 sub _log_backtrace {
4115     my ($msg, @addr) = @_;
4116    
4117 root 1.262 $msg =~ s/\n//;
4118 root 1.260
4119 root 1.262 # limit the # of concurrent backtraces
4120     if ($_log_backtrace < 2) {
4121     ++$_log_backtrace;
4122 root 1.446 my $perl_bt = Carp::longmess $msg;
4123 root 1.262 async {
4124 root 1.374 $Coro::current->{desc} = "abt $msg";
4125    
4126 root 1.262 my @bt = fork_call {
4127     @addr = map { sprintf "%x", $_ } @addr;
4128     my $self = (-f "/proc/$$/exe") ? "/proc/$$/exe" : $^X;
4129     open my $fh, "exec addr2line -C -f -i -e \Q$self\E @addr 2>&1 |"
4130     or die "addr2line: $!";
4131    
4132     my @funcs;
4133     my @res = <$fh>;
4134     chomp for @res;
4135     while (@res) {
4136     my ($func, $line) = splice @res, 0, 2, ();
4137     push @funcs, "[$func] $line";
4138     }
4139 root 1.260
4140 root 1.262 @funcs
4141     };
4142 root 1.260
4143 root 1.446 LOG llevInfo, "[ABT] $perl_bt\n";
4144     LOG llevInfo, "[ABT] --- C backtrace follows ---\n";
4145 root 1.262 LOG llevInfo, "[ABT] $_\n" for @bt;
4146     --$_log_backtrace;
4147     };
4148     } else {
4149 root 1.260 LOG llevInfo, "[ABT] $msg\n";
4150 root 1.262 LOG llevInfo, "[ABT] [suppressed]\n";
4151     }
4152 root 1.260 }
4153    
4154 root 1.249 # load additional modules
4155 root 1.467 require "cf/$_.pm" for @EXTRA_MODULES;
4156 root 1.483 cf::_connect_to_perl_2;
4157 root 1.249
4158 root 1.125 END { cf::emergency_save }
4159    
4160 root 1.1 1
4161