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