ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.453
Committed: Tue Sep 23 04:29:11 2008 UTC (15 years, 8 months ago) by root
Branch: MAIN
CVS Tags: rel-2_71
Changes since 1.452: +54 -16 lines
Log Message:
*** empty log message ***

File Contents

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