ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.547
Committed: Sun May 9 22:51:13 2010 UTC (14 years ago) by root
Branch: MAIN
Changes since 1.546: +8 -1 lines
Log Message:
fix some issues related to fork, highscore calculation and quit_character

File Contents

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