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