ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/lib/cf.pm
Revision: 1.451
Committed: Mon Sep 22 05:42:41 2008 UTC (15 years, 8 months ago) by root
Branch: MAIN
Changes since 1.450: +4 -5 lines
Log Message:
tweaks

File Contents

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